- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
135 lines
4.8 KiB
Solidity
135 lines
4.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
|
/**
|
|
* @title MockDVMPool
|
|
* @notice Minimal mock of a DODO PMM pool so DODOPMMIntegration can deploy and create pools on Chain 138
|
|
* @dev Not for production; use real DODO DVM when available. Implements view and swap stubs.
|
|
*/
|
|
contract MockDVMPool {
|
|
address public baseToken;
|
|
address public quoteToken;
|
|
uint256 public baseReserve;
|
|
uint256 public quoteReserve;
|
|
uint256 public midPrice;
|
|
uint256 public totalSupply;
|
|
mapping(address => uint256) private _shares;
|
|
uint256 public constant _K_ = 0.5e18; // 50% slippage factor (DODO convention)
|
|
uint256 public constant _LP_FEE_RATE_ = 3; // 0.03% in basis points
|
|
|
|
constructor(
|
|
address _baseToken,
|
|
address _quoteToken,
|
|
uint256 _initialPrice
|
|
) {
|
|
baseToken = _baseToken;
|
|
quoteToken = _quoteToken;
|
|
midPrice = _initialPrice;
|
|
baseReserve = 0;
|
|
quoteReserve = 0;
|
|
}
|
|
|
|
function _BASE_TOKEN_() external view returns (address) {
|
|
return baseToken;
|
|
}
|
|
|
|
function _QUOTE_TOKEN_() external view returns (address) {
|
|
return quoteToken;
|
|
}
|
|
|
|
function _BASE_RESERVE_() external view returns (uint256) {
|
|
return baseReserve;
|
|
}
|
|
|
|
function _QUOTE_RESERVE_() external view returns (uint256) {
|
|
return quoteReserve;
|
|
}
|
|
|
|
function getVaultReserve() external view returns (uint256, uint256) {
|
|
return (baseReserve, quoteReserve);
|
|
}
|
|
|
|
function balanceOf(address owner) external view returns (uint256) {
|
|
return _shares[owner];
|
|
}
|
|
|
|
function getMidPrice() external view returns (uint256) {
|
|
return midPrice;
|
|
}
|
|
|
|
/// @notice Oracle price (same as mid for mock); enables MCP dodo_pmm_v2_like profile
|
|
function getOraclePrice() external view returns (uint256) {
|
|
return midPrice;
|
|
}
|
|
|
|
function querySellBase(address, uint256 amount) external view returns (uint256, uint256) {
|
|
return ((amount * midPrice) / 1e18, 0);
|
|
}
|
|
|
|
function querySellQuote(address, uint256 amount) external view returns (uint256, uint256) {
|
|
if (midPrice == 0) {
|
|
return (0, 0);
|
|
}
|
|
return ((amount * 1e18) / midPrice, 0);
|
|
}
|
|
|
|
function sellBase(address to) external returns (uint256 receiveQuoteAmount) {
|
|
uint256 baseBal = IERC20(baseToken).balanceOf(address(this));
|
|
require(baseBal >= baseReserve, "MockDVMPool: base");
|
|
uint256 baseInput = baseBal - baseReserve;
|
|
if (baseInput == 0) return 0;
|
|
(receiveQuoteAmount,) = this.querySellBase(address(0), baseInput);
|
|
require(IERC20(quoteToken).transfer(to, receiveQuoteAmount), "MockDVMPool: q");
|
|
baseReserve = IERC20(baseToken).balanceOf(address(this));
|
|
quoteReserve = IERC20(quoteToken).balanceOf(address(this));
|
|
}
|
|
|
|
function sellQuote(address to) external returns (uint256 receiveBaseAmount) {
|
|
uint256 quoteBal = IERC20(quoteToken).balanceOf(address(this));
|
|
require(quoteBal >= quoteReserve, "MockDVMPool: quote");
|
|
uint256 quoteInput = quoteBal - quoteReserve;
|
|
if (quoteInput == 0) return 0;
|
|
(receiveBaseAmount,) = this.querySellQuote(address(0), quoteInput);
|
|
require(IERC20(baseToken).transfer(to, receiveBaseAmount), "MockDVMPool: b");
|
|
baseReserve = IERC20(baseToken).balanceOf(address(this));
|
|
quoteReserve = IERC20(quoteToken).balanceOf(address(this));
|
|
}
|
|
|
|
function buyShares(address to) external returns (uint256, uint256, uint256) {
|
|
uint256 baseBal = IERC20(baseToken).balanceOf(address(this));
|
|
uint256 quoteBal = IERC20(quoteToken).balanceOf(address(this));
|
|
require(baseBal >= baseReserve && quoteBal >= quoteReserve, "MockDVMPool: reserve");
|
|
|
|
uint256 baseInput = baseBal - baseReserve;
|
|
uint256 quoteInput = quoteBal - quoteReserve;
|
|
require(baseInput > 0 && quoteInput > 0, "MockDVMPool: no input");
|
|
|
|
uint256 lpShare;
|
|
if (totalSupply == 0) {
|
|
lpShare = baseBal;
|
|
require(lpShare > 2001, "MockDVMPool: MINT_AMOUNT_NOT_ENOUGH");
|
|
totalSupply = lpShare;
|
|
_shares[address(0)] = 1001;
|
|
lpShare -= 1001;
|
|
_shares[to] += lpShare;
|
|
} else {
|
|
uint256 baseShares = (baseInput * totalSupply) / baseReserve;
|
|
uint256 quoteShares = (quoteInput * totalSupply) / quoteReserve;
|
|
lpShare = baseShares < quoteShares ? baseShares : quoteShares;
|
|
totalSupply += lpShare;
|
|
_shares[to] += lpShare;
|
|
}
|
|
|
|
baseReserve = baseBal;
|
|
quoteReserve = quoteBal;
|
|
return (baseInput, quoteInput, lpShare);
|
|
}
|
|
|
|
function sync() external {
|
|
baseReserve = IERC20(baseToken).balanceOf(address(this));
|
|
quoteReserve = IERC20(quoteToken).balanceOf(address(this));
|
|
}
|
|
}
|