编译器
0.8.22+commit.4fc1097e
文件 1 的 7:Executors.sol
pragma solidity ^0.8.17;
import {Owners} from "./Owners.sol";
abstract contract Executors is Owners {
event ExecutorSet(address indexed executor, bool active);
mapping(address => bool) public executors;
modifier isExecutor() {
require(executors[msg.sender], "Unauthorized");
_;
}
function _setExecutor(address executor, bool active) internal virtual {
executors[executor] = active;
emit ExecutorSet(executor, active);
}
function setExecutor(address owner, bool active) external virtual isOwner {
_setExecutor(owner, active);
}
}
文件 2 的 7:IERC20.sol
pragma solidity ^0.8.17;
interface IERC20 {
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(
address recipient,
uint256 amount
) external returns (bool);
function allowance(
address owner,
address spender
) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
}
文件 3 的 7:IOracleV1.sol
pragma solidity ^0.8.17;
interface IOracleV1 {
function getRouterAddress() external view returns (address);
function getPoolsAPY(string memory chain) external view returns (uint64);
function getSaversAPY(string memory chain) external view returns (uint64);
function getInboundAddress(
string memory chain
) external view returns (bytes memory, address);
}
文件 4 的 7:IThorchainRouterV4.sol
pragma solidity ^0.8.17;
interface IThorchainRouterV4 {
function depositWithExpiry(
address payable vault,
address asset,
uint amount,
string memory memo,
uint expiration
) external payable;
}
文件 5 的 7:Owners.sol
pragma solidity ^0.8.17;
abstract contract Owners {
event OwnerSet(address indexed owner, bool active);
mapping(address => bool) public owners;
modifier isOwner() {
require(owners[msg.sender], "Unauthorized");
_;
}
function _setOwner(address owner, bool active) internal virtual {
owners[owner] = active;
emit OwnerSet(owner, active);
}
function setOwner(address owner, bool active) external virtual isOwner {
_setOwner(owner, active);
}
}
文件 6 的 7:SafeTransferLib.sol
pragma solidity >=0.8.0;
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool callStatus;
assembly {
callStatus := call(gas(), to, amount, 0, 0, 0, 0)
}
require(callStatus, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 amount
) internal {
bool callStatus;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(
freeMemoryPointer,
0x23b872dd00000000000000000000000000000000000000000000000000000000
)
mstore(
add(freeMemoryPointer, 4),
and(from, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(
add(freeMemoryPointer, 36),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(add(freeMemoryPointer, 68), amount)
callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
}
require(
didLastOptionalReturnCallSucceed(callStatus),
"TRANSFER_FROM_FAILED"
);
}
function safeTransfer(address token, address to, uint256 amount) internal {
bool callStatus;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(
freeMemoryPointer,
0xa9059cbb00000000000000000000000000000000000000000000000000000000
)
mstore(
add(freeMemoryPointer, 4),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(add(freeMemoryPointer, 36), amount)
callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(
didLastOptionalReturnCallSucceed(callStatus),
"TRANSFER_FAILED"
);
}
function safeApprove(address token, address to, uint256 amount) internal {
bool callStatus;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(
freeMemoryPointer,
0x095ea7b300000000000000000000000000000000000000000000000000000000
)
mstore(
add(freeMemoryPointer, 4),
and(to, 0xffffffffffffffffffffffffffffffffffffffff)
)
mstore(add(freeMemoryPointer, 36), amount)
callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
}
require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
}
function didLastOptionalReturnCallSucceed(
bool callStatus
) private pure returns (bool success) {
assembly {
let returnDataSize := returndatasize()
if iszero(callStatus) {
returndatacopy(0, 0, returnDataSize)
revert(0, returnDataSize)
}
switch returnDataSize
case 32 {
returndatacopy(0, 0, returnDataSize)
success := iszero(iszero(mload(0)))
}
case 0 {
success := 1
}
default {
success := 0
}
}
}
}
文件 7 的 7:TSFeeDistributor_V2.sol
pragma solidity ^0.8.17;
import {Owners} from "../../lib/Owners.sol";
import {Executors} from "../../lib/Executors.sol";
import {SafeTransferLib} from "../../lib/SafeTransferLib.sol";
import {IERC20} from "../../interfaces/IERC20.sol";
import {IThorchainRouterV4} from "../../interfaces/IThorchainRouterV4.sol";
import {IOracleV1} from "../../interfaces/IOracleV1.sol";
contract TSFeeDistributor_V2 is Owners, Executors {
using SafeTransferLib for address;
IOracleV1 public oracle;
IThorchainRouterV4 public tcRouter;
IERC20 public feeAsset;
uint256 public minFeeAmount;
uint256 private _communityDistribution;
uint32 public treasuryBps;
uint32 public communityBps;
uint32 public treasuryIndex;
uint32 public communityIndex;
bool public publicMode;
bool public transferTreasury;
address public treasuryWallet;
mapping(uint32 => string) public memoTreasury;
mapping(uint32 => string) public memoCommunity;
event TreasuryDistribution(uint256 amount, string memo);
event CommunityDistribution(uint256 amount, string memo);
constructor(
address _oracleAddress,
address _tcRouterAddress,
address _feeAsset,
address _treasuryWallet
) {
treasuryBps = 2500;
communityBps = 7500;
_communityDistribution = 0;
oracle = IOracleV1(_oracleAddress);
tcRouter = IThorchainRouterV4(_tcRouterAddress);
feeAsset = IERC20(_feeAsset);
minFeeAmount = 0;
_feeAsset.safeApprove(_tcRouterAddress, 0);
_feeAsset.safeApprove(_tcRouterAddress, type(uint256).max);
publicMode = false;
transferTreasury = true;
treasuryWallet = _treasuryWallet;
_setOwner(msg.sender, true);
}
function setMinFeeAmount(uint256 amount) external isOwner {
minFeeAmount = amount;
}
function setTCRouter(address _tcRouterAddress) public isOwner {
tcRouter = IThorchainRouterV4(_tcRouterAddress);
feeAsset.approve(_tcRouterAddress, 0);
feeAsset.approve(_tcRouterAddress, type(uint256).max);
}
function setShares(uint32 treasury, uint32 community) external isOwner {
require(treasury + community == 10000, "Shares must add up to 10000");
treasuryBps = treasury;
communityBps = community;
}
function getMemoTreasury(uint32 id) external view returns (string memory) {
return memoTreasury[id];
}
function setMemoTreasury(uint32 id, string memory memo) external isOwner {
memoTreasury[id] = memo;
}
function setTreasuryIndex(uint32 index) external isOwner {
treasuryIndex = index;
}
function setTreasuryTransfer(bool _transferTreasury) external isOwner {
transferTreasury = _transferTreasury;
}
function getMemoCommunity(uint32 id) external view returns (string memory) {
return memoCommunity[id];
}
function setMemoCommunity(uint32 id, string memory memo) external isOwner {
memoCommunity[id] = memo;
}
function setCommunityIndex(uint32 index) external isOwner {
communityIndex = index;
}
function setTreasuryWallet(address _treasuryWallet) external isOwner {
treasuryWallet = _treasuryWallet;
}
function distributeTreasuryExecutor(
address inboundAddress
) external isExecutor {
require(!publicMode, "Must call distributeTreasury instead.");
_distributeTreasury(inboundAddress);
}
function distributeTreasury() external {
require(publicMode, "Must call distributeTreasuryExecutor instead.");
(, address inboundAddress) = oracle.getInboundAddress("ETH");
_distributeTreasury(inboundAddress);
}
function _distributeTreasury(address inboundAddress) internal {
require(
_communityDistribution == 0,
"It's the community's turn to receive distribution"
);
uint256 balance = feeAsset.balanceOf(address(this));
require(balance >= minFeeAmount, "Balance is below minimum fee amount");
uint256 treasuryAmount = (balance * treasuryBps) / 10000;
_communityDistribution = balance - treasuryAmount;
if (transferTreasury) {
feeAsset.transfer(treasuryWallet, treasuryAmount);
} else {
tcRouter.depositWithExpiry{value: 0}(
payable(inboundAddress),
address(feeAsset),
treasuryAmount,
memoTreasury[treasuryIndex],
type(uint256).max
);
}
emit TreasuryDistribution(treasuryAmount, memoTreasury[treasuryIndex]);
}
function distributeCommunityExecutor(
address inboundAddress
) external isExecutor {
require(!publicMode, "Must call distributeCommunity instead.");
_distributeCommunity(inboundAddress);
}
function distributeCommunity() external {
require(publicMode, "Must call distributeCommunityExecutor instead.");
(, address inboundAddress) = oracle.getInboundAddress("ETH");
_distributeCommunity(inboundAddress);
}
function _distributeCommunity(address inboundAddress) internal {
require(
_communityDistribution > 0,
"It's the treasury's turn to receive distribution"
);
require(
_communityDistribution <= feeAsset.balanceOf(address(this)),
"Community distribution exceeds balance"
);
tcRouter.depositWithExpiry{value: 0}(
payable(inboundAddress),
address(feeAsset),
_communityDistribution,
memoCommunity[communityIndex],
type(uint256).max
);
emit CommunityDistribution(
_communityDistribution,
memoCommunity[communityIndex]
);
_communityDistribution = 0;
}
}
{
"compilationTarget": {
"src/contracts/misc/TSFeeDistributor_V2.sol": "TSFeeDistributor_V2"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"_oracleAddress","type":"address"},{"internalType":"address","name":"_tcRouterAddress","type":"address"},{"internalType":"address","name":"_feeAsset","type":"address"},{"internalType":"address","name":"_treasuryWallet","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"memo","type":"string"}],"name":"CommunityDistribution","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"bool","name":"active","type":"bool"}],"name":"ExecutorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"bool","name":"active","type":"bool"}],"name":"OwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"memo","type":"string"}],"name":"TreasuryDistribution","type":"event"},{"inputs":[],"name":"communityBps","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"communityIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"distributeCommunity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"inboundAddress","type":"address"}],"name":"distributeCommunityExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributeTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"inboundAddress","type":"address"}],"name":"distributeTreasuryExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"executors","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"getMemoCommunity","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"getMemoTreasury","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"memoCommunity","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"memoTreasury","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minFeeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracleV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"owners","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"index","type":"uint32"}],"name":"setCommunityIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"bool","name":"active","type":"bool"}],"name":"setExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"string","name":"memo","type":"string"}],"name":"setMemoCommunity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"string","name":"memo","type":"string"}],"name":"setMemoTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setMinFeeAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"bool","name":"active","type":"bool"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"treasury","type":"uint32"},{"internalType":"uint32","name":"community","type":"uint32"}],"name":"setShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tcRouterAddress","type":"address"}],"name":"setTCRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"index","type":"uint32"}],"name":"setTreasuryIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_transferTreasury","type":"bool"}],"name":"setTreasuryTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasuryWallet","type":"address"}],"name":"setTreasuryWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tcRouter","outputs":[{"internalType":"contract IThorchainRouterV4","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferTreasury","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryBps","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]