Reference implementation of the constant-product pair. This reply is not normative. Where it disagrees with the proposal, the proposal prevails.
IEquityDexPair
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
// Minimal constant-product pair. Liquidity is an internal balance, not an ERC-20.
interface IEquityDexPair {
function equityToken() external view returns (address);
function dividendAsset() external view returns (address);
function reserveDividend() external view returns (uint256);
function totalLiquidity() external view returns (uint256);
function liquidityOf(address account) external view returns (uint256);
function tradingPaused() external view returns (bool);
function lpScale() external view returns (uint256);
function dividendPerLiquidity() external view returns (uint256);
function unclaimedLiquidityDividend(address account) external view returns (uint256);
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external pure returns (uint256);
/// @notice Quote before the claim. The quote reserve is reduced by the dividend not yet claimed from the share token.
function getSwapAmountOut(uint256 amountEquIn, uint256 amountDivIn)
external
view
returns (uint256 amountEquOut, uint256 amountDivOut);
/// @notice Claim the share-token dividend and mark the price down. Trading stops for good when the claim consumes the quote reserve.
function sync() external returns (uint256 dividendPerShare);
function addLiquidity(uint256 amountEqu, uint256 amountDivMin, uint256 amountDivMax)
external
payable
returns (uint256 amountDiv, uint256 liquidity);
function removeLiquidity(uint256 liquidity, uint256 amountEquMin, uint256 amountDivMin)
external
returns (uint256 amountEqu, uint256 amountDiv);
function swap(uint256 amountEquIn, uint256 amountDivIn, uint256 minEquOut, uint256 minDivOut)
external
payable
returns (uint256 amountEqu, uint256 amountDiv);
function withdrawLiquidityDividend() external returns (uint256 amount);
event OnLiquidityAdded(address indexed account, uint256 amountEquity, uint256 amountDividend, uint256 liquidity);
event OnLiquidityRemoved(
address indexed account, uint256 liquidity, uint256 amountEquity, uint256 amountDividend
);
event OnSwapped(address indexed account, address indexed tokenIn, uint256 amountEquity, uint256 amountDividend);
event OnDividendMarkdown(uint256 received, uint256 credited, uint256 reserveDividend);
event OnLiquidityDividendWithdrawn(address indexed account, uint256 amount);
}
EquityDexPair
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;
import "./IEquityDexPair.sol";
import "./IEquityToken.sol";
import "./IERC20.sol";
// Minimal x * y = k pair.
// Received dividend A. When A < quote reserve R, credit liquidity 2A and reduce the reserve by A.
// When A >= R, credit liquidity A + R and stop trading.
// The second A is taken from the quote reserve so the price drops ex-dividend.
// The quote reserve is booked separately from dividends not yet withdrawn.
contract EquityDexPair is IEquityDexPair {
uint256 public constant MIN_LIQUIDITY = 1000;
uint256 public constant override lpScale = 2 ** 64;
uint256 private constant _CALL_GAS = 210000;
address public immutable override equityToken;
address public immutable override dividendAsset;
uint256 public override reserveDividend;
uint256 public override totalLiquidity;
uint256 public override dividendPerLiquidity;
bool public override tradingPaused;
mapping(address => uint256) public override liquidityOf;
mapping(address => uint256) public dividendPerLiquidityOf;
mapping(address => uint256) internal liquiditySettled;
uint256 private _locked;
modifier lock() {
require(_locked == 0);
_locked = 1;
_;
_locked = 0;
}
constructor(address equityToken_) {
require(equityToken_ != address(0));
equityToken = equityToken_;
dividendAsset = IEquityToken(equityToken_).dividendToken();
require(dividendAsset != equityToken_);
}
function unclaimedLiquidityDividend(address account) public view override returns (uint256) {
return _liquidityAmountAt(account, dividendPerLiquidity);
}
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
public
pure
override
returns (uint256)
{
require(amountIn > 0 && reserveIn > 0 && reserveOut > 0);
uint256 k = reserveIn * reserveOut;
uint256 grossOut = reserveOut - (k / (reserveIn + amountIn));
return grossOut * 997 / 1000;
}
function getSwapAmountOut(uint256 amountEquIn, uint256 amountDivIn)
external
view
override
returns (uint256 amountEquOut, uint256 amountDivOut)
{
require((amountEquIn > 0 && amountDivIn == 0) || (amountEquIn == 0 && amountDivIn > 0));
uint256 waiting = IEquityToken(equityToken).unclaimedDividendOf(address(this));
require(reserveDividend > waiting + MIN_LIQUIDITY);
uint256 tradable = reserveDividend - waiting;
uint256 equBal = IERC20(equityToken).balanceOf(address(this));
if (amountEquIn > 0) {
amountDivOut = getAmountOut(amountEquIn, equBal, tradable);
} else {
amountEquOut = getAmountOut(amountDivIn, tradable, equBal);
}
}
function sync() external override lock returns (uint256 marked) {
marked = _collect();
}
function addLiquidity(uint256 amountEqu, uint256 amountDivMin, uint256 amountDivMax)
external
payable
override
lock
returns (uint256 amountDiv, uint256 liquidity)
{
bool hadLiquidity = totalLiquidity > 0;
uint256 marked = _collect();
require(!tradingPaused);
_checkpointLiquidity(msg.sender);
uint256 equBefore = IERC20(equityToken).balanceOf(address(this));
_pullEquity(amountEqu);
uint256 liquidityOut;
if (totalLiquidity == 0) {
amountDiv = amountDivMax;
require(amountEqu > MIN_LIQUIDITY && amountDiv > MIN_LIQUIDITY);
liquidityOut = _sqrt(amountEqu * amountDiv) - MIN_LIQUIDITY;
totalLiquidity = MIN_LIQUIDITY;
} else {
amountDiv = amountEqu * reserveDividend / equBefore;
require(amountDivMin <= amountDiv && amountDiv <= amountDivMax);
require(amountEqu > MIN_LIQUIDITY && amountDiv > MIN_LIQUIDITY);
uint256 byEqu = amountEqu * totalLiquidity / equBefore;
uint256 byDiv = amountDiv * totalLiquidity / reserveDividend;
liquidityOut = byEqu < byDiv ? byEqu : byDiv;
}
require(liquidityOut > 0);
liquidity = liquidityOut;
_pullDividend(amountDiv, false);
liquidityOf[msg.sender] += liquidityOut;
totalLiquidity += liquidityOut;
reserveDividend += amountDiv;
if (dividendAsset == address(0) && msg.value > amountDiv) {
_pushEth(msg.sender, msg.value - amountDiv);
}
if (hadLiquidity) require(IEquityToken(equityToken).dividendPerShare() == marked);
emit OnLiquidityAdded(msg.sender, amountEqu, amountDiv, liquidityOut);
}
function removeLiquidity(uint256 liquidity, uint256 amountEquMin, uint256 amountDivMin)
external
override
lock
returns (uint256 amountEqu, uint256 amountDiv)
{
require(liquidity > 0 && liquidity <= liquidityOf[msg.sender]);
uint256 marked = _collect();
_checkpointLiquidity(msg.sender);
amountEqu = liquidity * IERC20(equityToken).balanceOf(address(this)) / totalLiquidity;
amountDiv = liquidity * reserveDividend / totalLiquidity;
require(amountEqu >= amountEquMin && amountDiv >= amountDivMin);
liquidityOf[msg.sender] -= liquidity;
totalLiquidity -= liquidity;
reserveDividend -= amountDiv;
uint256 dividend = liquiditySettled[msg.sender];
liquiditySettled[msg.sender] = 0;
_pushEquity(msg.sender, amountEqu);
_pushDividend(msg.sender, amountDiv + dividend);
require(IEquityToken(equityToken).dividendPerShare() == marked);
emit OnLiquidityRemoved(msg.sender, liquidity, amountEqu, amountDiv);
}
function swap(uint256 amountEquIn, uint256 amountDivIn, uint256 minEquOut, uint256 minDivOut)
external
payable
override
lock
returns (uint256 amountEqu, uint256 amountDiv)
{
require((amountEquIn > 0 && amountDivIn == 0) || (amountEquIn == 0 && amountDivIn > 0));
uint256 marked = _collect();
require(!tradingPaused);
if (amountEquIn > 0) {
require(msg.value == 0);
uint256 equBefore = IERC20(equityToken).balanceOf(address(this));
_pullEquity(amountEquIn);
amountDiv = getAmountOut(amountEquIn, equBefore, reserveDividend);
require(amountDiv >= minDivOut && amountDiv > 0);
reserveDividend -= amountDiv;
amountEqu = amountEquIn;
_pushDividend(msg.sender, amountDiv);
require(IEquityToken(equityToken).dividendPerShare() == marked);
emit OnSwapped(msg.sender, equityToken, amountEqu, amountDiv);
} else {
uint256 equBefore = IERC20(equityToken).balanceOf(address(this));
_pullDividend(amountDivIn, true);
amountEqu = getAmountOut(amountDivIn, reserveDividend, equBefore);
require(amountEqu >= minEquOut && amountEqu > 0);
reserveDividend += amountDivIn;
amountDiv = amountDivIn;
_pushEquity(msg.sender, amountEqu);
require(IEquityToken(equityToken).dividendPerShare() == marked);
emit OnSwapped(msg.sender, dividendAsset, amountEqu, amountDiv);
}
}
function withdrawLiquidityDividend() external override lock returns (uint256 amount) {
_collect();
_checkpointLiquidity(msg.sender);
amount = liquiditySettled[msg.sender];
if (amount == 0) return 0;
liquiditySettled[msg.sender] = 0;
_pushDividend(msg.sender, amount);
emit OnLiquidityDividendWithdrawn(msg.sender, amount);
}
function _collect() internal returns (uint256 marked) {
if (totalLiquidity == 0) return IEquityToken(equityToken).dividendPerShare();
if (dividendAsset == address(0)) {
uint256 beforeBal = address(this).balance;
uint256 received = IEquityToken(equityToken).withdrawDividend();
require(address(this).balance - beforeBal == received);
} else {
uint256 beforeBal = IERC20(dividendAsset).balanceOf(address(this));
uint256 received = IEquityToken(equityToken).withdrawDividend();
require(IERC20(dividendAsset).balanceOf(address(this)) - beforeBal == received);
_markdown(received);
}
marked = IEquityToken(equityToken).dividendPerShare();
}
function _markdown(uint256 received) internal {
if (received == 0) return;
require(totalLiquidity > 0);
uint256 credit;
if (received < reserveDividend) {
credit = received * 2;
reserveDividend -= received;
} else {
credit = received + reserveDividend;
reserveDividend = 0;
tradingPaused = true;
}
uint256 delta = credit * lpScale / totalLiquidity;
if (delta == 0) return;
dividendPerLiquidity += delta;
emit OnDividendMarkdown(received, credit, reserveDividend);
}
function _checkpointLiquidity(address account) internal {
uint256 height = dividendPerLiquidity;
if (height > dividendPerLiquidityOf[account]) {
liquiditySettled[account] = _liquidityAmountAt(account, height);
dividendPerLiquidityOf[account] = height;
}
}
function _liquidityAmountAt(address account, uint256 height) internal view returns (uint256) {
uint256 liq = liquidityOf[account];
uint256 start = dividendPerLiquidityOf[account];
uint256 accrued = 0;
if (liq > 0 && height > start) accrued = liq * (height - start) / lpScale;
return accrued + liquiditySettled[account];
}
function _pullEquity(uint256 amount) internal {
uint256 beforeBal = IERC20(equityToken).balanceOf(address(this));
require(IERC20(equityToken).transferFrom(msg.sender, address(this), amount));
require(IERC20(equityToken).balanceOf(address(this)) - beforeBal == amount);
}
function _pullDividend(uint256 amount, bool exactEth) internal {
if (dividendAsset == address(0)) {
if (exactEth) require(msg.value == amount);
else require(msg.value >= amount);
} else {
require(msg.value == 0);
uint256 beforeBal = IERC20(dividendAsset).balanceOf(address(this));
require(IERC20(dividendAsset).transferFrom(msg.sender, address(this), amount));
require(IERC20(dividendAsset).balanceOf(address(this)) - beforeBal >= amount);
}
}
function _pushEquity(address to, uint256 amount) internal {
if (amount == 0) return;
require(IERC20(equityToken).transfer(to, amount));
}
function _pushDividend(address to, uint256 amount) internal {
if (amount == 0) return;
if (dividendAsset == address(0)) _pushEth(to, amount);
else require(IERC20(dividendAsset).transfer(to, amount));
}
function _pushEth(address to, uint256 amount) internal {
(bool ok,) = to.call{value: amount, gas: _CALL_GAS}("");
require(ok);
}
function _sqrt(uint256 y) internal pure returns (uint256 z) {
if (y == 0) return 0;
uint256 x = y / 2 + 1;
z = y;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
}
receive() external payable {
require(_locked == 1);
require(msg.sender == equityToken && dividendAsset == address(0));
_markdown(msg.value);
}
}
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | EIP Editing Office Hour (EIP + ERC ) Meeting #111, Aug 18, 2026 | 0 | 18.71 | 13-08-2026 |
| 2 | EIP Editing Office Hour (EIP + ERC ) Meeting #108, July 28, 2026 | 0 | 18.32 | 27-07-2026 |
| 3 | EIP-0000 Preferential Gas Costs for Native ETH Operations – Draft for feedback | 0 | 13.65 | 05-08-2026 |
| 4 | EIP Editing Office Hour (EIP + ERC ) Meeting #107, July 21, 2026 | 0 | 17.62 | 15-07-2026 |
| 5 | EIP-0000 Native ETH as ERC-20 System Contract – Draft for feedback | 0 | 12.39 | 05-08-2026 |
| 6 | Cohort Order Book: an O(1) fully on-chain limit order book via generational fungible liquidity | 0 | 12.86 | 15-07-2026 |
| 7 | All Core Devs - Consensus (ACDC) #183, July 23 2026 | 0 | 15.18 | 13-07-2026 |
| 8 | The unleashed power of stacking EEZ & FCR seen through an example | 0 | 12.96 | 08-10-2026 |
| 9 | ERC-TBA: Prediction Market CTF Wrapper | 0 | 11.4 | 27-07-2026 |
| 10 | Mascot needed for Hegotá upgrade | 0 | 12.14 | 17-10-2026 |