编译器
0.8.17+commit.8df45f5f
文件 1 的 13:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 13:Erc20LockData.sol
pragma solidity ^0.8.17;
struct Erc20LockData {
address token;
address withdrawer;
uint256 creationTime;
uint256 timeInterval;
uint256 withdrawedCount;
uint256 count;
uint256 stepByStepUnlockCount;
}
文件 3 的 13:Erc20Sale.sol
pragma solidity ^0.8.17;
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import 'contracts/fee/IFeeSettings.sol';
import './IErc20Sale.sol';
import '../lib/ownable/Ownable.sol';
import 'contracts/asset_lockers/erc20/IErc20Locker.sol';
struct BuyFunctionData {
uint256 spend;
uint256 lastCount;
uint256 transferred;
uint256 sendCount;
}
contract Erc20Sale is IErc20Sale {
using SafeERC20 for IERC20;
IFeeSettings public immutable feeSettings;
IErc20Locker public immutable locker;
mapping(uint256 => PositionData) _positions;
mapping(uint256 => mapping(address => bool)) _whiteLists;
mapping(uint256 => uint256) _limits;
mapping(uint256 => mapping(address => uint256)) _usedLimits;
mapping(uint256 => OfferData) _offers;
mapping(uint256 => BuyLockSettings) _lockSettings;
uint256 public totalOffers;
uint256 _totalPositions;
constructor(address feeSettings_, address locker_) {
feeSettings = IFeeSettings(feeSettings_);
locker = IErc20Locker(locker_);
}
function createPosition(
address asset1,
address asset2,
uint256 priceNom,
uint256 priceDenom,
uint256 count,
uint256 buyLimit,
address[] calldata whiteList,
BuyLockSettings calldata lockSettings
) external {
uint8 flags = 0;
if (count > 0) {
uint256 lastCount = IERC20(asset1).balanceOf(address(this));
IERC20(asset1).safeTransferFrom(msg.sender, address(this), count);
count = IERC20(asset1).balanceOf(address(this)) - lastCount;
}
if (buyLimit > 0) flags |= BUYLIMIT_FLAG;
if (whiteList.length > 0) flags |= WHITELIST_FLAG;
if (lockSettings.lockTime > 0) flags |= LOCK_FLAG;
_positions[++_totalPositions] = PositionData(
msg.sender,
asset1,
asset2,
priceNom,
priceDenom,
count,
0,
flags
);
if (flags & LOCK_FLAG > 0) {
require(
lockSettings.receivePercent < LOCK_PRECISION,
'receive percent in lock must be less than 100% for lock'
);
require(
lockSettings.receivePercent +
lockSettings.unlockPercentByTime <=
LOCK_PRECISION,
'lock settings is not correct: receivePercent+unlockPercentByTime > LOCK_PRECISION'
);
_lockSettings[_totalPositions] = lockSettings;
}
if (buyLimit > 0) _limits[_totalPositions] = buyLimit;
for (uint256 i = 0; i < whiteList.length; ++i)
_whiteLists[_totalPositions][whiteList[i]] = true;
emit OnCreate(_totalPositions);
if (whiteList.length > 0)
emit OnWhiteListed(_totalPositions, true, whiteList);
}
function createOffer(
uint256 positionId,
uint256 asset1Count,
uint256 asset2Count
) external {
PositionData memory position = _positions[positionId];
require(position.owner != address(0), 'position is not exists');
++totalOffers;
_offers[totalOffers].positionId = positionId;
_offers[totalOffers].state = 1;
_offers[totalOffers].owner = msg.sender;
_offers[totalOffers].asset1Count = asset1Count;
_offers[totalOffers].asset2Count = asset2Count;
uint256 lastCount = IERC20(position.asset2).balanceOf(address(this));
IERC20(position.asset2).safeTransferFrom(
msg.sender,
address(this),
asset2Count
);
_offers[totalOffers].asset2Count =
IERC20(position.asset2).balanceOf(address(this)) -
lastCount;
emit OnOfer(positionId, totalOffers);
}
function removeOffer(uint256 offerId) external {
OfferData storage offer = _offers[offerId];
require(offer.state == 1, 'offer is not created or already used');
require(offer.owner == msg.sender, 'only owner can remove the offer');
offer.state = 0;
PositionData memory position = _positions[offer.positionId];
IERC20(position.asset2).safeTransferFrom(
address(this),
offer.owner,
offer.asset2Count
);
emit OnRemoveOfer(offer.positionId, offerId);
}
function applyOffer(uint256 offerId) external {
OfferData storage offer = _offers[offerId];
require(offer.state == 1, 'offer is not created or already used');
offer.state = 2;
PositionData storage pos = _positions[offer.positionId];
require(pos.owner != address(0), 'position is not exists');
require(pos.owner == msg.sender, 'only owner can apply offer');
uint256 buyCount_ = offer.asset1Count;
require(
buyCount_ <= pos.count1,
'not enough owner asset to apply offer'
);
require(buyCount_ > 0, 'nothing to buy');
if (pos.flags & LOCK_FLAG > 0) {
IERC20(pos.asset1).approve(address(locker), type(uint256).max);
BuyLockSettings memory lockSettings = _lockSettings[
offer.positionId
];
uint256 sendCount = (buyCount_ * lockSettings.receivePercent) /
LOCK_PRECISION;
if (sendCount > 0) {
uint256 fee = feeSettings.feeForCount(offer.owner, sendCount);
if (fee > 0)
IERC20(pos.asset1).safeTransfer(
feeSettings.feeAddress(),
fee
);
IERC20(pos.asset1).safeTransfer(offer.owner, sendCount - fee);
}
locker.lockStepByStepUnlocking(
pos.asset1,
buyCount_ - sendCount,
offer.owner,
lockSettings.lockTime,
((buyCount_ - sendCount) * lockSettings.unlockPercentByTime) /
LOCK_PRECISION
);
} else {
uint256 buyFee = feeSettings.feeForCount(offer.owner, buyCount_);
if (buyFee > 0) {
IERC20(pos.asset1).safeTransfer(
feeSettings.feeAddress(),
buyFee
);
}
IERC20(pos.asset1).safeTransfer(offer.owner, buyCount_ - buyFee);
}
pos.count1 -= buyCount_;
uint256 sellFee = feeSettings.feeForCount(pos.owner, offer.asset2Count);
if (sellFee > 0) {
IERC20(pos.asset2).safeTransfer(feeSettings.feeAddress(), sellFee);
}
pos.count2 += offer.asset2Count - sellFee;
emit OnApplyOfer(offer.positionId, offerId);
}
function getOffer(
uint256 offerId
) external view returns (OfferData memory) {
return _offers[offerId];
}
function addBalance(uint256 positionId, uint256 count) external {
PositionData storage pos = _positions[positionId];
uint256 lastCount = IERC20(pos.asset1).balanceOf(address(this));
IERC20(pos.asset1).safeTransferFrom(msg.sender, address(this), count);
pos.count1 += IERC20(pos.asset1).balanceOf(address(this)) - lastCount;
}
function withdraw(
uint256 positionId,
uint256 assetCode,
address to,
uint256 count
) external {
_withdraw(positionId, _positions[positionId], assetCode, to, count);
}
function withdrawAllTo(
uint256 positionId,
uint256 assetCode,
address to
) external {
PositionData storage pos = _positions[positionId];
if (assetCode == 1)
_withdraw(
positionId,
_positions[positionId],
assetCode,
to,
pos.count1
);
else if (assetCode == 2)
_withdraw(
positionId,
_positions[positionId],
assetCode,
to,
pos.count2
);
else revert('unknown asset code');
}
function withdrawAll(uint256 positionId, uint256 assetCode) external {
PositionData storage pos = _positions[positionId];
if (assetCode == 1)
_withdraw(
positionId,
_positions[positionId],
assetCode,
msg.sender,
pos.count1
);
else if (assetCode == 2)
_withdraw(
positionId,
_positions[positionId],
assetCode,
msg.sender,
pos.count2
);
else revert('unknown asset code');
}
function _withdraw(
uint256 positionId,
PositionData storage pos,
uint256 assetCode,
address to,
uint256 count
) private {
require(pos.owner == msg.sender, 'only for position owner');
if (assetCode == 1) {
require(pos.count1 >= count, 'not enough asset count');
uint256 lastCount = IERC20(pos.asset1).balanceOf(address(this));
IERC20(pos.asset1).safeTransfer(to, count);
uint256 transferred = lastCount -
IERC20(pos.asset1).balanceOf(address(this));
require(
pos.count1 >= transferred,
'not enough asset count after withdraw'
);
pos.count1 -= transferred;
} else if (assetCode == 2) {
require(pos.count2 >= count, 'not enough asset count');
uint256 lastCount = IERC20(pos.asset2).balanceOf(address(this));
IERC20(pos.asset2).safeTransfer(to, count);
uint256 transferred = lastCount -
IERC20(pos.asset2).balanceOf(address(this));
require(
pos.count2 >= transferred,
'not enough asset count after withdraw'
);
pos.count2 -= transferred;
} else revert('unknown asset code');
emit OnWithdraw(positionId, assetCode, to, count);
}
function setPrice(
uint256 positionId,
uint256 priceNom,
uint256 priceDenom
) external {
PositionData storage pos = _positions[positionId];
require(pos.owner == msg.sender, 'only for position owner');
pos.priceNom = priceNom;
pos.priceDenom = priceDenom;
emit OnPrice(positionId);
}
function setWhiteList(
uint256 positionId,
bool whiteListed,
address[] calldata accounts
) external {
PositionData storage pos = _positions[positionId];
require(pos.owner == msg.sender, 'only for position owner');
for (uint256 i = 0; i < accounts.length; ++i) {
_whiteLists[positionId][accounts[i]] = whiteListed;
}
emit OnWhiteListed(positionId, whiteListed, accounts);
}
function isWhiteListed(
uint256 positionId,
address account
) external view returns (bool) {
return _whiteLists[positionId][account];
}
function enableWhiteList(uint256 positionId, bool enabled) external {
PositionData storage pos = _positions[positionId];
require(pos.owner == msg.sender, 'only for position owner');
if (enabled) pos.flags |= WHITELIST_FLAG;
else pos.flags &= ~WHITELIST_FLAG;
emit OnWhiteListEnabled(positionId, enabled);
}
function enableBuyLimit(uint256 positionId, bool enabled) external {
PositionData storage pos = _positions[positionId];
require(pos.owner == msg.sender, 'only for position owner');
if (enabled) pos.flags |= BUYLIMIT_FLAG;
else pos.flags &= ~BUYLIMIT_FLAG;
emit OnBuyLimitEnable(positionId, enabled);
}
function setBuyLimit(uint256 positionId, uint256 limit) external {
PositionData storage pos = _positions[positionId];
require(pos.owner == msg.sender, 'only for position owner');
_limits[positionId] = limit;
emit OnBuyLimit(positionId, limit);
}
function getBuyLimit(uint256 positionId) external view returns (uint256) {
return _limits[positionId];
}
function buy(
uint256 positionId,
address to,
uint256 count,
uint256 priceNom,
uint256 priceDenom,
address antibot
) external {
PositionData storage pos = _positions[positionId];
BuyFunctionData memory data;
require(msg.sender == antibot, 'antibot');
if (pos.flags & WHITELIST_FLAG > 0) {
require(
_whiteLists[positionId][msg.sender],
'the account is not in whitelist'
);
}
if (pos.flags & BUYLIMIT_FLAG > 0) {
uint256 usedLimit = _usedLimits[positionId][msg.sender] + count;
_usedLimits[positionId][msg.sender] = usedLimit;
require(
usedLimit <= _limits[positionId],
'account buy limit is over'
);
}
require(
pos.priceNom == priceNom && pos.priceDenom == priceDenom,
'the price is changed'
);
data.spend = _spendToBuy(pos, count);
require(
data.spend > 0,
'spend asset count is zero (count parameter is less than minimum count to spend)'
);
require(pos.count1 >= count, 'not enough asset count at position');
data.lastCount = IERC20(pos.asset1).balanceOf(address(this));
if (pos.flags & LOCK_FLAG > 0) {
IERC20(pos.asset1).approve(address(locker), type(uint256).max);
BuyLockSettings memory lockSettings = _lockSettings[positionId];
data.sendCount =
(count * lockSettings.receivePercent) /
LOCK_PRECISION;
if (data.sendCount > 0) {
uint256 fee = feeSettings.feeForCount(to, data.sendCount);
if (fee > 0)
IERC20(pos.asset1).safeTransfer(
feeSettings.feeAddress(),
fee
);
IERC20(pos.asset1).safeTransfer(to, data.sendCount - fee);
}
locker.lockStepByStepUnlocking(
pos.asset1,
count - data.sendCount,
to,
lockSettings.lockTime,
((count - data.sendCount) * lockSettings.unlockPercentByTime) /
LOCK_PRECISION
);
} else {
uint256 fee = feeSettings.feeForCount(to, count);
if (fee > 0)
IERC20(pos.asset1).safeTransfer(feeSettings.feeAddress(), fee);
IERC20(pos.asset1).safeTransfer(to, count - fee);
}
data.transferred =
data.lastCount -
IERC20(pos.asset1).balanceOf(address(this));
require(
pos.count1 >= data.transferred,
'not enough asset count after withdraw'
);
pos.count1 -= data.transferred;
data.lastCount = IERC20(pos.asset2).balanceOf(address(this));
uint256 sellFee = feeSettings.feeForCount(pos.owner, data.spend);
if (sellFee > 0) {
IERC20(pos.asset2).safeTransferFrom(
msg.sender,
feeSettings.feeAddress(),
sellFee
);
}
IERC20(pos.asset2).safeTransferFrom(
msg.sender,
address(this),
data.spend - sellFee
);
pos.count2 +=
IERC20(pos.asset2).balanceOf(address(this)) -
data.lastCount;
emit OnBuy(positionId, to, count);
}
function spendToBuy(
uint256 positionId,
uint256 count
) external view returns (uint256) {
return _spendToBuy(_positions[positionId], count);
}
function buyCount(
uint256 positionId,
uint256 spend
) external view returns (uint256) {
PositionData memory pos = _positions[positionId];
return (spend * pos.priceDenom) / pos.priceNom;
}
function _spendToBuy(
PositionData memory pos,
uint256 count
) private pure returns (uint256) {
return (count * pos.priceNom) / pos.priceDenom;
}
function getPosition(
uint256 positionId
) external view returns (PositionData memory) {
return _positions[positionId];
}
function getPositionLockSettings(
uint256 positionId
) external view returns (BuyLockSettings memory) {
return _lockSettings[positionId];
}
}
文件 4 的 13:IAssetLocker.sol
pragma solidity ^0.8.17;
interface IAssetLocker {
event OnLockPosition(uint256 id);
event OnWithdraw(uint256 id);
function positionsCount() external view returns (uint256);
function feeSettings() external view returns (address);
function withdrawer(uint256 id) external view returns (address);
function unlockTime(uint256 id) external view returns (uint256);
function isLocked(uint256 id) external view returns (bool);
function withdrawed(uint256 id) external view returns (bool);
function withdraw(uint256 id) external;
}
文件 5 的 13:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, 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 from, address to, uint256 amount) external returns (bool);
}
文件 6 的 13:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
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);
}
文件 7 的 13:IErc20Locker.sol
pragma solidity ^0.8.17;
import '../IAssetLocker.sol';
import './Erc20LockData.sol';
interface IErc20Locker is IAssetLocker {
function position(uint256 id) external view returns (Erc20LockData memory);
function lockTimeFor(
address token,
uint256 count,
uint256 unlockTime,
address withdrawer
) external;
function lockTime(
address token,
uint256 count,
uint256 unlockTime
) external;
function lockSecondsFor(
address token,
uint256 count,
uint256 seconds_,
address withdrawer
) external;
function lockSeconds(
address token,
uint256 count,
uint256 seconds_
) external;
function lockStepByStepUnlocking(
address tokenAddress,
uint256 count,
address withdrawer,
uint256 interval,
uint256 stepByStepUnlockCount
) external;
function remainingTokensToWithdraw(uint256 id) external view returns (uint256);
function unlockedCount(uint256 id) external view returns (uint256);
function unlockedCountWithdrawAvailable(uint256 id) external view returns (uint256);
function unlockAllTime(uint256 id) external view returns (uint256);
}
文件 8 的 13:IErc20Sale.sol
pragma solidity ^0.8.17;
import './IErc20SaleCounterOffer.sol';
uint8 constant WHITELIST_FLAG = 1 << 0;
uint8 constant BUYLIMIT_FLAG = 1 << 1;
uint8 constant LOCK_FLAG = 1 << 2;
struct PositionData {
address owner;
address asset1;
address asset2;
uint256 priceNom;
uint256 priceDenom;
uint256 count1;
uint256 count2;
uint8 flags;
}
struct BuyLockSettings {
uint256 receivePercent;
uint256 lockTime;
uint256 unlockPercentByTime;
}
uint256 constant LOCK_PRECISION = 10000;
interface IErc20Sale is IErc20SaleCounterOffer {
event OnCreate(uint256 indexed positionId);
event OnBuy(
uint256 indexed positionId,
address indexed account,
uint256 count
);
event OnPrice(uint256 indexed positionId);
event OnWithdraw(
uint256 indexed positionId,
uint256 assetCode,
address to,
uint256 count
);
event OnWhiteListed(
uint256 indexed positionId,
bool isWhiteListed,
address[] accounts
);
event OnWhiteListEnabled(uint256 indexed positionId, bool enabled);
event OnBuyLimitEnable(uint256 indexed positionId, bool enable);
event OnBuyLimit(uint256 indexed positionId, uint256 limit);
function createPosition(
address asset1,
address asset2,
uint256 priceNom,
uint256 priceDenom,
uint256 count,
uint256 buyLimit,
address[] calldata whiteList,
BuyLockSettings calldata lockSettings
) external;
function addBalance(uint256 positionId, uint256 count) external;
function withdraw(
uint256 positionId,
uint256 assetCode,
address to,
uint256 count
) external;
function withdrawAllTo(
uint256 positionId,
uint256 assetCode,
address to
) external;
function withdrawAll(uint256 positionId, uint256 assetCode) external;
function setPrice(
uint256 positionId,
uint256 priceNom,
uint256 priceDenom
) external;
function setWhiteList(
uint256 positionId,
bool whiteListed,
address[] calldata accounts
) external;
function isWhiteListed(
uint256 positionId,
address account
) external view returns (bool);
function enableWhiteList(uint256 positionId, bool enabled) external;
function enableBuyLimit(uint256 positionId, bool enabled) external;
function setBuyLimit(uint256 positionId, uint256 limit) external;
function getBuyLimit(uint256 positionId) external view returns (uint256);
function buy(
uint256 positionId,
address to,
uint256 count,
uint256 priceNom,
uint256 priceDenom,
address antibot
) external;
function spendToBuy(
uint256 positionId,
uint256 count
) external view returns (uint256);
function buyCount(
uint256 positionId,
uint256 spend
) external view returns (uint256);
function getPosition(
uint256 positionId
) external view returns (PositionData memory);
function getPositionLockSettings(
uint256 positionId
) external view returns (BuyLockSettings memory);
}
文件 9 的 13:IErc20SaleCounterOffer.sol
pragma solidity ^0.8.17;
struct OfferData {
uint256 positionId;
uint256 asset1Count;
uint256 asset2Count;
uint8 state;
address owner;
}
interface IErc20SaleCounterOffer {
event OnOfer(uint256 indexed positionId, uint256 indexed offerId);
event OnApplyOfer(uint256 indexed positionId, uint256 indexed offerId);
event OnRemoveOfer(uint256 indexed positionId, uint256 indexed offerId);
function createOffer(
uint256 positionId,
uint256 asset1Count,
uint256 asset2Count
) external;
function removeOffer(uint256 offerId) external;
function getOffer(uint256 offerId) external returns (OfferData memory);
function applyOffer(uint256 offerId) external;
}
文件 10 的 13:IFeeSettings.sol
pragma solidity ^0.8.17;
interface IFeeSettings {
function feeAddress() external view returns (address);
function feePercent() external view returns (uint256);
function feePercentFor(address account) external view returns (uint256);
function feeForCount(
address account,
uint256 count
) external view returns (uint256);
function feeDecimals() external view returns (uint256);
function feeEth() external view returns (uint256);
function feeEthFor(address account) external view returns (uint256);
function zeroFeeShare() external view returns (uint256);
}
文件 11 的 13:IOwnable.sol
pragma solidity ^0.8.17;
interface IOwnable {
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
}
文件 12 的 13:Ownable.sol
pragma solidity ^0.8.17;
import './IOwnable.sol';
contract Ownable is IOwnable {
address _owner;
constructor() {
_owner = msg.sender;
}
modifier onlyOwner() {
require(_owner == msg.sender, 'caller is not the owner');
_;
}
function owner() external virtual view returns (address) {
return _owner;
}
function transferOwnership(address newOwner) external override onlyOwner {
_owner = newOwner;
}
}
文件 13 的 13:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
{
"compilationTarget": {
"contracts/fixed_price_sales/Erc20Sale.sol": "Erc20Sale"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"feeSettings_","type":"address"},{"internalType":"address","name":"locker_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"OnApplyOfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"OnBuy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"OnBuyLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"enable","type":"bool"}],"name":"OnBuyLimitEnable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"OnCreate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"OnOfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"OnPrice","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"OnRemoveOfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"OnWhiteListEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isWhiteListed","type":"bool"},{"indexed":false,"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"OnWhiteListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetCode","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"OnWithdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"addBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"applyOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"count","type":"uint256"},{"internalType":"uint256","name":"priceNom","type":"uint256"},{"internalType":"uint256","name":"priceDenom","type":"uint256"},{"internalType":"address","name":"antibot","type":"address"}],"name":"buy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"spend","type":"uint256"}],"name":"buyCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"asset1Count","type":"uint256"},{"internalType":"uint256","name":"asset2Count","type":"uint256"}],"name":"createOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset1","type":"address"},{"internalType":"address","name":"asset2","type":"address"},{"internalType":"uint256","name":"priceNom","type":"uint256"},{"internalType":"uint256","name":"priceDenom","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"},{"internalType":"uint256","name":"buyLimit","type":"uint256"},{"internalType":"address[]","name":"whiteList","type":"address[]"},{"components":[{"internalType":"uint256","name":"receivePercent","type":"uint256"},{"internalType":"uint256","name":"lockTime","type":"uint256"},{"internalType":"uint256","name":"unlockPercentByTime","type":"uint256"}],"internalType":"struct BuyLockSettings","name":"lockSettings","type":"tuple"}],"name":"createPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"enableBuyLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"bool","name":"enabled","type":"bool"}],"name":"enableWhiteList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeSettings","outputs":[{"internalType":"contract IFeeSettings","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"getBuyLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"getOffer","outputs":[{"components":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"asset1Count","type":"uint256"},{"internalType":"uint256","name":"asset2Count","type":"uint256"},{"internalType":"uint8","name":"state","type":"uint8"},{"internalType":"address","name":"owner","type":"address"}],"internalType":"struct OfferData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"getPosition","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"asset1","type":"address"},{"internalType":"address","name":"asset2","type":"address"},{"internalType":"uint256","name":"priceNom","type":"uint256"},{"internalType":"uint256","name":"priceDenom","type":"uint256"},{"internalType":"uint256","name":"count1","type":"uint256"},{"internalType":"uint256","name":"count2","type":"uint256"},{"internalType":"uint8","name":"flags","type":"uint8"}],"internalType":"struct PositionData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"getPositionLockSettings","outputs":[{"components":[{"internalType":"uint256","name":"receivePercent","type":"uint256"},{"internalType":"uint256","name":"lockTime","type":"uint256"},{"internalType":"uint256","name":"unlockPercentByTime","type":"uint256"}],"internalType":"struct BuyLockSettings","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"isWhiteListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"locker","outputs":[{"internalType":"contract IErc20Locker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"removeOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"setBuyLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"priceNom","type":"uint256"},{"internalType":"uint256","name":"priceDenom","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"bool","name":"whiteListed","type":"bool"},{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"setWhiteList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"spendToBuy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalOffers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"assetCode","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"assetCode","type":"uint256"}],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"assetCode","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawAllTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]