feat: bridges, PMM, flash workflow, token-aggregation, and deployment docs
- 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
This commit is contained in:
@@ -15,8 +15,12 @@ import "../reserve/IReserveSystem.sol";
|
||||
interface IDODOPMMPool {
|
||||
function _BASE_TOKEN_() external view returns (address);
|
||||
function _QUOTE_TOKEN_() external view returns (address);
|
||||
function sellBase(uint256 amount) external returns (uint256);
|
||||
function sellQuote(uint256 amount) external returns (uint256);
|
||||
function querySellBase(address trader, uint256 payBaseAmount) external view returns (uint256 receiveQuoteAmount, uint256 mtFee);
|
||||
function querySellQuote(address trader, uint256 payQuoteAmount) external view returns (uint256 receiveBaseAmount, uint256 mtFee);
|
||||
/// @notice Official DODO DVM: excess base in pool vs reserve is sold; `to` receives quote.
|
||||
function sellBase(address to) external returns (uint256 receiveQuoteAmount);
|
||||
/// @notice Official DODO DVM: excess quote in pool vs reserve is sold; `to` receives base.
|
||||
function sellQuote(address to) external returns (uint256 receiveBaseAmount);
|
||||
function buyShares(address to) external returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare);
|
||||
function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve);
|
||||
function getMidPrice() external view returns (uint256);
|
||||
@@ -54,6 +58,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
|
||||
bytes32 public constant POOL_MANAGER_ROLE = keccak256("POOL_MANAGER_ROLE");
|
||||
bytes32 public constant SWAP_OPERATOR_ROLE = keccak256("SWAP_OPERATOR_ROLE");
|
||||
uint256 private constant POOL_SURFACE_SAMPLE_AMOUNT = 1;
|
||||
|
||||
// DODO contracts
|
||||
address public immutable dodoVendingMachine;
|
||||
@@ -68,6 +73,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
// Pool mappings
|
||||
mapping(address => mapping(address => address)) public pools; // token0 => token1 => pool
|
||||
mapping(address => bool) public isRegisteredPool;
|
||||
mapping(address => bool) public hasStandardPoolSurface;
|
||||
|
||||
// Pool configuration
|
||||
struct PoolConfig {
|
||||
@@ -116,6 +122,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
address indexed quoteToken,
|
||||
address importer
|
||||
);
|
||||
event PoolSurfaceValidated(address indexed pool, bool standardSurface);
|
||||
|
||||
constructor(
|
||||
address admin,
|
||||
@@ -170,6 +177,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
isOpenTWAP // Enable TWAP
|
||||
);
|
||||
|
||||
_requireBasicPoolSurface(pool, compliantUSDT, officialUSDT);
|
||||
_recordPool(pool, compliantUSDT, officialUSDT, lpFeeRate, initialPrice, k, isOpenTWAP);
|
||||
|
||||
emit PoolCreated(pool, compliantUSDT, officialUSDT, msg.sender);
|
||||
@@ -199,6 +207,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
isOpenTWAP
|
||||
);
|
||||
|
||||
_requireBasicPoolSurface(pool, compliantUSDC, officialUSDC);
|
||||
_recordPool(pool, compliantUSDC, officialUSDC, lpFeeRate, initialPrice, k, isOpenTWAP);
|
||||
|
||||
emit PoolCreated(pool, compliantUSDC, officialUSDC, msg.sender);
|
||||
@@ -228,6 +237,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
isOpenTWAP
|
||||
);
|
||||
|
||||
_requireBasicPoolSurface(pool, compliantUSDT, compliantUSDC);
|
||||
_recordPool(pool, compliantUSDT, compliantUSDC, lpFeeRate, initialPrice, k, isOpenTWAP);
|
||||
|
||||
emit PoolCreated(pool, compliantUSDT, compliantUSDC, msg.sender);
|
||||
@@ -264,6 +274,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
isOpenTWAP
|
||||
);
|
||||
|
||||
_requireBasicPoolSurface(pool, baseToken, quoteToken);
|
||||
_recordPool(pool, baseToken, quoteToken, lpFeeRate, initialPrice, k, isOpenTWAP);
|
||||
|
||||
emit PoolCreated(pool, baseToken, quoteToken, msg.sender);
|
||||
@@ -300,6 +311,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
(baseToken, quoteToken) = (quoteToken, baseToken);
|
||||
}
|
||||
|
||||
_requireBasicPoolSurface(pool, baseToken, quoteToken);
|
||||
_recordPool(pool, baseToken, quoteToken, lpFeeRate, initialPrice, k, isOpenTWAP);
|
||||
emit PoolImported(pool, baseToken, quoteToken, msg.sender);
|
||||
}
|
||||
@@ -326,10 +338,22 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
|
||||
// Call buyShares on DODO pool to add liquidity
|
||||
(baseShare, quoteShare, lpShare) = IDODOPMMPool(pool).buyShares(msg.sender);
|
||||
_setStandardPoolSurface(pool, config.baseToken, config.quoteToken);
|
||||
|
||||
emit LiquidityAdded(pool, msg.sender, baseAmount, quoteAmount, lpShare);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Re-run full standard-surface validation for an already-registered pool.
|
||||
* @dev Useful during migrations when a replacement pool has been seeded and should
|
||||
* now qualify as a standard DODO venue for routers and indexers.
|
||||
*/
|
||||
function refreshPoolSurface(address pool) external onlyRole(POOL_MANAGER_ROLE) returns (bool standardSurface) {
|
||||
require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered");
|
||||
PoolConfig memory config = poolConfigs[pool];
|
||||
standardSurface = _setStandardPoolSurface(pool, config.baseToken, config.quoteToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Swap cUSDT for official USDT via DODO PMM
|
||||
* @param pool Pool address
|
||||
@@ -348,8 +372,8 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
// Transfer cUSDT to pool
|
||||
IERC20(compliantUSDT).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
|
||||
// Execute swap (sell base token)
|
||||
amountOut = IDODOPMMPool(pool).sellBase(amountIn);
|
||||
// Execute swap (sell base token) — DVM sends quote to msg.sender
|
||||
amountOut = IDODOPMMPool(pool).sellBase(msg.sender);
|
||||
|
||||
require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output");
|
||||
|
||||
@@ -374,8 +398,8 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
// Transfer USDT to pool
|
||||
IERC20(officialUSDT).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
|
||||
// Execute swap (sell quote token)
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(amountIn);
|
||||
// Execute swap (sell quote token) — DVM sends base to msg.sender
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(msg.sender);
|
||||
|
||||
require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output");
|
||||
|
||||
@@ -398,7 +422,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
require(amountIn > 0, "DODOPMMIntegration: zero amount");
|
||||
|
||||
IERC20(compliantUSDC).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellBase(amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellBase(msg.sender);
|
||||
|
||||
require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output");
|
||||
|
||||
@@ -421,7 +445,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
require(amountIn > 0, "DODOPMMIntegration: zero amount");
|
||||
|
||||
IERC20(officialUSDC).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(msg.sender);
|
||||
|
||||
require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output");
|
||||
|
||||
@@ -441,9 +465,8 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
require(amountIn > 0, "DODOPMMIntegration: zero amount");
|
||||
|
||||
IERC20(compliantUSDT).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellBase(amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellBase(msg.sender);
|
||||
require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output");
|
||||
IERC20(compliantUSDC).safeTransfer(msg.sender, amountOut);
|
||||
emit SwapExecuted(pool, compliantUSDT, compliantUSDC, amountIn, amountOut, msg.sender);
|
||||
}
|
||||
|
||||
@@ -460,9 +483,8 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
require(amountIn > 0, "DODOPMMIntegration: zero amount");
|
||||
|
||||
IERC20(compliantUSDC).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(msg.sender);
|
||||
require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output");
|
||||
IERC20(compliantUSDT).safeTransfer(msg.sender, amountOut);
|
||||
emit SwapExecuted(pool, compliantUSDC, compliantUSDT, amountIn, amountOut, msg.sender);
|
||||
}
|
||||
|
||||
@@ -486,16 +508,15 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
if (tokenIn == config.baseToken) {
|
||||
tokenOut = config.quoteToken;
|
||||
IERC20(tokenIn).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellBase(amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellBase(msg.sender);
|
||||
} else if (tokenIn == config.quoteToken) {
|
||||
tokenOut = config.baseToken;
|
||||
IERC20(tokenIn).safeTransferFrom(msg.sender, pool, amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(amountIn);
|
||||
amountOut = IDODOPMMPool(pool).sellQuote(msg.sender);
|
||||
} else {
|
||||
revert("DODOPMMIntegration: token not in pool");
|
||||
}
|
||||
require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output");
|
||||
IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
|
||||
emit SwapExecuted(pool, tokenIn, tokenOut, amountIn, amountOut, msg.sender);
|
||||
}
|
||||
|
||||
@@ -574,6 +595,7 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
pools[config.baseToken][config.quoteToken] = address(0);
|
||||
pools[config.quoteToken][config.baseToken] = address(0);
|
||||
isRegisteredPool[pool] = false;
|
||||
hasStandardPoolSurface[pool] = false;
|
||||
|
||||
delete poolConfigs[pool];
|
||||
|
||||
@@ -605,4 +627,55 @@ contract DODOPMMIntegration is AccessControl, ReentrancyGuard {
|
||||
createdAt: block.timestamp
|
||||
});
|
||||
}
|
||||
|
||||
function _requireBasicPoolSurface(
|
||||
address pool,
|
||||
address expectedBaseToken,
|
||||
address expectedQuoteToken
|
||||
) internal view {
|
||||
require(pool != address(0), "DODOPMMIntegration: zero pool");
|
||||
require(IDODOPMMPool(pool)._BASE_TOKEN_() == expectedBaseToken, "DODOPMMIntegration: unexpected base token");
|
||||
require(IDODOPMMPool(pool)._QUOTE_TOKEN_() == expectedQuoteToken, "DODOPMMIntegration: unexpected quote token");
|
||||
|
||||
IDODOPMMPool(pool).getVaultReserve();
|
||||
IDODOPMMPool(pool).getMidPrice();
|
||||
IDODOPMMPool(pool)._BASE_RESERVE_();
|
||||
IDODOPMMPool(pool)._QUOTE_RESERVE_();
|
||||
}
|
||||
|
||||
function _setStandardPoolSurface(
|
||||
address pool,
|
||||
address expectedBaseToken,
|
||||
address expectedQuoteToken
|
||||
) internal returns (bool standardSurface) {
|
||||
standardSurface = _hasStandardPoolSurface(pool, expectedBaseToken, expectedQuoteToken);
|
||||
require(standardSurface, "DODOPMMIntegration: pool missing standard DODO surface");
|
||||
hasStandardPoolSurface[pool] = true;
|
||||
emit PoolSurfaceValidated(pool, true);
|
||||
}
|
||||
|
||||
function _hasStandardPoolSurface(
|
||||
address pool,
|
||||
address expectedBaseToken,
|
||||
address expectedQuoteToken
|
||||
) internal view returns (bool) {
|
||||
_requireBasicPoolSurface(pool, expectedBaseToken, expectedQuoteToken);
|
||||
|
||||
(uint256 baseReserve, uint256 quoteReserve) = IDODOPMMPool(pool).getVaultReserve();
|
||||
if (baseReserve == 0 || quoteReserve == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try IDODOPMMPool(pool).querySellBase(address(this), POOL_SURFACE_SAMPLE_AMOUNT) returns (uint256, uint256) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
try IDODOPMMPool(pool).querySellQuote(address(this), POOL_SURFACE_SAMPLE_AMOUNT) returns (uint256, uint256) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user