Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/evm-contracts/contracts/lvr_amm/Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ contract Router is ReentrancyGuard, IMarketBuyCallback, IMarketSellCallback, IMa
error MarketNotAllowed();
error SlippageExceeded();
error ConfigFrozen();
error DynamicMarketsDisabled();

event FeeConfigUpdated(address indexed treasury, uint256 feeBps);
event FeeConfigFrozen(address indexed admin);
Expand Down Expand Up @@ -114,6 +115,7 @@ contract Router is ReentrancyGuard, IMarketBuyCallback, IMarketSellCallback, IMa
uint256 collateralIn
) public {
_checkRole(MARKET_OPERATOR_ROLE);
if (isDynamic) revert DynamicMarketsDisabled();
bytes32 marketId = keccak256(abi.encode(title, msg.sender, block.timestamp));
if (knownMarketIds[marketId]) revert MarketAlreadyExists();

Expand Down
37 changes: 26 additions & 11 deletions packages/evm-contracts/test/LvrMarket.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ contract LvrMarketTest is Test {
"Market for BTC price event",
"coingecko",
TEST_DUEL_KEY,
true,
false,
DURATION,
collateralIn
);
Expand All @@ -71,11 +71,26 @@ contract LvrMarketTest is Test {
vm.stopPrank();
}

function test_DynamicMarketsDisabled() public {
vm.startPrank(admin);
vm.expectRevert(Router.DynamicMarketsDisabled.selector);
router.create(
"Dynamic Market",
"Disabled until dynamic liquidity is safe",
"coingecko",
TEST_DUEL_KEY,
true,
DURATION,
100 * 10**18
);
vm.stopPrank();
}

function test_BuyYes() public {
uint256 collateralIn = 100 * 10**18;

vm.prank(admin);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, collateralIn);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, collateralIn);

address marketAddr = _marketAt(0);
LvrMarket market = LvrMarket(marketAddr);
Expand All @@ -99,7 +114,7 @@ contract LvrMarketTest is Test {
uint256 collateralIn = 100 * 10**18;

vm.prank(admin);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, collateralIn);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, collateralIn);

address marketAddr = _marketAt(0);
LvrMarket market = LvrMarket(marketAddr);
Expand Down Expand Up @@ -170,15 +185,15 @@ contract LvrMarketTest is Test {
function test_CreateRequiresMarketOperatorRole() public {
vm.startPrank(user1);
vm.expectRevert();
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, 100 * 10**18);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, 100 * 10**18);
vm.stopPrank();
}

function test_SlippageProtection() public {
uint256 collateralIn = 100 * 10**18;

vm.prank(admin);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, collateralIn);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, collateralIn);
address marketAddr = _marketAt(0);

vm.warp(block.timestamp + 1 hours);
Expand All @@ -195,7 +210,7 @@ contract LvrMarketTest is Test {
uint256 collateralIn = 100 * 10**18;

vm.prank(admin);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, collateralIn);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, collateralIn);
address marketAddr = _marketAt(0);
LvrMarket market = LvrMarket(marketAddr);

Expand All @@ -212,7 +227,7 @@ contract LvrMarketTest is Test {

function test_SettleMarketRequiresPendingState() public {
vm.prank(admin);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, 100 * 10**18);
router.create("Test Market", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, 100 * 10**18);
address marketAddr = _marketAt(0);

vm.expectRevert(bytes("Invalid Market State"));
Expand Down Expand Up @@ -249,13 +264,13 @@ contract LvrMarketTest is Test {
function test_ZeroDurationReverts() public {
vm.startPrank(admin);
vm.expectRevert(bytes("Duration must be positive"));
router.create("Zero Duration", "Desc", "Src", keccak256("zero-dur"), true, 0, 100 * 10**18);
router.create("Zero Duration", "Desc", "Src", keccak256("zero-dur"), false, 0, 100 * 10**18);
vm.stopPrank();
}

function test_GetPriceAfterDeadline() public {
vm.prank(admin);
router.create("Expiring Market", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, 100 * 10**18);
router.create("Expiring Market", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, 100 * 10**18);
address marketAddr = _marketAt(0);
LvrMarket market = LvrMarket(marketAddr);

Expand Down Expand Up @@ -298,7 +313,7 @@ contract LvrMarketTest is Test {

function test_AdminResolveSelectorRemoved() public {
vm.prank(admin);
router.create("Bond Slash Test", "Desc", "Src", TEST_DUEL_KEY, true, DURATION, 100 * 10**18);
router.create("Bond Slash Test", "Desc", "Src", TEST_DUEL_KEY, false, DURATION, 100 * 10**18);
address marketAddr = _marketAt(0);
(bool success, ) = marketAddr.call(abi.encodeWithSignature("adminResolve(uint256)", 1));
assertFalse(success, "adminResolve should not remain callable");
Expand All @@ -309,7 +324,7 @@ contract LvrMarketTest is Test {
_setupOracleDuel(duelKey);

vm.prank(admin);
router.create("Bond Return Test", "Desc", "Src", duelKey, true, DURATION, 100 * 10**18);
router.create("Bond Return Test", "Desc", "Src", duelKey, false, DURATION, 100 * 10**18);
address marketAddr = _lastMarketAddress();
LvrMarket market = LvrMarket(marketAddr);

Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-avax/keeper/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-avax/keeper/src/idl/lvr_amm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ export type LvrAmm = {
"code": 6028,
"name": "mathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "dynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-bsc/app/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-bsc/app/src/idl/lvr_amm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ export type LvrAmm = {
"code": 6028,
"name": "mathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "dynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-bsc/keeper/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-bsc/keeper/src/idl/lvr_amm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ export type LvrAmm = {
"code": 6028,
"name": "mathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "dynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-evm/keeper/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-evm/keeper/src/idl/lvr_amm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ export type LvrAmm = {
"code": 6028,
"name": "mathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "dynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
3 changes: 3 additions & 0 deletions packages/hyperbet-solana/anchor/programs/lvr_amm/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,7 @@ pub enum PredictionMarketError {

#[msg("AMM fixed-point overflow")]
MathFixedPointOverflow,

#[msg("Dynamic AMM markets are disabled")]
DynamicMarketsDisabled,
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub fn create_bet(
) -> Result<()> {
let config = &ctx.accounts.amm_config;
require!(!config.paused, PredictionMarketError::MarketPaused);
require!(!is_dynamic, PredictionMarketError::DynamicMarketsDisabled);

require!(
initial_liq > 0,
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-solana/anchor/target/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-solana/anchor/target/types/lvr_amm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ export type LvrAmm = {
"code": 6028,
"name": "mathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "dynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
2 changes: 1 addition & 1 deletion packages/hyperbet-solana/anchor/tests/amm-test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ export async function initializeCanonicalMarket(
const ammConfig = deriveAmmConfigPda(program.programId);

const initialLiq = new BN(LAMPORTS_PER_SOL * 5); // 5 SOL initial liquidity
const isDynamic = true;
const isDynamic = false;
const description = "Test AMM Open Market";
const expirationAt = new BN(Date.now() / 1000 + 3600);

Expand Down
15 changes: 15 additions & 0 deletions packages/hyperbet-solana/anchor/tests/lvr_amm_security.anchor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,21 @@ async function createBetFixture(options?: {
}

describe("lvr_amm security", () => {
it("rejects dynamic market creation while dynamic liquidity is disabled", async () => {
try {
await createBetFixture({
isDynamic: true,
expirationOffsetSecs: 300,
});
assert.fail("create_bet_account accepted a dynamic market");
} catch (error: unknown) {
assert.ok(
hasProgramError(error, "DynamicMarketsDisabled"),
`expected DynamicMarketsDisabled, got ${String(error)}`,
);
}
});

it("rejects settlement with a duel PDA derived from a different duel key", async () => {
const fixture = await createBetFixture({ expirationOffsetSecs: -60 });
const now = Math.floor(Date.now() / 1000);
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-solana/app/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-solana/app/src/idl/lvr_amm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ export type LvrAmm = {
"code": 6028,
"name": "mathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "dynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-solana/keeper/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-solana/keeper/src/idl/lvr_amm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,11 @@ export type LvrAmm = {
"code": 6028,
"name": "mathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "dynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/hyperbet-ui/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down
5 changes: 5 additions & 0 deletions packages/market-maker-bot/src/idl/lvr_amm.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,11 @@
"code": 6028,
"name": "MathFixedPointOverflow",
"msg": "AMM fixed-point overflow"
},
{
"code": 6029,
"name": "DynamicMarketsDisabled",
"msg": "Dynamic AMM markets are disabled"
}
],
"types": [
Expand Down