编译器
0.8.23+commit.f704f362
文件 1 的 7:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 2 的 7:FullMath.sol
pragma solidity ^0.8.0;
library FullMath {
function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
require(denominator > prod1);
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = (0 - denominator) & denominator;
assembly {
denominator := div(denominator, twos)
}
assembly {
prod0 := div(prod0, twos)
}
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inv = (3 * denominator) ^ 2;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
result = prod0 * inv;
return result;
}
}
function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
文件 3 的 7:IERC20Minimal.sol
pragma solidity 0.8.23;
interface IERC20Minimal {
function transferFrom(address from, address to, uint256 value) external returns (bool);
function transfer(address, uint256) external returns (bool);
function balanceOf(address) external view returns (uint256);
function totalSupply() external view returns (uint256);
}
文件 4 的 7:IKotoV3.sol
pragma solidity 0.8.23;
import {PricingLibrary} from "../PricingLibrary.sol";
interface IKotoV3 {
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function bond() external payable returns (uint256 payout);
function bondLp(uint256 _lpAmount) external returns (uint256 payout);
function redeem(uint256 amount) external returns (uint256 payout);
function burn(uint256 amount) external returns (bool success);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function pool() external view returns (address);
function ownership() external pure returns (address);
function bondPrice() external view returns (uint256);
function bondPriceLp() external view returns (uint256);
function redemptionPrice() external view returns (uint256);
function marketInfo()
external
view
returns (PricingLibrary.Market memory, PricingLibrary.Term memory, PricingLibrary.Data memory);
function lpMarketInfo()
external
view
returns (PricingLibrary.Market memory, PricingLibrary.Term memory, PricingLibrary.Data memory);
function depository() external pure returns (address);
function create(uint256 ethBondAmount, uint256 lpBondAmount) external;
event AmmAdded(address poolAdded);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
event Bond(address indexed buyer, uint256 amount, uint256 bondPrice);
event CreateMarket(uint256 bonds, uint256 start, uint48 end);
event IncreaseLiquidity(uint256 kotoAdded, uint256 ethAdded);
event Launched(uint256 time);
event LimitsRemoved(uint256 time);
event OpenBondMarket(uint256 openingTime);
event Redeem(address indexed sender, uint256 burned, uint256 payout, uint256 floorPrice);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event UserExcluded(address indexed userToExclude);
error AlreadyLaunched();
error BondFailed();
error InsufficentAllowance();
error InsufficentBalance();
error InsufficentBondsAvailable();
error InvalidSender();
error InvalidTransfer();
error LimitsReached();
error MarketClosed();
error MaxPayout();
error OngoingBonds();
error OnlyOwner();
error RedeemFailed();
error Reentrancy();
}
文件 5 的 7:KotoV3.sol
pragma solidity 0.8.23;
import {PricingLibrary} from "./PricingLibrary.sol";
import {SafeTransferLib} from "lib/solmate/src/utils/SafeTransferLib.sol";
import {FullMath} from "./libraries/FullMath.sol";
import {IERC20Minimal} from "./interfaces/IERC20Minimal.sol";
import {IKotoV3} from "./interfaces/IKotoV3.sol";
contract KotoV3 is IKotoV3 {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
mapping(address => bool) private _excluded;
mapping(address => bool) private _amms;
uint256 private _totalSupply;
PricingLibrary.Adjustment private adjustment;
PricingLibrary.Data private data;
PricingLibrary.Market private market;
PricingLibrary.Term private term;
PricingLibrary.Adjustment private lpAdjustment;
PricingLibrary.Data private lpData;
PricingLibrary.Market private lpMarket;
PricingLibrary.Term private lpTerm;
uint256 ethCapacityNext;
uint256 lpCapacityNext;
uint8 private locked;
bool private launched;
string private constant NAME = "Koto";
string private constant SYMBOL = "KOTO";
uint8 private constant DECIMALS = 18;
uint8 private constant FEE = 50;
bool private immutable zeroForOne;
address private constant UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
address private constant UNISWAP_V2_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address private constant OWNER = 0x946eF43867225695E29241813A8F41519634B36b;
address private constant BOND_DEPOSITORY = 0xE58B33c813ac4077bd2519dE90FccB189a19FA71;
address private immutable pair;
address private immutable token0;
address private immutable token1;
uint256 private constant INTERVAL = 604800;
modifier lock() {
if (locked == 2) revert Reentrancy();
locked = 2;
_;
locked = 1;
}
constructor() {
pair = _createUniswapV2Pair(address(this), WETH);
_excluded[OWNER] = true;
_excluded[BOND_DEPOSITORY] = true;
_excluded[address(this)] = true;
_amms[pair] = true;
_mint(OWNER, IERC20Minimal(0xc75c635c1F5e21D23eC8592Cb37503B82A7EF942).totalSupply());
(token0, token1) = _getTokens(pair);
zeroForOne = address(this) == token0 ? true : false;
_allowances[address(this)][UNISWAP_V2_ROUTER] = type(uint256).max;
}
function transfer(address _to, uint256 _value) public returns (bool success) {
if (_to == address(0) || _value == 0) revert InvalidTransfer();
_transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
if (_to == address(0) || _value == 0) revert InvalidTransfer();
if (_from != msg.sender) {
if (_allowances[_from][msg.sender] < _value) revert InsufficentAllowance();
_allowances[_from][msg.sender] -= _value;
}
_transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool success) {
address owner = msg.sender;
_allowances[owner][_spender] = _value;
return true;
}
function bond() public payable lock returns (uint256 payout) {
if (block.timestamp > term.conclusion) revert MarketClosed();
if (market.capacity != 0) {
PricingLibrary.Market memory _market = market;
PricingLibrary.Term memory _term = term;
PricingLibrary.Data memory _data = data;
PricingLibrary.Adjustment memory adjustments = adjustment;
uint256 _supply = _totalSupply;
uint48 time = uint48(block.timestamp);
(_market, _data, _term, adjustments) = PricingLibrary.decay(data, _market, _term, adjustments);
uint256 price = PricingLibrary.marketPrice(_term.controlVariable, _market.totalDebt, _supply);
payout = (msg.value * 1e18 / price);
if (payout > market.maxPayout) revert MaxPayout();
_market.capacity -= uint96(payout);
_market.purchased += uint96(msg.value);
_market.sold += uint96(payout);
_market.totalDebt += uint96(payout);
bool success = _bond(msg.sender, payout);
if (!success) revert BondFailed();
emit Bond(msg.sender, payout, price);
(_market, _term, _data, adjustments) =
PricingLibrary.tune(time, _market, _term, _data, adjustments, _supply);
market = _market;
term = _term;
data = _data;
adjustment = adjustments;
} else {
SafeTransferLib.safeTransferETH(msg.sender, msg.value);
}
}
function bondLp(uint256 _lpAmount) public lock returns (uint256 payout) {
if (block.timestamp > lpTerm.conclusion) revert MarketClosed();
if (lpMarket.capacity != 0) {
IERC20Minimal(pair).transferFrom(msg.sender, address(BOND_DEPOSITORY), _lpAmount);
PricingLibrary.Market memory _market = lpMarket;
PricingLibrary.Term memory _term = lpTerm;
PricingLibrary.Data memory _data = lpData;
PricingLibrary.Adjustment memory adjustments = lpAdjustment;
uint256 _supply = _totalSupply;
uint48 time = uint48(block.timestamp);
(_market, _data, _term, adjustments) = PricingLibrary.decay(lpData, _market, _term, adjustments);
uint256 price = PricingLibrary.marketPrice(_term.controlVariable, _market.totalDebt, _supply);
payout = (_lpAmount * 1e18 / price);
if (payout > lpMarket.maxPayout) revert MaxPayout();
_market.capacity -= uint96(payout);
_market.purchased += uint96(_lpAmount);
_market.sold += uint96(payout);
_market.totalDebt += uint96(payout);
bool success = _bond(msg.sender, payout);
if (!success) revert BondFailed();
emit Bond(msg.sender, payout, price);
(_market, _term, _data, adjustments) =
PricingLibrary.tune(time, _market, _term, _data, adjustments, _supply);
lpMarket = _market;
lpTerm = _term;
lpData = _data;
lpAdjustment = adjustments;
}
}
function redeem(uint256 amount) external returns (uint256 payout) {
uint256 price = FullMath.mulDiv(address(this).balance, 1e18, _totalSupply);
payout = FullMath.mulDiv(price, amount, 1e18);
_burn(msg.sender, amount);
SafeTransferLib.safeTransferETH(msg.sender, payout);
emit Redeem(msg.sender, amount, payout, price);
}
function burn(uint256 amount) external returns (bool success) {
_burn(msg.sender, amount);
success = true;
emit Transfer(msg.sender, address(0), amount);
}
function name() public pure returns (string memory) {
return NAME;
}
function symbol() public pure returns (string memory) {
return SYMBOL;
}
function decimals() public pure returns (uint8) {
return DECIMALS;
}
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
function balanceOf(address _owner) public view returns (uint256) {
return _balances[_owner];
}
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
function pool() external view returns (address) {
return pair;
}
function ownership() external pure returns (address) {
return OWNER;
}
function bondPrice() external view returns (uint256) {
return _currentMarketPrice(true);
}
function bondPriceLp() external view returns (uint256) {
return _currentMarketPrice(false);
}
function redemptionPrice() external view returns (uint256) {
return ((address(this).balance * 1e18) / _totalSupply);
}
function marketInfo()
external
view
returns (PricingLibrary.Market memory, PricingLibrary.Term memory, PricingLibrary.Data memory)
{
return (market, term, data);
}
function lpMarketInfo()
external
view
returns (PricingLibrary.Market memory, PricingLibrary.Term memory, PricingLibrary.Data memory)
{
return (lpMarket, lpTerm, lpData);
}
function depository() external pure returns (address) {
return BOND_DEPOSITORY;
}
function exclude(address user) external {
if (msg.sender != OWNER) revert OnlyOwner();
_excluded[user] = true;
emit UserExcluded(user);
}
function addAmm(address _pool) external {
if (msg.sender != OWNER) revert OnlyOwner();
_amms[_pool] = true;
emit AmmAdded(_pool);
}
function launch() external {
if (msg.sender != OWNER) revert OnlyOwner();
if (launched) revert AlreadyLaunched();
_addInitialLiquidity();
launched = true;
emit Launched(block.timestamp);
}
function create(uint256 ethBondAmount, uint256 lpBondAmount) external {
if (msg.sender != OWNER && msg.sender != BOND_DEPOSITORY) revert InvalidSender();
if (term.conclusion > block.timestamp) revert OngoingBonds();
uint256 currentBalance = _balances[address(this)];
if (currentBalance > 0) {
unchecked {
_balances[address(this)] -= currentBalance;
_totalSupply -= currentBalance;
}
emit Transfer(address(this), address(0), currentBalance);
}
uint256 total = ethBondAmount + lpBondAmount;
transferFrom(msg.sender, address(this), total);
ethCapacityNext = ethBondAmount;
lpCapacityNext = lpBondAmount;
_create();
_createLpMarket();
}
function _createUniswapV2Pair(address _token0, address _token1) private returns (address _pair) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0xc9c6539600000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 4), and(_token0, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(ptr, 36), and(_token1, 0xffffffffffffffffffffffffffffffffffffffff))
let result := call(gas(), UNISWAP_V2_FACTORY, 0, ptr, 68, 0, 32)
if iszero(result) { revert(0, 0) }
_pair := mload(0x00)
}
}
function _addInitialLiquidity() private {
uint256 tokenAmount = _balances[address(this)];
assembly {
let ptr := mload(0x40)
mstore(ptr, 0xf305d71900000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 4), and(address(), 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(ptr, 36), tokenAmount)
mstore(add(ptr, 68), 0)
mstore(add(ptr, 100), 0)
mstore(add(ptr, 132), BOND_DEPOSITORY)
mstore(add(ptr, 164), timestamp())
let result := call(gas(), UNISWAP_V2_ROUTER, balance(address()), ptr, 196, 0, 0)
if iszero(result) { revert(0, 0) }
}
}
function _create() private {
uint96 targetDebt = uint96(ethCapacityNext);
if (ethCapacityNext > 0) {
uint256 initialPrice = _getPrice();
uint96 capacity = targetDebt;
uint96 maxPayout = uint96(targetDebt * 86400 / INTERVAL);
uint256 controlVariable = initialPrice * _totalSupply / targetDebt;
bool policy = _policy(capacity, initialPrice);
uint48 conclusion = uint48(block.timestamp + INTERVAL);
if (policy) {
market = PricingLibrary.Market(capacity, targetDebt, maxPayout, 0, 0);
term = PricingLibrary.Term(conclusion, controlVariable);
data =
PricingLibrary.Data(uint48(block.timestamp), uint48(block.timestamp), uint48(INTERVAL), 86400, 1800);
emit CreateMarket(capacity, block.timestamp, conclusion);
} else {
_burn(address(this), capacity);
term.conclusion = uint48(block.timestamp + INTERVAL);
market.capacity = 0;
}
}
ethCapacityNext = 0;
}
function _createLpMarket() private {
uint96 targetDebt = uint96(lpCapacityNext);
if (targetDebt > 0) {
uint256 initialPrice = _getLpPrice();
uint96 capacity = targetDebt;
uint96 maxPayout = uint96(targetDebt * 86400 / INTERVAL);
uint256 controlVariable = initialPrice * _totalSupply / targetDebt;
bool policy = _policy(capacity, initialPrice);
uint48 conclusion = uint48(block.timestamp + INTERVAL);
if (policy) {
lpMarket = PricingLibrary.Market(capacity, targetDebt, maxPayout, 0, 0);
lpTerm = PricingLibrary.Term(conclusion, controlVariable);
lpData =
PricingLibrary.Data(uint48(block.timestamp), uint48(block.timestamp), uint48(INTERVAL), 86400, 1800);
emit CreateMarket(capacity, block.timestamp, conclusion);
} else {
_burn(address(this), capacity);
lpTerm.conclusion = uint48(block.timestamp + INTERVAL);
lpMarket.capacity = 0;
}
}
lpCapacityNext = 0;
}
function _policy(uint256 capacity, uint256 price) private view returns (bool decision) {
uint256 supply = _totalSupply;
uint256 burnRelative = (address(this).balance * 1e18) / (supply - capacity);
uint256 bondRelative = ((address(this).balance * 1e18) + ((capacity * price))) / supply;
decision = burnRelative >= bondRelative ? false : true;
}
function _transfer(address from, address to, uint256 _value) private {
if (_value > _balances[from]) revert InsufficentBalance();
bool fees;
if (_amms[to] || _amms[from]) {
if (_excluded[to] || _excluded[from]) {
fees = false;
} else {
fees = true;
}
}
if (fees) {
uint256 fee = (_value * FEE) / 1000;
unchecked {
_balances[from] -= _value;
_balances[BOND_DEPOSITORY] += fee;
}
_value -= fee;
unchecked {
_balances[to] += _value;
}
} else {
unchecked {
_balances[from] -= _value;
_balances[to] += _value;
}
}
emit Transfer(from, to, _value);
}
function _mint(address to, uint256 value) private {
unchecked {
_balances[to] += value;
_totalSupply += value;
}
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) private {
if (_balances[from] < value) revert InsufficentBalance();
unchecked {
_balances[from] -= value;
_totalSupply -= value;
}
emit Transfer(from, address(0), value);
}
function _bond(address to, uint256 value) private returns (bool success) {
if (value > _balances[address(this)]) revert InsufficentBondsAvailable();
unchecked {
_balances[to] += value;
_balances[address(this)] -= value;
}
success = true;
emit Transfer(address(this), to, value);
}
function _getPrice() private view returns (uint256 price) {
address _pair = pair;
uint112 reserve0;
uint112 reserve1;
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000)
let success := staticcall(gas(), _pair, ptr, 4, 0, 0)
if iszero(success) { revert(0, 0) }
returndatacopy(0x00, 0, 32)
returndatacopy(0x20, 0x20, 32)
reserve0 := mload(0x00)
reserve1 := mload(0x20)
}
if (zeroForOne) {
price = FullMath.mulDiv(uint256(reserve1), 1e18, uint256(reserve0));
} else {
price = FullMath.mulDiv(uint256(reserve0), 1e18, uint256(reserve1));
}
}
function _getLpPrice() private view returns (uint256 _lpPrice) {
address _pair = pair;
uint112 reserve0;
uint112 reserve1;
uint256 lpTotalSupply;
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000)
let success := staticcall(gas(), _pair, ptr, 4, 0, 0)
if iszero(success) { revert(0, 0) }
returndatacopy(0x00, 0, 32)
returndatacopy(0x20, 0x20, 32)
reserve0 := mload(0x00)
reserve1 := mload(0x20)
mstore(add(ptr, 0x20), 0x18160ddd00000000000000000000000000000000000000000000000000000000)
let result := staticcall(gas(), _pair, add(ptr, 0x20), 4, 0, 32)
lpTotalSupply := mload(0x00)
}
if (zeroForOne) {
_lpPrice = FullMath.mulDiv(reserve0 * 2, 1e18, lpTotalSupply);
} else {
_lpPrice = FullMath.mulDiv(reserve1 * 2, 1e18, lpTotalSupply);
}
}
function _getTokens(address _pair) private view returns (address _token0, address _token1) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x0dfe168100000000000000000000000000000000000000000000000000000000)
let resultToken0 := staticcall(gas(), _pair, ptr, 4, 0, 32)
mstore(add(ptr, 4), 0xd21220a700000000000000000000000000000000000000000000000000000000)
let resultToken1 := staticcall(gas(), _pair, add(ptr, 4), 4, 32, 32)
if or(iszero(resultToken0), iszero(resultToken1)) { revert(0, 0) }
_token0 := mload(0x00)
_token1 := mload(0x20)
}
}
function _currentMarketPrice(bool eth) private view returns (uint256) {
if (eth) {
return (
FullMath.mulDiv(
_currentControlVariable(term.controlVariable, adjustment),
PricingLibrary.debtRatio(market.totalDebt, _totalSupply),
1e18
)
);
} else {
return (
FullMath.mulDiv(
_currentControlVariable(lpTerm.controlVariable, lpAdjustment),
PricingLibrary.debtRatio(lpMarket.totalDebt, _totalSupply),
1e18
)
);
}
}
function _currentControlVariable(uint256 controlVariable, PricingLibrary.Adjustment memory info)
private
view
returns (uint256)
{
(uint256 decay,,) = PricingLibrary.controlDecay(info);
return controlVariable - decay;
}
receive() external payable {}
}
文件 6 的 7:PricingLibrary.sol
pragma solidity 0.8.23;
library PricingLibrary {
struct Data {
uint48 lastTune;
uint48 lastDecay;
uint48 length;
uint48 depositInterval;
uint48 tuneInterval;
}
struct Market {
uint96 capacity;
uint96 totalDebt;
uint96 maxPayout;
uint96 sold;
uint96 purchased;
}
struct Adjustment {
uint128 change;
uint48 lastAdjustment;
uint48 timeToAdjusted;
bool active;
}
struct Term {
uint48 conclusion;
uint256 controlVariable;
}
function decay(Data memory data, Market memory market, Term memory terms, Adjustment memory adjustments)
internal
view
returns (Market memory, Data memory, Term memory, Adjustment memory)
{
uint48 time = uint48(block.timestamp);
market.totalDebt -= debtDecay(data, market);
data.lastDecay = time;
if (adjustments.active) {
(uint128 adjustby, uint48 dt, bool stillActive) = controlDecay(adjustments);
terms.controlVariable -= adjustby;
if (stillActive) {
adjustments.change -= adjustby;
adjustments.timeToAdjusted -= dt;
adjustments.lastAdjustment = time;
} else {
adjustments.active = false;
}
}
return (market, data, terms, adjustments);
}
function controlDecay(Adjustment memory info) internal view returns (uint128, uint48, bool) {
if (!info.active) return (0, 0, false);
uint48 secondsSince = uint48(block.timestamp) - info.lastAdjustment;
bool active = secondsSince < info.timeToAdjusted;
uint128 _decay = active ? (info.change * secondsSince) / info.timeToAdjusted : info.change;
return (_decay, secondsSince, active);
}
function marketPrice(uint256 _controlVariable, uint256 _totalDebt, uint256 _totalSupply)
internal
pure
returns (uint256)
{
return ((_controlVariable * debtRatio(_totalDebt, _totalSupply)) / 1e18);
}
function debtRatio(uint256 _totalDebt, uint256 _totalSupply) internal pure returns (uint256) {
return ((_totalDebt * 1e18) / _totalSupply);
}
function debtDecay(Data memory data, Market memory market) internal view returns (uint64) {
uint256 secondsSince = block.timestamp - data.lastDecay;
return uint64((market.totalDebt * secondsSince) / data.length);
}
struct TuneCache {
uint256 remaining;
uint256 price;
uint256 capacity;
uint256 targetDebt;
uint256 ncv;
}
function tune(
uint48 time,
Market memory market,
Term memory term,
Data memory data,
Adjustment memory adjustment,
uint256 _totalSupply
) internal pure returns (Market memory, Term memory, Data memory, Adjustment memory) {
TuneCache memory cache;
if (time >= data.lastTune + data.tuneInterval) {
cache.remaining = term.conclusion - time;
cache.price = marketPrice(term.controlVariable, market.totalDebt, _totalSupply);
cache.capacity = market.capacity;
market.maxPayout = uint96((cache.capacity * data.depositInterval / cache.remaining));
cache.targetDebt = cache.capacity * data.length / cache.remaining;
cache.ncv = (cache.price * _totalSupply) / cache.targetDebt;
if (cache.ncv < term.controlVariable) {
uint128 change = uint128(term.controlVariable - cache.ncv);
adjustment = Adjustment(change, time, data.tuneInterval, true);
} else {
term.controlVariable = cache.ncv;
}
data.lastTune = time;
}
return (market, term, data, adjustment);
}
}
文件 7 的 7:SafeTransferLib.sol
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
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)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
{
"compilationTarget": {
"src/KotoV3.sol": "KotoV3"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":solmate/=lib/solmate/src/"
]
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyLaunched","type":"error"},{"inputs":[],"name":"BondFailed","type":"error"},{"inputs":[],"name":"InsufficentAllowance","type":"error"},{"inputs":[],"name":"InsufficentBalance","type":"error"},{"inputs":[],"name":"InsufficentBondsAvailable","type":"error"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"InvalidTransfer","type":"error"},{"inputs":[],"name":"LimitsReached","type":"error"},{"inputs":[],"name":"MarketClosed","type":"error"},{"inputs":[],"name":"MaxPayout","type":"error"},{"inputs":[],"name":"OngoingBonds","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"RedeemFailed","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolAdded","type":"address"}],"name":"AmmAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bondPrice","type":"uint256"}],"name":"Bond","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bonds","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint48","name":"end","type":"uint48"}],"name":"CreateMarket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"kotoAdded","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAdded","type":"uint256"}],"name":"IncreaseLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"Launched","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"LimitsRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"openingTime","type":"uint256"}],"name":"OpenBondMarket","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"burned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"floorPrice","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userToExclude","type":"address"}],"name":"UserExcluded","type":"event"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"}],"name":"addAmm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bond","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lpAmount","type":"uint256"}],"name":"bondLp","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bondPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bondPriceLp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ethBondAmount","type":"uint256"},{"internalType":"uint256","name":"lpBondAmount","type":"uint256"}],"name":"create","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"depository","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"exclude","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"launch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lpMarketInfo","outputs":[{"components":[{"internalType":"uint96","name":"capacity","type":"uint96"},{"internalType":"uint96","name":"totalDebt","type":"uint96"},{"internalType":"uint96","name":"maxPayout","type":"uint96"},{"internalType":"uint96","name":"sold","type":"uint96"},{"internalType":"uint96","name":"purchased","type":"uint96"}],"internalType":"struct PricingLibrary.Market","name":"","type":"tuple"},{"components":[{"internalType":"uint48","name":"conclusion","type":"uint48"},{"internalType":"uint256","name":"controlVariable","type":"uint256"}],"internalType":"struct PricingLibrary.Term","name":"","type":"tuple"},{"components":[{"internalType":"uint48","name":"lastTune","type":"uint48"},{"internalType":"uint48","name":"lastDecay","type":"uint48"},{"internalType":"uint48","name":"length","type":"uint48"},{"internalType":"uint48","name":"depositInterval","type":"uint48"},{"internalType":"uint48","name":"tuneInterval","type":"uint48"}],"internalType":"struct PricingLibrary.Data","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketInfo","outputs":[{"components":[{"internalType":"uint96","name":"capacity","type":"uint96"},{"internalType":"uint96","name":"totalDebt","type":"uint96"},{"internalType":"uint96","name":"maxPayout","type":"uint96"},{"internalType":"uint96","name":"sold","type":"uint96"},{"internalType":"uint96","name":"purchased","type":"uint96"}],"internalType":"struct PricingLibrary.Market","name":"","type":"tuple"},{"components":[{"internalType":"uint48","name":"conclusion","type":"uint48"},{"internalType":"uint256","name":"controlVariable","type":"uint256"}],"internalType":"struct PricingLibrary.Term","name":"","type":"tuple"},{"components":[{"internalType":"uint48","name":"lastTune","type":"uint48"},{"internalType":"uint48","name":"lastDecay","type":"uint48"},{"internalType":"uint48","name":"length","type":"uint48"},{"internalType":"uint48","name":"depositInterval","type":"uint48"},{"internalType":"uint48","name":"tuneInterval","type":"uint48"}],"internalType":"struct PricingLibrary.Data","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"ownership","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"payout","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redemptionPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]