Skip to content

Commit 0d53aa8

Browse files
authored
chore: cleanup (#10)
* chore: cleanup * chore: add tests * chore: coverage for process manager data * docs: readme
1 parent 433de3d commit 0d53aa8

File tree

9 files changed

+115
-25
lines changed

9 files changed

+115
-25
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Lyra Matching Contracts
2+
3+
[![codecov](https://codecov.io/gh/lyra-finance/v2-matching/branch/master/graph/badge.svg?token=ZhHkpl1UGB)](https://codecov.io/gh/lyra-finance/v2-matching)
4+
5+
This repository contains a set of smart contracts designed to enable sequential matching by our back-end on [v2 protocol](https://github.com/lyra-finance/v2-core). The contracts facilitate the execution of transactions based on a trusted keeper (`trade-executor`), while giving user full custody of funds.
6+
7+
8+
## Main components:
9+
10+
`Matching`: Process signed orders and additional "actionData" from the orderbook, ensuring whitelisted modules can execute actions within specified rules. Inherits `OrderVerifier` and `SubAccountManager`
11+
12+
Other Modules: contract that take ownership of user's subAccounts from the matching contract, and then execute accordingly base on what users signed.
13+
14+
For detailed information about the contracts, modules, and installation instructions, please refer to [Notion](https://www.notion.so/lyra-finance/Matching-59db600914334665ba7179c1f03ac6c2).
15+
16+
## Building and Testing:
17+
18+
```shell
19+
forge build
20+
```
21+
22+
Run tests
23+
24+
```shell
25+
forge test
26+
```
27+

src/Matching.sol

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity ^0.8.13;
33

4-
import "forge-std/console2.sol";
5-
64
// inherited
75
import {OrderVerifier} from "./OrderVerifier.sol";
86
import {IMatching} from "./interfaces/IMatching.sol";
@@ -23,7 +21,7 @@ contract Matching is IMatching, OrderVerifier {
2321
constructor(ISubAccounts _accounts) OrderVerifier(_accounts) {}
2422

2523
////////////////////////////
26-
// Onwer-only Functions //
24+
// Owner-only Functions //
2725
////////////////////////////
2826

2927
/**
@@ -47,14 +45,13 @@ contract Matching is IMatching, OrderVerifier {
4745

4846
function verifyAndMatch(SignedOrder[] memory orders, bytes memory actionData) public onlyTradeExecutor {
4947
IMatchingModule module = orders[0].module;
50-
_verifyModule(module);
48+
49+
if (!allowedModules[address(module)]) revert M_OnlyAllowedModule();
5150

5251
IMatchingModule.VerifiedOrder[] memory verifiedOrders = new IMatchingModule.VerifiedOrder[](orders.length);
5352
for (uint i = 0; i < orders.length; i++) {
5453
verifiedOrders[i] = _verifyOrder(orders[i]);
55-
if (orders[i].module != module) {
56-
revert M_MismatchedModule();
57-
}
54+
if (orders[i].module != module) revert M_MismatchedModule();
5855
}
5956
_submitModuleAction(module, verifiedOrders, actionData);
6057
}
@@ -63,6 +60,10 @@ contract Matching is IMatching, OrderVerifier {
6360
// Internal Functions //
6461
//////////////////////////
6562

63+
/**
64+
* @notice sent array of signed actions to the module contract
65+
* @dev expect the module to transfer the ownership back to Matching.sol at the end
66+
*/
6667
function _submitModuleAction(
6768
IMatchingModule module,
6869
IMatchingModule.VerifiedOrder[] memory orders,
@@ -98,12 +99,6 @@ contract Matching is IMatching, OrderVerifier {
9899
}
99100
}
100101

101-
function _verifyModule(IMatchingModule module) internal view {
102-
if (!allowedModules[address(module)]) {
103-
revert M_OnlyAllowedModule();
104-
}
105-
}
106-
107102
///////////////
108103
// Modifiers //
109104
///////////////

src/modules/BaseModule.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {IBaseModule} from "../interfaces/IBaseModule.sol";
66

77
// Interfaces
88
import {ISubAccounts} from "v2-core/src/interfaces/ISubAccounts.sol";
9-
import {IMatchingModule} from "../interfaces/IMatchingModule.sol";
109
import {IMatching} from "../interfaces/IMatching.sol";
1110

1211
abstract contract BaseModule is IBaseModule {

src/modules/DepositModule.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity ^0.8.13;
33

4-
import "forge-std/console2.sol";
5-
64
// Inherited
75
import {BaseModule} from "./BaseModule.sol";
86
import {IDepositModule} from "../interfaces/IDepositModule.sol";

src/modules/TradeModule.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ import {ITradeModule} from "../interfaces/ITradeModule.sol";
1616
// Interfaces
1717
import {IBaseManager} from "v2-core/src/interfaces/IBaseManager.sol";
1818
import {IDataReceiver} from "v2-core/src/interfaces/IDataReceiver.sol";
19-
import {IMatchingModule} from "../interfaces/IMatchingModule.sol";
2019
import {ISubAccounts} from "v2-core/src/interfaces/ISubAccounts.sol";
2120
import {IAsset} from "v2-core/src/interfaces/IAsset.sol";
2221
import {IPerpAsset} from "v2-core/src/interfaces/IPerpAsset.sol";
23-
import {SignedMath} from "openzeppelin/utils/math/SignedMath.sol";
2422
import {IMatching} from "../interfaces/IMatching.sol";
2523

2624
contract TradeModule is ITradeModule, BaseModule, Ownable2Step {
@@ -52,10 +50,16 @@ contract TradeModule is ITradeModule, BaseModule, Ownable2Step {
5250
// Admin //
5351
///////////
5452

53+
/**
54+
* @dev set fee recipient account
55+
*/
5556
function setFeeRecipient(uint _feeRecipient) external onlyOwner {
5657
feeRecipient = _feeRecipient;
5758
}
5859

60+
/**
61+
* @dev set perp asset mapping
62+
*/
5963
function setPerpAsset(IPerpAsset _perpAsset, bool isPerp) external onlyOwner {
6064
isPerpAsset[_perpAsset] = isPerp;
6165
}
@@ -220,6 +224,9 @@ contract TradeModule is ITradeModule, BaseModule, Ownable2Step {
220224
});
221225
}
222226

227+
/**
228+
* @dev send data to IDataReceiver contracts. Can be used to update oracles before pairing trades
229+
*/
223230
function _processManagerData(bytes memory managerData) internal {
224231
if (managerData.length == 0) return;
225232
IBaseManager.ManagerData[] memory managerDatas = abi.decode(managerData, (IBaseManager.ManagerData[]));

src/modules/TransferModule.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
pragma solidity ^0.8.13;
33

44
// Inherited
5-
import {Ownable2Step} from "openzeppelin/access/Ownable2Step.sol";
65
import {BaseModule} from "./BaseModule.sol";
76
import {ITransferModule} from "../interfaces/ITransferModule.sol";
87

98
// Interfaces
109
import {ISubAccounts} from "v2-core/src/interfaces/ISubAccounts.sol";
1110
import {IManager} from "v2-core/src/interfaces/IManager.sol";
1211
import {IAsset} from "v2-core/src/interfaces/IAsset.sol";
13-
import {IMatchingModule} from "../interfaces/IMatchingModule.sol";
1412
import {IMatching} from "../interfaces/IMatching.sol";
1513

1614
// Handles transferring assets from one subaccount to another

test/MatchingBasic.t.sol

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,15 @@ contract MatchingBasicTest is MatchingBase {
4040
vm.expectRevert(IMatching.M_AccountNotReturned.selector);
4141
_verifyAndMatch(orders, "");
4242
}
43+
44+
function testCannotExecuteActionsForDifferentModules() public {
45+
BadModule badModule = new BadModule();
46+
47+
IOrderVerifier.SignedOrder[] memory orders = new IOrderVerifier.SignedOrder[](2);
48+
orders[0] = _createFullSignedOrder(camAcc, 0, address(depositModule), "", block.timestamp + 1 days, cam, cam, camPk);
49+
orders[1] = _createFullSignedOrder(camAcc, 0, address(badModule), "", block.timestamp + 1 days, cam, cam, camPk);
50+
51+
vm.expectRevert(IMatching.M_MismatchedModule.selector);
52+
_verifyAndMatch(orders, "");
53+
}
4354
}

test/mock/MockDataReceiver.sol

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.13;
3+
4+
import {IDataReceiver} from "v2-core/src/interfaces/IDataReceiver.sol";
5+
import {MockFeeds} from "v2-core/test/shared/mocks/MockFeeds.sol";
6+
7+
contract MockDataReceiver is IDataReceiver {
8+
// update mocked feed
9+
function acceptData(bytes memory data) external {
10+
(address mockedFeed, uint price) = abi.decode(data, (address, uint));
11+
MockFeeds(mockedFeed).setSpot(price, 1e18);
12+
}
13+
}

test/modules/Trade.t.sol

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
// SPDX-License-Identifier: UNLICENSED
22
pragma solidity ^0.8.18;
33

4-
import "forge-std/Test.sol";
5-
64
import {MatchingBase} from "test/shared/MatchingBase.t.sol";
75
import {IOrderVerifier} from "src/interfaces/IOrderVerifier.sol";
8-
import {IERC20BasedAsset} from "v2-core/src/interfaces/IERC20BasedAsset.sol";
9-
import {IManager} from "v2-core/src/interfaces/IManager.sol";
10-
import {IERC20Metadata} from "openzeppelin/token/ERC20/extensions/IERC20Metadata.sol";
116
import {TradeModule, ITradeModule} from "src/modules/TradeModule.sol";
7+
import {IPerpAsset} from "v2-core/src/interfaces/IPerpAsset.sol";
8+
import {IBaseManager} from "v2-core/src/interfaces/IBaseManager.sol";
9+
import {MockDataReceiver} from "../mock/MockDataReceiver.sol";
1210

1311
contract TradeModuleTest is MatchingBase {
1412
// Test trading
@@ -18,6 +16,21 @@ contract TradeModuleTest is MatchingBase {
1816
// - mismatch of signed orders and trade data
1917
// - cannot trade if the order is expired
2018

19+
function testSetFeeRecipient() public {
20+
uint newAcc = subAccounts.createAccount(cam, pmrm);
21+
tradeModule.setFeeRecipient(newAcc);
22+
assertEq(tradeModule.feeRecipient(), newAcc);
23+
}
24+
25+
function testSetIsPerp() public {
26+
tradeModule.setPerpAsset(IPerpAsset(address(this)), true);
27+
assertEq(tradeModule.isPerpAsset(IPerpAsset(address(this))), true);
28+
}
29+
30+
////////////////////////
31+
// Test Trades //
32+
////////////////////////
33+
2134
function testTrade() public {
2235
IOrderVerifier.SignedOrder[] memory orders = _getDefaultOrders();
2336
_verifyAndMatch(orders, _createMatchedTrade(camAcc, dougAcc, 1e18, 78e18, 0, 0));
@@ -327,6 +340,35 @@ contract TradeModuleTest is MatchingBase {
327340
assertEq(subAccounts.getBalance(dougAcc, mockPerp, 0), -1e18);
328341
}
329342

343+
function testCanUpdateSpotAndThenTrade() public {
344+
MockDataReceiver mockedUpdater = new MockDataReceiver();
345+
uint newPrice = 2000;
346+
347+
// use the default orders
348+
IOrderVerifier.SignedOrder[] memory orders = _getDefaultOrders();
349+
350+
// fill in default fill details
351+
ITradeModule.FillDetails[] memory fills = new ITradeModule.FillDetails[](1);
352+
fills[0] = ITradeModule.FillDetails({filledAccount: dougAcc, amountFilled: 1e18, price: 78e18, fee: 0});
353+
354+
// the data that should be processed before trade
355+
IBaseManager.ManagerData[] memory managerDatas = new IBaseManager.ManagerData[](1);
356+
bytes memory receiverData = abi.encode(address(feed), newPrice);
357+
managerDatas[0] = IBaseManager.ManagerData(address(mockedUpdater), receiverData);
358+
bytes memory finalManagerData = abi.encode(managerDatas);
359+
360+
ITradeModule.ActionData memory actionData =
361+
ITradeModule.ActionData({takerAccount: camAcc, takerFee: 0, fillDetails: fills, managerData: finalManagerData});
362+
363+
bytes memory matchData = abi.encode(actionData);
364+
365+
_verifyAndMatch(orders, matchData);
366+
367+
(uint newSpot,) = feed.getSpot();
368+
assertEq(newSpot, newPrice);
369+
}
370+
371+
/// @dev return order of 2: [0: cam (taker)], [1: doug (maker)]
330372
function _getDefaultOrders() internal returns (IOrderVerifier.SignedOrder[] memory) {
331373
IOrderVerifier.SignedOrder[] memory orders = new IOrderVerifier.SignedOrder[](2);
332374

0 commit comments

Comments
 (0)