This repository has been archived by the owner on May 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 168
/
ERC20Handler.sol
152 lines (124 loc) · 5.53 KB
/
ERC20Handler.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
pragma solidity 0.6.4;
pragma experimental ABIEncoderV2;
import "../interfaces/IDepositExecute.sol";
import "./HandlerHelpers.sol";
import "../ERC20Safe.sol";
import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol";
contract ERC20Handler is IDepositExecute, HandlerHelpers, ERC20Safe {
bool public _useContractWhitelist;
struct DepositRecord {
address _tokenAddress;
uint8 _destinationChainID;
bytes32 _resourceID;
uint _lenDestinationRecipientAddress;
bytes _destinationRecipientAddress;
address _depositer;
uint _amount;
}
// depositNonce => Deposit Record
mapping (uint256 => DepositRecord) public _depositRecords;
constructor(
address bridgeAddress,
bytes32[] memory initialResourceIDs,
address[] memory initialContractAddresses,
address[] memory burnableContractAddresses
) public {
require(initialResourceIDs.length == initialContractAddresses.length,
"mismatch length between initialResourceIDs and initialContractAddresses");
_bridgeAddress = bridgeAddress;
for (uint256 i = 0; i < initialResourceIDs.length; i++) {
_setResource(initialResourceIDs[i], initialContractAddresses[i]);
}
for (uint256 i = 0; i < burnableContractAddresses.length; i++) {
_setBurnable(burnableContractAddresses[i]);
}
}
function getDepositRecord(uint256 depositID) public view returns (DepositRecord memory) {
return _depositRecords[depositID];
}
// Initiate a transfer by completing a deposit (transferFrom).
// Data passed into the function should be constructed as follows:
//
// resourceID bytes32 bytes 0 - 32
// amount uint256 bytes 32 - 64
// recipientAddress length uint256 bytes 64 - 96
// recipientAddress bytes bytes 96 - END
function deposit(
uint8 destinationChainID,
uint256 depositNonce,
address depositer,
bytes memory data
) public override _onlyBridge {
bytes32 resourceID;
bytes memory recipientAddress;
uint256 amount;
uint256 lenRecipientAddress;
assembly {
resourceID := mload(add(data, 0x20))
amount := mload(add(data, 0x40))
recipientAddress := mload(0x40)
lenRecipientAddress := mload(add(0x60, data))
mstore(0x40, add(0x20, add(recipientAddress, lenRecipientAddress)))
calldatacopy(
recipientAddress, // copy to destinationRecipientAddress
0xE4, // copy from calldata @ 0x104
sub(calldatasize(), 0xE4) // copy size (calldatasize - 0x104)
)
}
address tokenAddress = _resourceIDToTokenContractAddress[resourceID];
require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted");
if (_burnList[tokenAddress]) {
burnERC20(tokenAddress, depositer, amount);
} else {
lockERC20(tokenAddress, depositer, address(this), amount);
}
_depositRecords[depositNonce] = DepositRecord(
tokenAddress,
destinationChainID,
resourceID,
lenRecipientAddress,
recipientAddress,
depositer,
amount
);
}
// execute a deposit
// bytes memory data passed into the function should be constructed as follows:
// resourceID bytes32 bytes 0 - 32
// amount uint256 bytes 32 - 64
// --------------------------------------------------------------------
// destinationRecipientAddress length uint256 bytes 64 - 96
// destinationRecipientAddress bytes bytes 96 - END
function executeDeposit(bytes memory data) public override _onlyBridge {
uint256 amount;
bytes32 resourceID;
bytes memory destinationRecipientAddress;
assembly {
resourceID := mload(add(data, 0x20))
amount := mload(add(data, 0x40))
destinationRecipientAddress := mload(0x40)
let lenDestinationRecipientAddress := mload(add(0x60, data))
mstore(0x40, add(0x20, add(destinationRecipientAddress, lenDestinationRecipientAddress)))
// in the calldata the destinationRecipientAddress is stored at 0xC4 after accounting for the function signature and length declaration
calldatacopy(
destinationRecipientAddress, // copy to destinationRecipientAddress
0x84, // copy from calldata @ 0x84
sub(calldatasize(), 0x84) // copy size to the end of calldata
)
}
bytes20 recipientAddress;
address tokenAddress = _resourceIDToTokenContractAddress[resourceID];
assembly {
recipientAddress := mload(add(destinationRecipientAddress, 0x20))
}
require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted");
if (_burnList[tokenAddress]) {
mintERC20(tokenAddress, address(recipientAddress), amount);
} else {
releaseERC20(tokenAddress, address(recipientAddress), amount);
}
}
function withdraw(address tokenAddress, address recipient, uint amount) public _onlyBridge {
releaseERC20(tokenAddress, recipient, amount);
}
}