- Expand token-aggregation API (report routes), canonical tokens, pools - Add flash vault contracts + tests (indexed, DODO cwUSDC, XAUT borrow) - PMM pools JSON, deploy/export scripts, metamask verified list Co-authored-by: Cursor <cursoragent@cursor.com>
138 lines
4.9 KiB
Solidity
138 lines
4.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import {Test} from "forge-std/Test.sol";
|
|
import {DBISEngineXIndexedLiquidityVault} from "../../contracts/flash/DBISEngineXIndexedLiquidityVault.sol";
|
|
import {MockMintableToken} from "../dbis/MockMintableToken.sol";
|
|
|
|
contract MockEngineXUniswapV3Pool {
|
|
address public immutable token0;
|
|
address public immutable token1;
|
|
uint24 public immutable fee;
|
|
uint160 public sqrtPriceX96;
|
|
int24 public tick;
|
|
uint128 public liquidity;
|
|
|
|
constructor(address token0_, address token1_, uint24 fee_) {
|
|
token0 = token0_;
|
|
token1 = token1_;
|
|
fee = fee_;
|
|
}
|
|
|
|
function setSlot0(uint160 sqrtPriceX96_, int24 tick_) external {
|
|
sqrtPriceX96 = sqrtPriceX96_;
|
|
tick = tick_;
|
|
}
|
|
|
|
function setLiquidity(uint128 liquidity_) external {
|
|
liquidity = liquidity_;
|
|
}
|
|
|
|
function slot0() external view returns (uint160, int24, uint16, uint16, uint16, uint8, bool) {
|
|
return (sqrtPriceX96, tick, 0, 0, 0, 0, true);
|
|
}
|
|
}
|
|
|
|
contract DBISEngineXIndexedLiquidityVaultTest is Test {
|
|
MockMintableToken internal cwusdc;
|
|
MockMintableToken internal usdc;
|
|
MockEngineXUniswapV3Pool internal pool;
|
|
DBISEngineXIndexedLiquidityVault internal vault;
|
|
|
|
address internal constant RECIPIENT = address(0xD00D);
|
|
uint160 internal constant ONE_TO_ONE_SQRT_PRICE_X96 = 79_228_162_514_264_337_593_543_950_336;
|
|
bytes32 internal constant PROOF_ID = bytes32("indexed-proof");
|
|
bytes32 internal constant SWAP_TX = bytes32(uint256(0xA1));
|
|
bytes32 internal constant LIQUIDITY_TX = bytes32(uint256(0xB1));
|
|
bytes32 internal constant ISO_HASH = bytes32(uint256(0x1001));
|
|
bytes32 internal constant AUDIT_HASH = bytes32(uint256(0x1002));
|
|
bytes32 internal constant PEG_HASH = bytes32(uint256(0x1003));
|
|
|
|
function setUp() public {
|
|
cwusdc = new MockMintableToken("Wrapped cWUSDC", "cWUSDC", 6, address(this));
|
|
usdc = new MockMintableToken("USD Coin", "USDC", 6, address(this));
|
|
pool = new MockEngineXUniswapV3Pool(address(cwusdc), address(usdc), 100);
|
|
pool.setSlot0(ONE_TO_ONE_SQRT_PRICE_X96, 0);
|
|
pool.setLiquidity(1_000_000);
|
|
cwusdc.mint(address(pool), 100_000_000);
|
|
usdc.mint(address(pool), 100_000_000);
|
|
|
|
vault = new DBISEngineXIndexedLiquidityVault(
|
|
address(cwusdc), address(usdc), address(pool), address(this), 100, 1_000, 1_000_000
|
|
);
|
|
}
|
|
|
|
function testRecordIndexedProofAnchorsPublicPoolState() public {
|
|
DBISEngineXIndexedLiquidityVault.IndexedProof memory proof = _proof(PROOF_ID);
|
|
|
|
(uint160 sqrtPriceX96, int24 tick, uint128 liquidity) = vault.recordIndexedProof(proof);
|
|
|
|
assertEq(sqrtPriceX96, ONE_TO_ONE_SQRT_PRICE_X96, "sqrt price");
|
|
assertEq(tick, 0, "tick");
|
|
assertEq(liquidity, 1_000_000, "liquidity");
|
|
assertTrue(vault.usedProofIds(PROOF_ID), "proof consumed");
|
|
}
|
|
|
|
function testRejectsDuplicateProofId() public {
|
|
vault.recordIndexedProof(_proof(PROOF_ID));
|
|
|
|
vm.expectRevert(bytes("proof used"));
|
|
vault.recordIndexedProof(_proof(PROOF_ID));
|
|
}
|
|
|
|
function testRejectsTickDrift() public {
|
|
pool.setSlot0(ONE_TO_ONE_SQRT_PRICE_X96, 101);
|
|
|
|
vm.expectRevert(bytes("tick drift too high"));
|
|
vault.recordIndexedProof(_proof(PROOF_ID));
|
|
}
|
|
|
|
function testRejectsInsufficientLiquidity() public {
|
|
pool.setLiquidity(999);
|
|
|
|
vm.expectRevert(bytes("insufficient liquidity"));
|
|
vault.recordIndexedProof(_proof(PROOF_ID));
|
|
}
|
|
|
|
function testRejectsOversizedProofAmount() public {
|
|
DBISEngineXIndexedLiquidityVault.IndexedProof memory proof = _proof(PROOF_ID);
|
|
proof.exactOutputAmount = 1_000_001;
|
|
|
|
vm.expectRevert(bytes("proof amount too high"));
|
|
vault.recordIndexedProof(proof);
|
|
}
|
|
|
|
function testOperatorAllowlist() public {
|
|
vault.setOperatorAllowlistEnabled(true);
|
|
|
|
vm.expectRevert(bytes("operator not approved"));
|
|
vm.prank(address(0xBEEF));
|
|
vault.recordIndexedProof(_proof(PROOF_ID));
|
|
|
|
vault.setOperatorApproved(address(0xBEEF), true);
|
|
vm.prank(address(0xBEEF));
|
|
vault.recordIndexedProof(_proof(PROOF_ID));
|
|
assertTrue(vault.usedProofIds(PROOF_ID), "proof consumed");
|
|
}
|
|
|
|
function testPauseBlocksProofs() public {
|
|
vault.pause();
|
|
|
|
vm.expectRevert(bytes("paused"));
|
|
vault.recordIndexedProof(_proof(PROOF_ID));
|
|
}
|
|
|
|
function _proof(bytes32 proofId) internal pure returns (DBISEngineXIndexedLiquidityVault.IndexedProof memory) {
|
|
return DBISEngineXIndexedLiquidityVault.IndexedProof({
|
|
proofId: proofId,
|
|
publicSwapTxHash: SWAP_TX,
|
|
liquidityTxHash: LIQUIDITY_TX,
|
|
outputRecipient: RECIPIENT,
|
|
exactOutputAmount: 100_000,
|
|
iso20022DocumentHash: ISO_HASH,
|
|
auditEnvelopeHash: AUDIT_HASH,
|
|
pegProofHash: PEG_HASH
|
|
});
|
|
}
|
|
}
|