-
Notifications
You must be signed in to change notification settings - Fork 1
/
Funding.sol
186 lines (166 loc) · 6.63 KB
/
Funding.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
Copyright 2022 JOJO Exchange
SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "../interfaces/internal/IPriceSource.sol";
import "../interfaces/IPerpetual.sol";
import "../libraries/Errors.sol";
import "../libraries/SignedDecimalMath.sol";
import "./Liquidation.sol";
import "./Operation.sol";
import "./Types.sol";
library Funding {
using SafeERC20 for IERC20;
// ========== events ==========
event Deposit(address indexed to, address indexed payer, uint256 primaryAmount, uint256 secondaryAmount);
event Withdraw(address indexed to, address indexed payer, uint256 primaryAmount, uint256 secondaryAmount);
event RequestWithdraw(
address indexed payer, uint256 primaryAmount, uint256 secondaryAmount, uint256 executionTimestamp
);
event TransferIn(address trader, uint256 primaryAmount, uint256 secondaryAmount);
event TransferOut(address trader, uint256 primaryAmount, uint256 secondaryAmount);
// ========== deposit ==========
function deposit(Types.State storage state, uint256 primaryAmount, uint256 secondaryAmount, address to) external {
if (primaryAmount > 0) {
IERC20(state.primaryAsset).safeTransferFrom(msg.sender, address(this), primaryAmount);
state.primaryCredit[to] += SafeCast.toInt256(primaryAmount);
}
if (secondaryAmount > 0) {
IERC20(state.secondaryAsset).safeTransferFrom(msg.sender, address(this), secondaryAmount);
state.secondaryCredit[to] += secondaryAmount;
}
emit Deposit(to, msg.sender, primaryAmount, secondaryAmount);
}
// ========== withdraw ==========
function isWithdrawValid(
Types.State storage state,
address spender,
address from,
uint256 primaryAmount,
uint256 secondaryAmount
)
internal
view
returns (bool)
{
return spender == from
|| (
state.primaryCreditAllowed[from][spender] >= primaryAmount
&& state.secondaryCreditAllowed[from][spender] >= secondaryAmount
);
}
function requestWithdraw(
Types.State storage state,
address from,
uint256 primaryAmount,
uint256 secondaryAmount
)
external
{
require(isWithdrawValid(state, msg.sender, from, primaryAmount, secondaryAmount), Errors.WITHDRAW_INVALID);
state.pendingPrimaryWithdraw[msg.sender] = primaryAmount;
state.pendingSecondaryWithdraw[msg.sender] = secondaryAmount;
state.withdrawExecutionTimestamp[msg.sender] = block.timestamp + state.withdrawTimeLock;
emit RequestWithdraw(msg.sender, primaryAmount, secondaryAmount, state.withdrawExecutionTimestamp[msg.sender]);
}
function executeWithdraw(
Types.State storage state,
address from,
address to,
bool isInternal,
bytes memory param
)
external
{
require(state.withdrawExecutionTimestamp[from] <= block.timestamp, Errors.WITHDRAW_PENDING);
uint256 primaryAmount = state.pendingPrimaryWithdraw[from];
uint256 secondaryAmount = state.pendingSecondaryWithdraw[from];
require(isWithdrawValid(state, msg.sender, from, primaryAmount, secondaryAmount), Errors.WITHDRAW_INVALID);
state.pendingPrimaryWithdraw[from] = 0;
state.pendingSecondaryWithdraw[from] = 0;
// No need to change withdrawExecutionTimestamp, because we set pending
// withdraw amount to 0.
_withdraw(state, msg.sender, from, to, primaryAmount, secondaryAmount, isInternal, param);
}
function fastWithdraw(
Types.State storage state,
address from,
address to,
uint256 primaryAmount,
uint256 secondaryAmount,
bool isInternal,
bytes memory param
)
external
{
require(
!state.fastWithdrawDisabled || state.fastWithdrawalWhitelist[msg.sender], Errors.FAST_WITHDRAW_NOT_ALLOWED
);
require(isWithdrawValid(state, msg.sender, from, primaryAmount, secondaryAmount), Errors.WITHDRAW_INVALID);
_withdraw(state, msg.sender, from, to, primaryAmount, secondaryAmount, isInternal, param);
}
function _withdraw(
Types.State storage state,
address spender,
address from,
address to,
uint256 primaryAmount,
uint256 secondaryAmount,
bool isInternal,
bytes memory param
)
private
{
if (spender != from) {
state.primaryCreditAllowed[from][spender] -= primaryAmount;
state.secondaryCreditAllowed[from][spender] -= secondaryAmount;
emit Operation.FundOperatorAllowedChange(
from, spender, state.primaryCreditAllowed[from][spender], state.secondaryCreditAllowed[from][spender]
);
}
if (primaryAmount > 0) {
state.primaryCredit[from] -= SafeCast.toInt256(primaryAmount);
if (isInternal) {
state.primaryCredit[to] += SafeCast.toInt256(primaryAmount);
} else {
IERC20(state.primaryAsset).safeTransfer(to, primaryAmount);
}
}
if (secondaryAmount > 0) {
state.secondaryCredit[from] -= secondaryAmount;
if (isInternal) {
state.secondaryCredit[to] += secondaryAmount;
} else {
IERC20(state.secondaryAsset).safeTransfer(to, secondaryAmount);
}
}
if (primaryAmount > 0) {
// if trader withdraw primary asset, we should check if solid safe
require(Liquidation._isSolidIMSafe(state, from), Errors.ACCOUNT_NOT_SAFE);
} else {
// if trader didn't withdraw primary asset, normal safe check is enough
require(Liquidation._isIMSafe(state, from), Errors.ACCOUNT_NOT_SAFE);
}
if (isInternal) {
emit TransferIn(to, primaryAmount, secondaryAmount);
emit TransferOut(from, primaryAmount, secondaryAmount);
} else {
emit Withdraw(to, from, primaryAmount, secondaryAmount);
}
if (param.length != 0) {
require(Address.isContract(to), "target is not a contract");
(bool success,) = to.call(param);
if (success == false) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
}
}
}