-
Notifications
You must be signed in to change notification settings - Fork 1
/
AbstractRouter.sol
155 lines (133 loc) 路 7.49 KB
/
AbstractRouter.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {AccessControlled} from "@mgv-strats/src/strategies/utils/AccessControlled.sol";
import {IERC20} from "@mgv/lib/IERC20.sol";
/// @title AbstractRouter
/// @notice Partial implementation and requirements for liquidity routers.
abstract contract AbstractRouter is AccessControlled(msg.sender) {
///@notice the bound maker contracts which are allowed to call this router.
mapping(address => bool) internal boundMakerContracts;
///@notice This modifier verifies that `msg.sender` an allowed caller of this router.
modifier onlyBound() {
require(isBound(msg.sender), "AccessControlled/Invalid");
_;
}
///@notice This modifier verifies that `msg.sender` is the admin or an allowed caller of this router.
modifier boundOrAdmin() {
require(msg.sender == admin() || isBound(msg.sender), "AccessControlled/Invalid");
_;
}
///@notice logging bound maker contract
///@param maker the maker address. This is indexed, so that RPC calls can filter on it.
///@notice by emitting this data, an indexer will be able to keep track of what maker contracts are allowed to call this router.
event MakerBind(address indexed maker);
///@notice logging unbound maker contract
///@param maker the maker address. This is indexed, so that RPC calls can filter on it.
///@notice by emitting this data, an indexer will be able to keep track of what maker contracts are allowed to call this router.
event MakerUnbind(address indexed maker);
///@notice getter for the `makers: addr => bool` mapping
///@param mkr the address of a maker contract
///@return true if `mkr` is authorized to call this router.
function isBound(address mkr) public view returns (bool) {
return boundMakerContracts[mkr];
}
///@notice pulls liquidity from the reserve and sends it to the calling maker contract.
///@param token is the ERC20 managing the pulled asset
///@param reserveId identifies the fund owner (router implementation dependent).
///@param amount of `token` the maker contract wishes to pull from its reserve
///@param strict when the calling maker contract accepts to receive more funds from reserve than required (this may happen for gas optimization)
///@return pulled the amount that was successfully pulled.
function pull(IERC20 token, address reserveId, uint amount, bool strict) external onlyBound returns (uint pulled) {
if (strict && amount == 0) {
return 0;
}
pulled = __pull__({token: token, reserveId: reserveId, amount: amount, strict: strict});
}
///@notice router-dependent implementation of the `pull` function
///@param token Token to be transferred
///@param reserveId determines the location of the reserve (router implementation dependent).
///@param amount The amount of tokens to be transferred
///@param strict wether the caller maker contract wishes to pull at most `amount` tokens of owner.
///@return pulled The amount pulled if successful; otherwise, 0.
function __pull__(IERC20 token, address reserveId, uint amount, bool strict) internal virtual returns (uint);
///@notice pushes assets from calling's maker contract to a reserve
///@param token is the asset the maker is pushing
///@param reserveId determines the location of the reserve (router implementation dependent).
///@param amount is the amount of asset that should be transferred from the calling maker contract
///@return pushed fraction of `amount` that was successfully pushed to reserve.
function push(IERC20 token, address reserveId, uint amount) external onlyBound returns (uint pushed) {
if (amount == 0) {
return 0;
}
pushed = __push__({token: token, reserveId: reserveId, amount: amount});
}
///@notice router-dependent implementation of the `push` function
///@param token Token to be transferred
///@param reserveId determines the location of the reserve (router implementation dependent).
///@param amount The amount of tokens to be transferred
///@return pushed The amount pushed if successful; otherwise, 0.
function __push__(IERC20 token, address reserveId, uint amount) internal virtual returns (uint pushed);
///@notice iterative `push` for the whole balance in a single call
///@param tokens to flush
///@param reserveId determines the location of the reserve (router implementation dependent).
function flush(IERC20[] calldata tokens, address reserveId) external onlyBound {
for (uint i = 0; i < tokens.length; ++i) {
uint amount = tokens[i].balanceOf(msg.sender);
if (amount > 0) {
require(__push__(tokens[i], reserveId, amount) == amount, "router/pushFailed");
}
}
}
///@notice adds a maker contract address to the allowed makers of this router
///@dev this function is callable by router's admin to bootstrap, but later on an allowed maker contract can add another address
///@param makerContract the maker contract address
function bind(address makerContract) public onlyAdmin {
boundMakerContracts[makerContract] = true;
emit MakerBind(makerContract);
}
///@notice removes a maker contract address from the allowed makers of this router
///@param makerContract the maker contract address
function _unbind(address makerContract) internal {
boundMakerContracts[makerContract] = false;
emit MakerUnbind(makerContract);
}
///@notice removes `msg.sender` from the allowed makers of this router
function unbind() external onlyBound {
_unbind(msg.sender);
}
///@notice removes a makerContract from the allowed makers of this router
///@param makerContract the maker contract address
function unbind(address makerContract) external onlyAdmin {
_unbind(makerContract);
}
///@notice allows a makerContract to verify it is ready to use `this` router for a particular reserve
///@dev `checkList` returns normally if all needed approval are strictly positive. It reverts otherwise with a reason.
///@param token is the asset (and possibly its overlyings) whose approval must be checked
///@param reserveId of the tokens that are being pulled
function checkList(IERC20 token, address reserveId) external view {
require(isBound(msg.sender), "Router/callerIsNotBoundToRouter");
// checking maker contract has approved this for token transfer (in order to push to reserve)
require(token.allowance(msg.sender, address(this)) > 0, "Router/NotApprovedByMakerContract");
// pulling on behalf of `reserveId` might require a special approval (e.g if `reserveId` is some account on a protocol).
__checkList__(token, reserveId);
}
///@notice router-dependent additional checks
///@param token is the asset (and possibly its overlyings) whose approval must be checked
///@param reserveId of the tokens that are being pulled
function __checkList__(IERC20 token, address reserveId) internal view virtual;
///@notice performs necessary approval to activate router function on a particular asset
///@param token the asset one wishes to use the router for
function activate(IERC20 token) external boundOrAdmin {
__activate__(token);
}
///@notice router-dependent implementation of the `activate` function
///@param token the asset one wishes to use the router for
function __activate__(IERC20 token) internal virtual {
token; //ssh
}
///@notice Balance of a reserve
///@param token the asset one wishes to know the balance of
///@param reserveId the identifier of the reserve
///@return the balance of the reserve
function balanceOfReserve(IERC20 token, address reserveId) public view virtual returns (uint);
}