Files
smom-dbis-138/contracts/bridge/atomic/AtomicLiquidityVault.sol
defiQUG 76aa419320 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
2026-04-07 23:40:52 -07:00

152 lines
6.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IAtomicLiquidityVault} from "./interfaces/IAtomicLiquidityVault.sol";
import {AtomicTypes} from "./AtomicTypes.sol";
contract AtomicLiquidityVault is AccessControl, IAtomicLiquidityVault {
using SafeERC20 for IERC20;
bytes32 public constant COORDINATOR_ROLE = keccak256("COORDINATOR_ROLE");
bytes32 public constant RECONCILER_ROLE = keccak256("RECONCILER_ROLE");
bytes32 public constant BUFFER_MANAGER_ROLE = keccak256("BUFFER_MANAGER_ROLE");
struct StoredCorridorState {
uint256 totalLiquidity;
uint256 reservedLiquidity;
uint256 targetBuffer;
uint256 settlementBacklog;
}
struct Reservation {
bytes32 corridorId;
address token;
uint256 amount;
bool exists;
bool fulfilled;
}
mapping(bytes32 => mapping(address => StoredCorridorState)) private _corridorState;
mapping(bytes32 => Reservation) public reservations;
event CorridorFunded(bytes32 indexed corridorId, address indexed token, address indexed funder, uint256 amount);
event LiquidityReserved(bytes32 indexed obligationId, bytes32 indexed corridorId, address indexed token, uint256 amount);
event ReservedLiquidityFulfilled(bytes32 indexed obligationId, address indexed recipient, uint256 amount);
event ReservationReleased(bytes32 indexed obligationId, uint256 amount);
event SettlementReconciled(bytes32 indexed corridorId, address indexed token, uint256 amount);
event TargetBufferSet(bytes32 indexed corridorId, address indexed token, uint256 targetBuffer);
error ReservationExists();
error ReservationMissing();
error ReservationAlreadyFulfilled();
error InsufficientFreeLiquidity();
constructor(address admin) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(RECONCILER_ROLE, admin);
_grantRole(BUFFER_MANAGER_ROLE, admin);
}
function fundCorridor(bytes32 corridorId, address token, uint256 amount) external {
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
_corridorState[corridorId][token].totalLiquidity += amount;
emit CorridorFunded(corridorId, token, msg.sender, amount);
}
function setTargetBuffer(bytes32 corridorId, address token, uint256 targetBuffer)
external
onlyRole(BUFFER_MANAGER_ROLE)
{
_corridorState[corridorId][token].targetBuffer = targetBuffer;
emit TargetBufferSet(corridorId, token, targetBuffer);
}
function reserveLiquidity(bytes32 corridorId, address token, bytes32 obligationId, uint256 amount)
external
onlyRole(COORDINATOR_ROLE)
{
if (reservations[obligationId].exists) revert ReservationExists();
if (freeLiquidity(corridorId, token) < amount) revert InsufficientFreeLiquidity();
reservations[obligationId] = Reservation({
corridorId: corridorId,
token: token,
amount: amount,
exists: true,
fulfilled: false
});
_corridorState[corridorId][token].reservedLiquidity += amount;
emit LiquidityReserved(obligationId, corridorId, token, amount);
}
function fulfillReservedLiquidity(bytes32 obligationId, address recipient)
external
onlyRole(COORDINATOR_ROLE)
returns (uint256 amount)
{
Reservation storage reservation = reservations[obligationId];
if (!reservation.exists) revert ReservationMissing();
if (reservation.fulfilled) revert ReservationAlreadyFulfilled();
reservation.fulfilled = true;
StoredCorridorState storage state = _corridorState[reservation.corridorId][reservation.token];
state.reservedLiquidity -= reservation.amount;
state.totalLiquidity -= reservation.amount;
state.settlementBacklog += reservation.amount;
IERC20(reservation.token).safeTransfer(recipient, reservation.amount);
emit ReservedLiquidityFulfilled(obligationId, recipient, reservation.amount);
return reservation.amount;
}
function releaseReservation(bytes32 obligationId)
external
onlyRole(COORDINATOR_ROLE)
returns (uint256 amount)
{
Reservation storage reservation = reservations[obligationId];
if (!reservation.exists) revert ReservationMissing();
if (reservation.fulfilled) revert ReservationAlreadyFulfilled();
amount = reservation.amount;
_corridorState[reservation.corridorId][reservation.token].reservedLiquidity -= amount;
delete reservations[obligationId];
emit ReservationReleased(obligationId, amount);
}
function reconcileSettlement(bytes32 corridorId, address token, uint256 amount, address from)
external
onlyRole(RECONCILER_ROLE)
{
IERC20(token).safeTransferFrom(from, address(this), amount);
StoredCorridorState storage state = _corridorState[corridorId][token];
state.totalLiquidity += amount;
uint256 backlog = state.settlementBacklog;
state.settlementBacklog = amount >= backlog ? 0 : backlog - amount;
emit SettlementReconciled(corridorId, token, amount);
}
function getCorridorLiquidityState(bytes32 corridorId, address token)
external
view
returns (AtomicTypes.CorridorLiquidityState memory state)
{
StoredCorridorState memory stored = _corridorState[corridorId][token];
state = AtomicTypes.CorridorLiquidityState({
totalLiquidity: stored.totalLiquidity,
reservedLiquidity: stored.reservedLiquidity,
freeLiquidity: stored.totalLiquidity - stored.reservedLiquidity,
targetBuffer: stored.targetBuffer,
settlementBacklog: stored.settlementBacklog
});
}
function freeLiquidity(bytes32 corridorId, address token) public view returns (uint256) {
StoredCorridorState memory state = _corridorState[corridorId][token];
return state.totalLiquidity - state.reservedLiquidity;
}
}