Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cancellation cost calculation for keepers #2348

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
11 changes: 11 additions & 0 deletions auxiliary/ArbitrumGasPriceOracle/contracts/ArbGasPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ contract ArbGasPriceOracle is IExternalNode {
*/
uint256 public constant KIND_LIQUIDATE = 2;

/**
* @notice identifies resources consumed via order cancellation
*/
uint256 public constant KIND_CANCEL = 3;

/**
* @notice the ArbGasInfo precompile contract on Arbitrum
*/
Expand All @@ -46,6 +51,9 @@ contract ArbGasPriceOracle is IExternalNode {
// Call params
uint256 numberOfUpdatedFeeds;
uint256 executionKind;
// Cancel
uint256 l1CancelGasUnits;
uint256 l2CancelGasUnits;
}

/**
Expand Down Expand Up @@ -223,6 +231,9 @@ contract ArbGasPriceOracle is IExternalNode {
// Iterations is fixed to 1 for liquidations
gasUnitsL1 = runtimeParams.l1LiquidateGasUnits;
gasUnitsL2 = runtimeParams.l2LiquidateGasUnits;
} else if (runtimeParams.executionKind == KIND_CANCEL) {
gasUnitsL1 = runtimeParams.l1CancelGasUnits;
gasUnitsL2 = runtimeParams.l2CancelGasUnits;
} else {
revert ArbGasPriceOracleInvalidExecutionKind();
}
Expand Down
14 changes: 14 additions & 0 deletions auxiliary/ArbitrumGasPriceOracle/storage.dump.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@
"size": 32,
"slot": "7",
"offset": 0
},
{
"type": "uint256",
"name": "l1CancelGasUnits",
"size": 32,
"slot": "8",
"offset": 0
},
{
"type": "uint256",
"name": "l2CancelGasUnits",
"size": 32,
"slot": "9",
"offset": 0
}
]
}
Expand Down
15 changes: 14 additions & 1 deletion auxiliary/OpGasPriceOracle/contracts/OpGasPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract OpGasPriceOracle is IExternalNode {
uint256 public constant KIND_SETTLEMENT = 0;
uint256 public constant KIND_FLAG = 1;
uint256 public constant KIND_LIQUIDATE = 2;
uint256 public constant KIND_CANCEL = 3;

struct RuntimeParams {
// Set up params
Expand All @@ -41,8 +42,16 @@ contract OpGasPriceOracle is IExternalNode {
// Call params
uint256 numberOfUpdatedFeeds;
uint256 executionKind;
// Cancel
uint256 l1CancelGasUnits;
uint256 l2CancelGasUnits;
}

/**
* @notice thrown when the execution kind is invalid
*/
error OpGasPriceOracleInvalidExecutionKind();

constructor(address _ovmGasPriceOracleAddress) {
// Addresses configuration
ovmGasPriceOracleAddress = _ovmGasPriceOracleAddress;
Expand Down Expand Up @@ -212,8 +221,12 @@ contract OpGasPriceOracle is IExternalNode {
gasUnitsL1 = runtimeParams.l1LiquidateGasUnits;
gasUnitsL2 = runtimeParams.l2LiquidateGasUnits;
unsignedTxSize = runtimeParams.liquidateTxSize;
} else if (runtimeParams.executionKind == KIND_CANCEL) {
gasUnitsL1 = runtimeParams.l1CancelGasUnits;
gasUnitsL2 = runtimeParams.l2CancelGasUnits;
unsignedTxSize = 0;
} else {
revert("Invalid execution kind");
revert OpGasPriceOracleInvalidExecutionKind();
}
}

Expand Down
14 changes: 14 additions & 0 deletions auxiliary/OpGasPriceOracle/storage.dump.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@
"size": 32,
"slot": "10",
"offset": 0
},
{
"type": "uint256",
"name": "l1CancelGasUnits",
"size": 32,
"slot": "11",
"offset": 0
},
{
"type": "uint256",
"name": "l2CancelGasUnits",
"size": 32,
"slot": "12",
"offset": 0
}
]
}
Expand Down
16 changes: 13 additions & 3 deletions markets/perps-market/contracts/mocks/MockGasPriceNode.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,25 @@ contract MockGasPriceNode is IExternalNode {
uint256 public constant KIND_SETTLEMENT = 0;
uint256 public constant KIND_FLAG = 1;
uint256 public constant KIND_LIQUIDATE = 2;
uint256 public constant KIND_CANCEL = 3;

uint256 public settlementCost;
uint256 public flagCost;
uint256 public liquidateCost;
uint256 public cancelCost;

constructor() {}
error InvalidExecutionKind();

function setCosts(uint256 _settlementCost, uint256 _flagCost, uint256 _liquidateCost) external {
function setCosts(
uint256 _settlementCost,
uint256 _flagCost,
uint256 _liquidateCost,
uint256 _cancelCost
) external {
settlementCost = _settlementCost;
flagCost = _flagCost;
liquidateCost = _liquidateCost;
cancelCost = _cancelCost;
}

// solhint-disable numcast/safe-cast
Expand Down Expand Up @@ -49,8 +57,10 @@ contract MockGasPriceNode is IExternalNode {
theOutput.price = int256(flagCost * numberOfUpdatedFeeds);
} else if (executionKind == KIND_LIQUIDATE) {
theOutput.price = int256(liquidateCost);
} else if (executionKind == KIND_CANCEL) {
theOutput.price = int256(cancelCost);
} else {
revert("Invalid execution kind");
revert InvalidExecutionKind();
}

return theOutput;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ contract AsyncOrderCancelModule is IAsyncOrderCancelModule, IMarketEvents, IAcco
runtime.accountId = asyncOrder.request.accountId;
runtime.marketId = asyncOrder.request.marketId;
runtime.acceptablePrice = asyncOrder.request.acceptablePrice;
runtime.settlementReward = settlementStrategy.settlementReward;
runtime.settlementReward = AsyncOrder.cancellationRewardCost(settlementStrategy);
runtime.sizeDelta = asyncOrder.request.sizeDelta;

// check if account is flagged
Expand Down
9 changes: 9 additions & 0 deletions markets/perps-market/contracts/storage/AsyncOrder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,15 @@ library AsyncOrder {
return KeeperCosts.load().getSettlementKeeperCosts() + strategy.settlementReward;
}

/**
* @notice Calculates the cancellation rewards.
*/
function cancellationRewardCost(
SettlementStrategy.Data storage strategy
) internal view returns (uint256) {
return KeeperCosts.load().getCancellationKeeperCosts() + strategy.settlementReward;
}

/**
* @notice Calculates the order fees.
*/
Expand Down
9 changes: 9 additions & 0 deletions markets/perps-market/contracts/storage/KeeperCosts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ library KeeperCosts {
uint256 private constant KIND_SETTLEMENT = 0;
uint256 private constant KIND_FLAG = 1;
uint256 private constant KIND_LIQUIDATE = 2;
uint256 private constant KIND_CANCEL = 3;

struct Data {
bytes32 keeperCostNodeId;
Expand All @@ -44,6 +45,14 @@ library KeeperCosts {
sUSDCost = _processWithRuntime(self.keeperCostNodeId, factory, 0, KIND_SETTLEMENT);
}

function getCancellationKeeperCosts(
Data storage self
) internal view returns (uint256 sUSDCost) {
PerpsMarketFactory.Data storage factory = PerpsMarketFactory.load();

sUSDCost = _processWithRuntime(self.keeperCostNodeId, factory, 0, KIND_CANCEL);
}

function getFlagKeeperCosts(
Data storage self,
uint128 accountId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Keeper Rewards - Caps', () => {
settlementCost: 1111,
flagCost: 3333,
liquidateCost: 5555,
cancelCost: 2222,
};
const { systems, perpsMarkets, provider, trader1, keeperCostOracleNode, keeper, owner } =
bootstrapMarkets({
Expand Down Expand Up @@ -66,7 +67,12 @@ describe('Keeper Rewards - Caps', () => {
before('set keeper costs', async () => {
await keeperCostOracleNode()
.connect(owner())
.setCosts(KeeperCosts.settlementCost, KeeperCosts.flagCost, KeeperCosts.liquidateCost);
.setCosts(
KeeperCosts.settlementCost,
KeeperCosts.flagCost,
KeeperCosts.liquidateCost,
KeeperCosts.cancelCost
);
});

const restoreToConfiguration = snapshotCheckpoint(provider);
Expand All @@ -76,6 +82,7 @@ describe('Keeper Rewards - Caps', () => {
assertBn.equal(await keeperCostOracleNode().settlementCost(), KeeperCosts.settlementCost);
assertBn.equal(await keeperCostOracleNode().flagCost(), KeeperCosts.flagCost);
assertBn.equal(await keeperCostOracleNode().liquidateCost(), KeeperCosts.liquidateCost);
assertBn.equal(await keeperCostOracleNode().cancelCost(), KeeperCosts.cancelCost);
});

const capTestCases = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Keeper Rewards - Multiple Liquidation steps', () => {
settlementCost: 1111,
flagCost: 3333,
liquidateCost: 5555,
cancelCost: 2222,
};
const { systems, perpsMarkets, provider, trader1, keeperCostOracleNode, keeper, owner } =
bootstrapMarkets({
Expand Down Expand Up @@ -64,7 +65,12 @@ describe('Keeper Rewards - Multiple Liquidation steps', () => {
before('set keeper costs', async () => {
await keeperCostOracleNode()
.connect(owner())
.setCosts(KeeperCosts.settlementCost, KeeperCosts.flagCost, KeeperCosts.liquidateCost);
.setCosts(
KeeperCosts.settlementCost,
KeeperCosts.flagCost,
KeeperCosts.liquidateCost,
KeeperCosts.cancelCost
);
});

const rewardGuards = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Keeper Rewards - Multiple Collaterals', () => {
settlementCost: 1111,
flagCost: 3333,
liquidateCost: 5555,
cancelCost: 2222,
};
const {
systems,
Expand Down Expand Up @@ -93,7 +94,12 @@ describe('Keeper Rewards - Multiple Collaterals', () => {
before('set keeper costs', async () => {
await keeperCostOracleNode()
.connect(owner())
.setCosts(KeeperCosts.settlementCost, KeeperCosts.flagCost, KeeperCosts.liquidateCost);
.setCosts(
KeeperCosts.settlementCost,
KeeperCosts.flagCost,
KeeperCosts.liquidateCost,
KeeperCosts.cancelCost
);
});

before('set minLiquidationRewardUsd, maxLiquidationRewardUsd - uncapped', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('Keeper Rewards - Multiple Positions', () => {
settlementCost: 1111,
flagCost: 3333,
liquidateCost: 5555,
cancelCost: 2222,
};
const { systems, perpsMarkets, provider, trader1, keeperCostOracleNode, keeper, owner } =
bootstrapMarkets({
Expand Down Expand Up @@ -77,7 +78,12 @@ describe('Keeper Rewards - Multiple Positions', () => {
before('set keeper costs', async () => {
await keeperCostOracleNode()
.connect(owner())
.setCosts(KeeperCosts.settlementCost, KeeperCosts.flagCost, KeeperCosts.liquidateCost);
.setCosts(
KeeperCosts.settlementCost,
KeeperCosts.flagCost,
KeeperCosts.liquidateCost,
KeeperCosts.cancelCost
);
});

before('set minLiquidationRewardUsd, maxLiquidationRewardUsd - uncapped', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('Keeper Rewards - Settlement', () => {
settlementCost: 1111,
flagCost: 3333,
liquidateCost: 5555,
cancelCost: 2222,
};
const { systems, perpsMarkets, provider, trader1, keeperCostOracleNode, keeper, owner } =
bootstrapMarkets({
Expand Down Expand Up @@ -72,7 +73,12 @@ describe('Keeper Rewards - Settlement', () => {
before('set keeper costs', async () => {
await keeperCostOracleNode()
.connect(owner())
.setCosts(KeeperCosts.settlementCost, KeeperCosts.flagCost, KeeperCosts.liquidateCost);
.setCosts(
KeeperCosts.settlementCost,
KeeperCosts.flagCost,
KeeperCosts.liquidateCost,
KeeperCosts.cancelCost
);
});

before('add collateral', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const KeeperCosts = {
settlementCost: bn(10),
flagCost: bn(20),
liquidateCost: bn(15),
cancelCost: bn(5),
};

const MIN_LIQ_REWARD = bn(10);
Expand Down Expand Up @@ -116,7 +117,12 @@ describe('liquidation margin only', () => {
before('set keeper costs', async () => {
await keeperCostOracleNode()
.connect(owner())
.setCosts(KeeperCosts.settlementCost, KeeperCosts.flagCost, KeeperCosts.liquidateCost);
.setCosts(
KeeperCosts.settlementCost,
KeeperCosts.flagCost,
KeeperCosts.liquidateCost,
KeeperCosts.cancelCost
);
});

before('add collateral to margin', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const createKeeperCostNode = async (owner: ethers.Signer, OracleManager:
const factory = await hre.ethers.getContractFactory('MockGasPriceNode');
const keeperCostNode = await factory.connect(owner).deploy();

await keeperCostNode.setCosts(0, 0, 0);
await keeperCostNode.setCosts(0, 0, 0, 0);

const params1 = abi.encode(['address'], [keeperCostNode.address]);
await OracleManager.connect(owner).registerNode(NodeTypes.EXTERNAL, params1, []);
Expand Down