forked from Polymarket/ctf-exchange-v2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCollateralToken.sol
More file actions
250 lines (207 loc) · 9.61 KB
/
Copy pathCollateralToken.sol
File metadata and controls
250 lines (207 loc) · 9.61 KB
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.34;
import { OwnableRoles } from "@solady/src/auth/OwnableRoles.sol";
import { ERC20 } from "@solady/src/tokens/ERC20.sol";
import { Initializable } from "@solady/src/utils/Initializable.sol";
import { SafeTransferLib } from "@solady/src/utils/SafeTransferLib.sol";
import { UUPSUpgradeable } from "@solady/src/utils/UUPSUpgradeable.sol";
import { CollateralErrors } from "./abstract/CollateralErrors.sol";
import { ICollateralToken } from "./interfaces/ICollateralToken.sol";
import { ICollateralTokenCallbacks } from "./interfaces/ICollateralTokenCallbacks.sol";
abstract contract CollateralTokenEvents {
/// @notice Emitted when an asset is wrapped into collateral
/// @param caller Address that initiated the wrap
/// @param asset The underlying asset address
/// @param to Recipient of the minted collateral
/// @param amount Amount of collateral minted
event Wrapped(address indexed caller, address indexed asset, address indexed to, uint256 amount);
/// @notice Emitted when collateral is unwrapped to an asset
/// @param caller Address that initiated the unwrap
/// @param asset The underlying asset address
/// @param to Recipient of the unwrapped asset
/// @param amount Amount of collateral burned
event Unwrapped(address indexed caller, address indexed asset, address indexed to, uint256 amount);
}
/// @title CollateralToken
/// @author Polymarket
/// @notice ROLE_0: Minter/Burner
/// @notice ROLE_1: Wrapper/Unwrapper
contract CollateralToken is
UUPSUpgradeable,
Initializable,
ERC20,
OwnableRoles,
CollateralErrors,
CollateralTokenEvents,
ICollateralToken
{
using SafeTransferLib for address;
/*--------------------------------------------------------------
STATE
--------------------------------------------------------------*/
/// @notice Address of the native USDC token
address public immutable USDC;
/// @notice Address of the bridged USDC.e token
address public immutable USDCE;
/// @notice Address of the vault holding underlying assets
address public immutable VAULT;
/*--------------------------------------------------------------
CONSTANTS
--------------------------------------------------------------*/
/// @dev Role flag for mint/burn privileges
uint256 internal constant MINTER_ROLE = _ROLE_0;
/// @dev Role flag for wrap/unwrap privileges
uint256 internal constant WRAPPER_ROLE = _ROLE_1;
/*--------------------------------------------------------------
MODIFIERS
--------------------------------------------------------------*/
/// @dev Reverts if the asset is not USDC or USDC.e
modifier onlyValidAsset(address _asset) {
require(_asset == USDC || _asset == USDCE, InvalidAsset());
_;
}
/*--------------------------------------------------------------
CONSTRUCTOR
--------------------------------------------------------------*/
/// @notice Deploys the CollateralToken implementation
/// @param _usdc Address of the native USDC token
/// @param _usdce Address of the bridged USDC.e token
/// @param _vault Address of the vault for underlying assets
constructor(address _usdc, address _usdce, address _vault) {
USDC = _usdc;
USDCE = _usdce;
VAULT = _vault;
_disableInitializers();
}
/*--------------------------------------------------------------
INITIALIZE
--------------------------------------------------------------*/
/// @notice Initializes the contract with the given owner.
/// @dev This replaces the constructor for upgradeable contracts.
/// @param _owner The address to set as the owner of the contract.
function initialize(address _owner) external initializer {
_initializeOwner(_owner);
}
/*--------------------------------------------------------------
VIEW
--------------------------------------------------------------*/
/// @notice Returns the token name
/// @return The token name string
function name() public pure override returns (string memory) {
return "Polymarket USD";
}
/// @notice Returns the token symbol
/// @return The token symbol string
function symbol() public pure override returns (string memory) {
return "pUSD";
}
/// @notice Returns the token decimal precision
/// @return The number of decimals (6)
function decimals() public pure override returns (uint8) {
return 6;
}
/*--------------------------------------------------------------
EXTERNAL
--------------------------------------------------------------*/
/// @notice Mints a new collateral token
/// @param _to The address to mint the collateral token to
/// @param _amount The amount of collateral token to mint
/// @dev The caller must have the MINTER_ROLE
function mint(address _to, uint256 _amount) external onlyRoles(MINTER_ROLE) {
_mint(_to, _amount);
}
/// @notice Burns a collateral token
/// @param _amount The amount of collateral token to burn
/// @dev The caller must have the MINTER_ROLE
function burn(uint256 _amount) external onlyRoles(MINTER_ROLE) {
_burn(msg.sender, _amount);
}
/// @notice Wraps a supported asset into the collateral token
/// @param _asset The asset to wrap
/// @param _to The address to wrap the asset to
/// @param _amount The amount of asset to wrap
/// @param _callbackReceiver Address to receive the callback, or address(0) to skip callback
/// @param _data Callback data
/// @notice The asset must be a supported asset
/// @dev The caller must have the WRAPPER_ROLE
/// @dev The asset must be transferred into this contract either before calling this function or
/// in the callback
function wrap(address _asset, address _to, uint256 _amount, address _callbackReceiver, bytes calldata _data)
external
onlyRoles(WRAPPER_ROLE)
onlyValidAsset(_asset)
{
// mint
_mint(_to, _amount);
// callback (skip if address(0))
if (_callbackReceiver != address(0)) {
ICollateralTokenCallbacks(_callbackReceiver).wrapCallback(_asset, _to, _amount, _data);
}
// transfer asset to the vault
_asset.safeTransfer(VAULT, _amount);
emit Wrapped(msg.sender, _asset, _to, _amount);
}
/// @notice Unwraps a supported asset from the collateral token
/// @param _asset The asset to unwrap
/// @param _to The address to unwrap the asset to
/// @param _amount The amount of asset to unwrap
/// @param _callbackReceiver Address to receive the callback, or address(0) to skip callback
/// @param _data Callback data
/// @notice The asset must be a supported asset
/// @dev The caller must have the WRAPPER_ROLE
/// @dev The asset must be transferred into this contract either before calling this function or
/// in the callback
function unwrap(address _asset, address _to, uint256 _amount, address _callbackReceiver, bytes calldata _data)
external
onlyRoles(WRAPPER_ROLE)
onlyValidAsset(_asset)
{
// transfer asset from the vault
_asset.safeTransferFrom(VAULT, _to, _amount);
// callback (skip if address(0))
if (_callbackReceiver != address(0)) {
ICollateralTokenCallbacks(_callbackReceiver).unwrapCallback(_asset, _to, _amount, _data);
}
// burn
_burn(address(this), _amount);
emit Unwrapped(msg.sender, _asset, _to, _amount);
}
/*--------------------------------------------------------------
ROLE MANAGEMENT
--------------------------------------------------------------*/
/// @notice Grants minter role to an address
/// @param _minter Address to grant minter role
function addMinter(address _minter) external onlyOwner {
_grantRoles(_minter, MINTER_ROLE);
}
/// @notice Revokes minter role from an address
/// @param _minter Address to revoke minter role from
function removeMinter(address _minter) external onlyOwner {
_removeRoles(_minter, MINTER_ROLE);
}
/// @notice Grants wrapper role to an address
/// @param _wrapper Address to grant wrapper role
function addWrapper(address _wrapper) external onlyOwner {
_grantRoles(_wrapper, WRAPPER_ROLE);
}
/// @notice Revokes wrapper role from an address
/// @param _wrapper Address to revoke wrapper role from
function removeWrapper(address _wrapper) external onlyOwner {
_removeRoles(_wrapper, WRAPPER_ROLE);
}
/*--------------------------------------------------------------
SOLADY OVERRIDES
--------------------------------------------------------------*/
/// @dev Disables Permit2 infinite allowance
/// @return Always returns false
function _givePermit2InfiniteAllowance() internal pure override returns (bool) {
return false;
}
/*--------------------------------------------------------------
UUPS UPGRADE AUTHORIZATION
--------------------------------------------------------------*/
/// @dev Authorizes an upgrade to a new implementation.
/// @dev Only the owner can authorize upgrades.
/// @param newImplementation The address of the new implementation contract.
function _authorizeUpgrade(address newImplementation) internal override onlyOwner { }
}