Description:
ERC20 token contract. Standard implementation for fungible tokens on Ethereum.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/*
* USD Treasury (Ethereum)
* - Compatible con The Digital Dollar (USD) en la red Ethereum
* - Dirección del token USD: 0xD3096aC155a63F2D64ec0fd9EE82beC6b844C388
*/
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address a) external view returns (uint256);
function allowance(address o, address s) external view returns (uint256);
function approve(address s, uint256 v) external returns (bool);
function transfer(address to, uint256 v) external returns (bool);
function transferFrom(address f, address t, uint256 v) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
library Address {
function isContract(address a) internal view returns (bool) {
return a.code.length > 0;
}
}
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_call(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_call(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
_call(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function _call(IERC20 token, bytes memory data) private {
(bool ok, bytes memory ret) = address(token).call(data);
require(ok, "SafeERC20: call failed");
if (ret.length > 0) require(abi.decode(ret, (bool)), "SafeERC20: op failed");
}
}
interface ITreasuryReceiver {
function onTreasuryReceived(address from, address token, uint256 amount, bytes calldata data) external returns (bytes4);
}
abstract contract NonReentrant {
uint256 private _entered;
modifier nonReentrant() {
require(_entered == 0, "REENTRANCY");
_entered = 1;
_;
_entered = 0;
}
}
contract USDTreasuryEthereum is NonReentrant {
using SafeERC20 for IERC20;
uint256 private constant BPS_DENOM = 10_000;
bytes4 private constant MAGIC_ACK = 0x150b7a02;
IERC20 public immutable usd; // The Digital Dollar (USD)
address public admin;
bool public treasuryActive;
uint16 public goldBps = 5000;
uint16 public opsBps = 1000;
uint16 public rewardsBps = 4000;
uint256 public accountedBalance;
uint256 public goldReserve;
uint256 public opsReserve;
uint256 public rewardsReserve;
address public feeCollector;
mapping(address => address) public referrerOf;
mapping(address => bool) public referralLocked;
mapping(address => bool) public excludedFromRewards;
address public pendingMigration;
uint256 public migrationETA;
uint256 public migrationDelay = 72 hours;
mapping(address => bool) public rescueWhitelist;
mapping(address => uint256) public rescueCapPerToken;
mapping(address => uint256) public rescuedSoFar;
event AdminChanged(address indexed oldAdmin, address indexed newAdmin);
event TreasuryActivated(address indexed by, uint256 at);
event TreasuryDeactivated(address indexed by, uint256 at);
event SplitsChanged(uint16 goldBps, uint16 opsBps, uint16 rewardsBps);
event Deposited(address indexed from, uint256 amount, bool asFees);
event BucketsSettled(uint256 delta, uint256 addGold, uint256 addOps, uint256 addRewards);
event RewardsSent(address indexed to, uint256 amount);
event OpsSent(address indexed to, uint256 amount);
event GoldCommitted(address indexed to, uint256 amount, bytes32 refId);
event ExcludedSet(address indexed account, bool excluded);
event FeeCollectorSet(address indexed collector);
event ReferralRegistered(address indexed user, address indexed referrer);
event MigrationQueued(address indexed newTreasury, uint256 eta);
event MigrationCancelled();
event MigrationExecuted(address indexed newTreasury, uint256 amount, bool acked);
event RescueWhitelistSet(address indexed token, bool allowed, uint256 cap);
event TokenRescued(address indexed token, address indexed to, uint256 amount);
error NotAdmin();
error NotActive();
error ZeroAddress();
error BadSplits();
error NotAllowed();
error TooSoon();
error CapExceeded();
error USDNotRescuable();
modifier onlyAdmin() {
if (msg.sender != admin) revert NotAdmin();
_;
}
modifier whenActive() {
if (!treasuryActive) revert NotActive();
_;
}
constructor(address usdToken, address initialAdmin) {
if (usdToken == address(0) || initialAdmin == address(0)) revert ZeroAddress();
usd = IERC20(usdToken); // Token USD en Ethereum
admin = initialAdmin;
excludedFromRewards[address(0)] = true;
excludedFromRewards[address(this)] = true;
}
function setAdmin(address newAdmin) external onlyAdmin {
if (newAdmin == address(0)) revert ZeroAddress();
emit AdminChanged(admin, newAdmin);
admin = newAdmin;
}
function activateTreasury() external onlyAdmin {
treasuryActive = true;
emit TreasuryActivated(msg.sender, block.timestamp);
}
function deactivateTreasury() external onlyAdmin {
treasuryActive = false;
emit TreasuryDeactivated(msg.sender, block.timestamp);
}
function setSplits(uint16 _gold, uint16 _ops, uint16 _rewards) external onlyAdmin {
if (_gold + _ops + _rewards != BPS_DENOM) revert BadSplits();
goldBps = _gold; opsBps = _ops; rewardsBps = _rewards;
emit SplitsChanged(_gold, _ops, _rewards);
}
function setFeeCollector(address collector) external onlyAdmin {
if (collector == address(0)) revert ZeroAddress();
feeCollector = collector;
emit FeeCollectorSet(collector);
}
function setExcludedFromRewards(address account, bool excluded) external onlyAdmin {
excludedFromRewards[account] = excluded;
emit ExcludedSet(account, excluded);
}
function deposit(address from, uint256 amount, bool asFees) external nonReentrant {
if (!treasuryActive) revert NotActive();
if (msg.sender != admin && msg.sender != feeCollector) revert NotAllowed();
require(from != address(0) && amount > 0, "bad params");
usd.safeTransferFrom(from, address(this), amount);
emit Deposited(from, amount, asFees);
_settleBuckets();
}
function settleBuckets() external whenActive nonReentrant {
_settleBuckets();
}
function _settleBuckets() internal {
uint256 bal = usd.balanceOf(address(this));
if (bal <= accountedBalance) return;
uint256 delta = bal - accountedBalance;
uint256 addGold = (delta * goldBps) / BPS_DENOM;
uint256 addOps = (delta * opsBps) / BPS_DENOM;
uint256 addRewards = delta - addGold - addOps;
goldReserve += addGold;
opsReserve += addOps;
rewardsReserve += addRewards;
accountedBalance = bal;
emit BucketsSettled(delta, addGold, addOps, addRewards);
}
function sendRewards(address to, uint256 amount) external onlyAdmin whenActive nonReentrant {
require(to != address(0) && !excludedFromRewards[to], "invalid to");
require(amount > 0 && amount <= rewardsReserve, "amount");
rewardsReserve -= amount;
accountedBalance -= amount;
usd.safeTransfer(to, amount);
emit RewardsSent(to, amount);
}
function sendOps(address to, uint256 amount) external onlyAdmin whenActive nonReentrant {
require(to != address(0), "invalid to");
require(amount > 0 && amount <= opsReserve, "amount");
opsReserve -= amount;
accountedBalance -= amount;
usd.safeTransfer(to, amount);
emit OpsSent(to, amount);
}
function commitGold(address toVault, uint256 amount, bytes32 refId)
external
onlyAdmin
whenActive
nonReentrant
{
require(toVault != address(0), "vault=0");
require(amount > 0 && amount <= goldReserve, "amount");
goldReserve -= amount;
accountedBalance -= amount;
usd.safeTransfer(toVault, amount);
emit GoldCommitted(toVault, amount, refId);
}
function registerReferral(address user, address referrer) external onlyAdmin {
require(user != address(0) && referrer != address(0), "0 addr");
require(!referralLocked[user], "locked");
referrerOf[user] = referrer;
referralLocked[user] = true;
emit ReferralRegistered(user, referrer);
}
function setMigrationDelay(uint256 newDelay) external onlyAdmin {
require(newDelay >= 24 hours && newDelay <= 14 days, "delay bounds");
migrationDelay = newDelay;
}
function queueMigration(address newTreasury) external onlyAdmin {
require(newTreasury != address(0) && Address.isContract(newTreasury), "bad target");
pendingMigration = newTreasury;
migrationETA = block.timestamp + migrationDelay;
emit MigrationQueued(newTreasury, migrationETA);
}
function cancelMigration() external onlyAdmin {
pendingMigration = address(0);
migrationETA = 0;
emit MigrationCancelled();
}
function executeMigration(bytes calldata data) external onlyAdmin nonReentrant {
require(pendingMigration != address(0) && block.timestamp >= migrationETA, "not ready");
_settleBuckets();
uint256 amt = usd.balanceOf(address(this));
accountedBalance = 0;
goldReserve = 0;
opsReserve = 0;
rewardsReserve = 0;
address target = pendingMigration;
pendingMigration = address(0);
migrationETA = 0;
usd.safeTransfer(target, amt);
bool ack = false;
if (Address.isContract(target)) {
try ITreasuryReceiver(target).onTreasuryReceived(address(this), address(usd), amt, data)
returns (bytes4 r) {
if (r == MAGIC_ACK) ack = true;
} catch { ack = false; }
}
emit MigrationExecuted(target, amt, ack);
}
function setRescueRule(address token, bool allowed, uint256 cap) external onlyAdmin {
if (token == address(usd)) revert USDNotRescuable();
rescueWhitelist[token] = allowed;
rescueCapPerToken[token] = cap;
emit RescueWhitelistSet(token, allowed, cap);
}
function rescueERC20(address token, address to, uint256 amount) external onlyAdmin nonReentrant {
if (token == address(usd)) revert USDNotRescuable();
require(rescueWhitelist[token], "not whitelisted");
require(to != address(0) && amount > 0, "bad params");
uint256 cap = rescueCapPerToken[token];
if (cap > 0) {
uint256 newTotal = rescuedSoFar[token] + amount;
if (newTotal > cap) revert CapExceeded();
rescuedSoFar[token] = newTotal;
}
IERC20(token).safeTransfer(to, amount);
emit TokenRescued(token, to, amount);
}
function usdBalance() external view returns (uint256) {
return usd.balanceOf(address(this));
}
function unaccountedUSD() external view returns (uint256) {
uint256 bal = usd.balanceOf(address(this));
return (bal > accountedBalance) ? bal - accountedBalance : 0;
}
}
Submitted on: 2025-10-10 12:45:37
Comments
Log in to comment.
No comments yet.