-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathLockProxyPausable.sol
198 lines (166 loc) · 9.72 KB
/
LockProxyPausable.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
187
188
189
190
191
192
193
194
195
196
197
198
pragma solidity ^0.5.0;
import "./../libs/ownership/Ownable.sol";
import "./../libs/common/ZeroCopySource.sol";
import "./../libs/common/ZeroCopySink.sol";
import "./../libs/utils/Utils.sol";
import "./../libs/lifecycle/Pausable.sol";
import "./../libs/token/ERC20/SafeERC20.sol";
import "./../interface/IEthCrossChainManager.sol";
import "./../interface/IEthCrossChainManagerProxy.sol";
contract LockProxyPausable is Ownable, Pausable {
using SafeMath for uint;
using SafeERC20 for IERC20;
struct TxArgs {
bytes toAssetHash;
bytes toAddress;
uint256 amount;
}
address public managerProxyContract;
mapping(uint64 => bytes) public proxyHashMap;
mapping(address => mapping(uint64 => bytes)) public assetHashMap;
mapping(address => bool) safeTransfer;
event SetManagerProxyEvent(address manager);
event BindProxyEvent(uint64 toChainId, bytes targetProxyHash);
event BindAssetEvent(address fromAssetHash, uint64 toChainId, bytes targetProxyHash, uint initialAmount);
event UnlockEvent(address toAssetHash, address toAddress, uint256 amount);
event LockEvent(address fromAssetHash, address fromAddress, uint64 toChainId, bytes toAssetHash, bytes toAddress, uint256 amount);
modifier onlyManagerContract() {
IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy(managerProxyContract);
require(_msgSender() == ieccmp.getEthCrossChainManager(), "msgSender is not EthCrossChainManagerContract");
_;
}
function setManagerProxy(address ethCCMProxyAddr) onlyOwner public {
managerProxyContract = ethCCMProxyAddr;
emit SetManagerProxyEvent(managerProxyContract);
}
function bindProxyHash(uint64 toChainId, bytes memory targetProxyHash) onlyOwner public returns (bool) {
proxyHashMap[toChainId] = targetProxyHash;
emit BindProxyEvent(toChainId, targetProxyHash);
return true;
}
function bindAssetHash(address fromAssetHash, uint64 toChainId, bytes memory toAssetHash) onlyOwner public returns (bool) {
assetHashMap[fromAssetHash][toChainId] = toAssetHash;
emit BindAssetEvent(fromAssetHash, toChainId, toAssetHash, getBalanceFor(fromAssetHash));
return true;
}
function pause() onlyOwner public {
_pause();
}
function unpause() onlyOwner public {
_unpause();
}
/* @notice This function is meant to be invoked by the user,
* a certin amount teokens will be locked in the proxy contract the invoker/msg.sender immediately.
* Then the same amount of tokens will be unloked from target chain proxy contract at the target chain with chainId later.
* @param fromAssetHash The asset address in current chain, uniformly named as `fromAssetHash`
* @param toChainId The target chain id
*
* @param toAddress The address in bytes format to receive same amount of tokens in target chain
* @param amount The amount of tokens to be crossed from ethereum to the chain with chainId
*/
function lock(address fromAssetHash, uint64 toChainId, bytes memory toAddress, uint256 amount) whenNotPaused public payable returns (bool) {
require(amount != 0, "amount cannot be zero!");
require(_transferToContract(fromAssetHash, amount), "transfer asset from fromAddress to lock_proxy contract failed!");
bytes memory toAssetHash = assetHashMap[fromAssetHash][toChainId];
require(toAssetHash.length != 0, "empty illegal toAssetHash");
TxArgs memory txArgs = TxArgs({
toAssetHash: toAssetHash,
toAddress: toAddress,
amount: amount
});
bytes memory txData = _serializeTxArgs(txArgs);
IEthCrossChainManagerProxy eccmp = IEthCrossChainManagerProxy(managerProxyContract);
address eccmAddr = eccmp.getEthCrossChainManager();
IEthCrossChainManager eccm = IEthCrossChainManager(eccmAddr);
bytes memory toProxyHash = proxyHashMap[toChainId];
require(toProxyHash.length != 0, "empty illegal toProxyHash");
require(eccm.crossChain(toChainId, toProxyHash, "unlock", txData), "EthCrossChainManager crossChain executed error!");
emit LockEvent(fromAssetHash, _msgSender(), toChainId, toAssetHash, toAddress, amount);
return true;
}
// /* @notice This function is meant to be invoked by the ETH crosschain management contract,
// * then mint a certin amount of tokens to the designated address since a certain amount
// * was burnt from the source chain invoker.
// * @param argsBs The argument bytes recevied by the ethereum lock proxy contract, need to be deserialized.
// * based on the way of serialization in the source chain proxy contract.
// * @param fromContractAddr The source chain contract address
// * @param fromChainId The source chain id
// */
function unlock(bytes memory argsBs, bytes memory fromContractAddr, uint64 fromChainId) onlyManagerContract whenNotPaused public returns (bool) {
TxArgs memory args = _deserializeTxArgs(argsBs);
require(fromContractAddr.length != 0, "from proxy contract address cannot be empty");
require(Utils.equalStorage(proxyHashMap[fromChainId], fromContractAddr), "From Proxy contract address error!");
require(args.toAssetHash.length != 0, "toAssetHash cannot be empty");
address toAssetHash = Utils.bytesToAddress(args.toAssetHash);
require(assetHashMap[toAssetHash][fromChainId].length != 0, "toAsset not bind");
require(args.toAddress.length != 0, "toAddress cannot be empty");
address toAddress = Utils.bytesToAddress(args.toAddress);
require(_transferFromContract(toAssetHash, toAddress, args.amount), "transfer asset from lock_proxy contract to toAddress failed!");
emit UnlockEvent(toAssetHash, toAddress, args.amount);
return true;
}
function getBalanceFor(address fromAssetHash) public view returns (uint256) {
if (fromAssetHash == address(0)) {
// return address(this).balance; // this expression would result in error: Failed to decode output: Error: insufficient data for uint256 type
address selfAddr = address(this);
return selfAddr.balance;
} else {
IERC20 erc20Token = IERC20(fromAssetHash);
return erc20Token.balanceOf(address(this));
}
}
function _transferToContract(address fromAssetHash, uint256 amount) internal returns (bool) {
if (fromAssetHash == address(0)) {
// fromAssetHash === address(0) denotes user choose to lock ether
// passively check if the received msg.value equals amount
require(msg.value != 0, "transferred ether cannot be zero!");
require(msg.value == amount, "transferred ether is not equal to amount!");
} else {
// make sure lockproxy contract will decline any received ether
require(msg.value == 0, "there should be no ether transfer!");
// actively transfer amount of asset from msg.sender to lock_proxy contract
require(_transferERC20ToContract(fromAssetHash, _msgSender(), address(this), amount), "transfer erc20 asset to lock_proxy contract failed!");
}
return true;
}
function _transferFromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
if (toAssetHash == address(0x0000000000000000000000000000000000000000)) {
// toAssetHash === address(0) denotes contract needs to unlock ether to toAddress
// convert toAddress from 'address' type to 'address payable' type, then actively transfer ether
address(uint160(toAddress)).transfer(amount);
} else {
// actively transfer amount of asset from lock_proxy contract to toAddress
require(_transferERC20FromContract(toAssetHash, toAddress, amount), "transfer erc20 asset from lock_proxy contract to toAddress failed!");
}
return true;
}
function _transferERC20ToContract(address fromAssetHash, address fromAddress, address toAddress, uint256 amount) internal returns (bool) {
IERC20 erc20Token = IERC20(fromAssetHash);
// require(erc20Token.transferFrom(fromAddress, toAddress, amount), "trasnfer ERC20 Token failed!");
erc20Token.safeTransferFrom(fromAddress, toAddress, amount);
return true;
}
function _transferERC20FromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
IERC20 erc20Token = IERC20(toAssetHash);
// require(erc20Token.transfer(toAddress, amount), "trasnfer ERC20 Token failed!");
erc20Token.safeTransfer(toAddress, amount);
return true;
}
function _serializeTxArgs(TxArgs memory args) internal pure returns (bytes memory) {
bytes memory buff;
buff = abi.encodePacked(
ZeroCopySink.WriteVarBytes(args.toAssetHash),
ZeroCopySink.WriteVarBytes(args.toAddress),
ZeroCopySink.WriteUint255(args.amount)
);
return buff;
}
function _deserializeTxArgs(bytes memory valueBs) internal pure returns (TxArgs memory) {
TxArgs memory args;
uint256 off = 0;
(args.toAssetHash, off) = ZeroCopySource.NextVarBytes(valueBs, off);
(args.toAddress, off) = ZeroCopySource.NextVarBytes(valueBs, off);
(args.amount, off) = ZeroCopySource.NextUint255(valueBs, off);
return args;
}
}