-
Notifications
You must be signed in to change notification settings - Fork 6
/
WrappedCAT.sol
158 lines (138 loc) · 6.09 KB
/
WrappedCAT.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
// SPDX-License-Identifier: MIT License
/* yak tracks all over the place */
pragma solidity 0.8.23;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./interfaces/IPortalMessageReceiver.sol";
import "./interfaces/IPortal.sol";
/**
* @title Wrapped CAT Token Contract
* @notice ERC-20 representing wrapped Chia Asset Tokens by leveraging the warp.green protocol.
* @dev This contract manages the wrapping and unwrapping of Chia Asset Tokens (CATs) on Ethereum, allowing CATs to be used within the Ethereum ecosystem.
*/
contract WrappedCAT is ERC20, ERC20Permit, IPortalMessageReceiver {
/**
* @notice The address of the warp.green portal contract.
* @dev Used as an oracle. The portal usually sits behind a TransparentUpgradeableProxy.
*/
address public immutable portal;
/**
* @notice When wrapping or unwrapping CATs, a tip is sent to the warp.green protocol. The tip is expressed in basis points.
* @dev Tip is calculated as amount * tip / 10000 - the remaining amount is sent to the recipient.
*/
uint16 public immutable tip;
/**
* @notice Ratio used to convert mojos (smallest unit on Chia) to token units on Ethereum.
* @dev Token amount on this chain = [mojos on Chia] * `mojoToTokenRatio`
*/
uint64 public immutable mojoToTokenRatio;
/**
* @notice The chain id of the original CATs. Usually "xch", which indicates Chia.
* @dev Used to verify the source chain of messages.
*/
bytes3 public immutable otherChain;
/**
* @notice The hash of the puzzle used to verifiably lock CATs on Chia. Sender of messages from Chia.
* @dev Used to verify the source of messages when they're received.
*/
bytes32 public lockerPuzzleHash;
/**
* @notice The hash of the puzzle used to unlock CATs on Chia. Message receiver on Chia.
* @dev Used as a destination for sent messages after ERC-20 tokens are burned.
*/
bytes32 public unlockerPuzzleHash;
/**
* @notice Creates a Wrapped CAT token linked to a specific Chia Asset Token.
* @dev ERC-20 has 18 decimals - `mojoToTokenRatio` should be set accordingly.
* @param _name Name of the token.
* @param _symbol Symbol of the token.
* @param _portal Address of the warp.green portal contract.
* @param _tip Tip percentage (in basis points) paid to the portal. (0.01 - 10%)
* @param _mojoToTokenRatio Conversion ratio from mojos to token units.
* @param _otherChain ID of chain where CATs are locked (e.g., Chia).
*/
constructor(
string memory _name,
string memory _symbol,
address _portal,
uint16 _tip,
uint64 _mojoToTokenRatio,
bytes3 _otherChain
) ERC20(_name, _symbol) ERC20Permit(_name) {
require(_tip > 0 && _tip <= 1000, "!tip");
require(_portal != address(0), "!portal");
portal = _portal;
tip = _tip;
mojoToTokenRatio = _mojoToTokenRatio;
otherChain = _otherChain;
}
/**
* @notice Initializes puzzle hashes for locking and unlocking tokens. Should be called in the same transaction as deployment.
* @dev Allows the address of the contract to be determined using CREATE2, as the arguments below depend on the address of this contract. Can only be called once per contract lifetime.
* @param _lockerPuzzleHash Puzzle hash for locking CATs on Chia.
* @param _unlockerPuzzleHash Puzzle hash for unlocking CATs on Chia.
*/
function initializePuzzleHashes(
bytes32 _lockerPuzzleHash,
bytes32 _unlockerPuzzleHash
) external {
require(
lockerPuzzleHash == bytes32(0) && unlockerPuzzleHash == bytes32(0),
"nope"
);
lockerPuzzleHash = _lockerPuzzleHash;
unlockerPuzzleHash = _unlockerPuzzleHash;
}
/**
* @notice Receives and processes messages from the warp.green portal
* @dev Uses the warp.green portal contract as an oracle; verifies message and handles the unwrapping process.
* @param _source_chain Message source chain ID (e.g., "xch").
* @param _source Message source (puzzle hash). Must match the locker puzzle hash.
* @param _contents Message contents - receiver address and mojo amount.
*/
function receiveMessage(
bytes32 /* _nonce */,
bytes3 _source_chain,
bytes32 _source,
bytes32[] calldata _contents
) external {
require(
msg.sender == portal &&
_source == lockerPuzzleHash &&
_source_chain == otherChain,
"!msg"
);
uint256 amount = uint256(_contents[1]) * mojoToTokenRatio;
uint256 transferTip = (amount * tip) / 10000;
require(transferTip > 0 && amount > transferTip, "!amnt");
_mint(address(uint160(uint256(_contents[0]))), amount - transferTip);
_mint(portal, transferTip);
}
/**
* @notice Burns Wrapped CAT ERC-20s and sends a message to unlock the original CAT tokens on Chia.
* @dev Verifies the toll payment, burns ERC-20s, and sends a message to unlock CATs.
* @param _receiver Puzzle hash of the receiver on Chia.
* @param _mojoAmount Amount of CAT tokens (in mojos) to unlock on Chia.
*/
function bridgeBack(
bytes32 _receiver,
uint256 _mojoAmount
) external payable {
require(msg.value == IPortal(portal).messageToll(), "!toll");
uint256 transferTip = (_mojoAmount * tip) / 10000;
if (transferTip == 0) {
transferTip = 1;
}
require(_mojoAmount > transferTip, "!amnt");
_burn(msg.sender, _mojoAmount * mojoToTokenRatio);
_mint(portal, transferTip * mojoToTokenRatio);
bytes32[] memory contents = new bytes32[](2);
contents[0] = _receiver;
contents[1] = bytes32(_mojoAmount - transferTip);
IPortal(portal).sendMessage{value: msg.value}(
otherChain,
unlockerPuzzleHash,
contents
);
}
}