2
2
pragma solidity ^ 0.8.20 ;
3
3
4
4
import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
5
+ import {ShortString, ShortStrings} from "@openzeppelin/contracts/utils/ShortStrings.sol " ;
5
6
import {IBCAppBase} from "../commons/IBCAppBase.sol " ;
6
7
import {Packet} from "../../core/04-channel/IIBCChannel.sol " ;
7
8
import {IIBCModule} from "../../core/26-router/IIBCModule.sol " ;
8
- import {Height} from "../../proto/Client.sol " ;
9
9
import {Channel} from "../../proto/Channel.sol " ;
10
10
import {ICS20Lib} from "./ICS20Lib.sol " ;
11
11
import {IICS20Errors} from "./IICS20Errors.sol " ;
12
12
import {IIBCHandler} from "../../core/25-handler/IIBCHandler.sol " ;
13
13
14
14
contract ICS20Transfer is IBCAppBase , IICS20Errors {
15
- string public constant ICS20_VERSION = "ics20-1 " ;
15
+ using ShortStrings for string ;
16
+ using ShortStrings for ShortString;
16
17
17
- // mapping from denomination to account balances
18
- mapping ( string denom = > mapping ( address account = > uint256 balance )) internal _balances ;
18
+ /// @dev ICS20 version
19
+ string public constant ICS20_VERSION = " ics20-1 " ;
19
20
20
21
/// @dev IIBCHandler instance
21
22
IIBCHandler internal immutable ibcHandler;
23
+ /// @dev port identifier
24
+ ShortString internal immutable port;
25
+
26
+ /// @dev balance mapping for the token
27
+ mapping (string denom = > mapping (address account = > uint256 balance )) internal _balances;
22
28
23
29
/// @param ibcHandler_ IIBCHandler instance
24
- constructor (IIBCHandler ibcHandler_ ) {
30
+ /// @param port_ port identifier
31
+ constructor (IIBCHandler ibcHandler_ , string memory port_ ) {
25
32
ibcHandler = ibcHandler_;
33
+ port = port_.toShortString ();
26
34
}
27
35
28
36
// ------------------------------ Public Functions ------------------------------ //
29
37
30
38
/**
31
39
* @dev sendTransfer sends a transfer packet to the destination chain.
40
+ * @param sourceChannel source channel of the packet
32
41
* @param denom denomination of the token. It can assume the denom string is escaped or not required to be escaped.
33
42
* @param amount amount of the token
34
- * @param receiver receiver address on the destination chain
35
- * @param sourcePort source port of the packet
36
- * @param sourceChannel source channel of the packet
37
- * @param timeoutHeight timeout height of the packet
43
+ * @param receiver receiver address on the destination chain. This must be a valid address format per destination chain.
38
44
*/
39
45
function sendTransfer (
46
+ string calldata sourceChannel ,
40
47
string calldata denom ,
41
48
uint256 amount ,
42
49
string calldata receiver ,
43
- string calldata sourcePort ,
44
- string calldata sourceChannel ,
45
- uint64 timeoutHeight
50
+ ICS20Lib.Timeout calldata timeout
46
51
) external returns (uint64 ) {
47
52
if (! ICS20Lib.isEscapedJSONString (receiver)) {
48
53
revert ICS20InvalidReceiverAddress (receiver);
49
54
}
55
+ string memory sourcePort = port.toString ();
50
56
bytes memory denomPrefix = ICS20Lib.denomPrefix (sourcePort, sourceChannel);
51
57
bytes memory denomBytes = bytes (denom);
52
58
if (
@@ -62,28 +68,24 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
62
68
_burnVoucher (_msgSender (), denom, amount);
63
69
}
64
70
bytes memory packetData = ICS20Lib.marshalJSON (denom, amount, encodeAddress (_msgSender ()), receiver);
65
- return ibcHandler.sendPacket (
66
- sourcePort, sourceChannel, Height.Data ({revision_number: 0 , revision_height: timeoutHeight}), 0 , packetData
67
- );
71
+ return ibcHandler.sendPacket (sourcePort, sourceChannel, timeout.height, timeout.timestampNanos, packetData);
68
72
}
69
73
70
74
/**
71
75
* @dev depositSendTransfer sends a transfer packet to the destination chain after depositing the token.
76
+ * @param sourceChannel source channel of the packet
72
77
* @param tokenContract address of the token contract
73
78
* @param amount amount of the token
74
- * @param receiver receiver address on the destination chain
75
- * @param sourcePort source port of the packet
76
- * @param sourceChannel source channel of the packet
77
- * @param timeoutHeight timeout height of the packet
79
+ * @param receiver receiver address on the destination chain. This must be a valid address format per destination chain.
78
80
*/
79
81
function depositSendTransfer (
82
+ string calldata sourceChannel ,
80
83
address tokenContract ,
81
84
uint256 amount ,
82
85
string calldata receiver ,
83
- string calldata sourcePort ,
84
- string calldata sourceChannel ,
85
- uint64 timeoutHeight
86
+ ICS20Lib.Timeout calldata timeout
86
87
) external returns (uint64 ) {
88
+ string memory sourcePort = port.toString ();
87
89
if (! ICS20Lib.isEscapedJSONString (receiver)) {
88
90
revert ICS20InvalidReceiverAddress (receiver);
89
91
}
@@ -97,42 +99,40 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
97
99
_mintVoucher (getVoucherEscrow (sourceChannel), tokenContract, amount);
98
100
bytes memory packetData =
99
101
ICS20Lib.marshalJSON (ICS20Lib.addressToHexString (tokenContract), amount, encodeAddress (sender), receiver);
100
- return ibcHandler.sendPacket (
101
- sourcePort, sourceChannel, Height.Data ({revision_number: 0 , revision_height: timeoutHeight}), 0 , packetData
102
- );
102
+ return ibcHandler.sendPacket (sourcePort, sourceChannel, timeout.height, timeout.timestampNanos, packetData);
103
103
}
104
104
105
105
/**
106
106
* @dev deposit deposits the ERC20 token to the contract.
107
+ * @param to address to deposit the token
107
108
* @param tokenContract address of the token contract
108
109
* @param amount amount of the token
109
- * @param to address to deposit the token
110
110
*/
111
111
function deposit (address to , address tokenContract , uint256 amount ) public {
112
112
if (tokenContract == address (0 )) {
113
113
revert ICS20InvalidTokenContract (tokenContract);
114
114
}
115
- address from = _msgSender ();
116
- if (! IERC20 (tokenContract).transferFrom (from , address (this ), amount)) {
117
- revert ICS20FailedERC20Transfer (tokenContract, from , address (this ), amount);
115
+ address sender = _msgSender ();
116
+ if (! IERC20 (tokenContract).transferFrom (sender , address (this ), amount)) {
117
+ revert ICS20FailedERC20Transfer (tokenContract, sender , address (this ), amount);
118
118
}
119
119
_mintVoucher (to, tokenContract, amount);
120
120
}
121
121
122
122
/**
123
123
* @dev withdraw withdraws the ERC20 token from the contract.
124
+ * @param to address to withdraw the token
124
125
* @param tokenContract address of the token contract
125
126
* @param amount amount of the token
126
- * @param to address to withdraw the token
127
127
*/
128
128
function withdraw (address to , address tokenContract , uint256 amount ) public {
129
129
if (tokenContract == address (0 )) {
130
130
revert ICS20InvalidTokenContract (tokenContract);
131
131
}
132
- address from = _msgSender ();
133
- _burnVoucher (from , ICS20Lib.addressToHexString (tokenContract), amount);
132
+ address sender = _msgSender ();
133
+ _burnVoucher (sender , ICS20Lib.addressToHexString (tokenContract), amount);
134
134
if (! IERC20 (tokenContract).transfer (to, amount)) {
135
- revert ICS20FailedERC20TransferFrom (tokenContract, from , address (this ), to, amount);
135
+ revert ICS20FailedERC20TransferFrom (tokenContract, sender , address (this ), to, amount);
136
136
}
137
137
}
138
138
@@ -171,6 +171,7 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
171
171
/**
172
172
* @dev getVoucherEscrow returns the voucher escrow address for the given channel.
173
173
* @param channelId channel identifier
174
+ * @return voucher escrow address
174
175
*/
175
176
function getVoucherEscrow (string calldata channelId ) public view virtual returns (address ) {
176
177
return address (uint160 (uint256 (keccak256 (abi.encode (address (this ), channelId)))));
@@ -266,6 +267,9 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
266
267
onlyIBC
267
268
returns (address , string memory )
268
269
{
270
+ if (! _equal (msg_.portId.toShortString (), port)) {
271
+ revert ICS20UnexpectedPort (msg_.portId, port.toString ());
272
+ }
269
273
if (msg_.order != Channel.Order.ORDER_UNORDERED) {
270
274
revert IBCModuleChannelOrderNotAllowed (msg_.portId, msg_.channelId, msg_.order);
271
275
}
@@ -286,6 +290,9 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
286
290
onlyIBC
287
291
returns (address , string memory )
288
292
{
293
+ if (! _equal (msg_.portId.toShortString (), port)) {
294
+ revert ICS20UnexpectedPort (msg_.portId, port.toString ());
295
+ }
289
296
if (msg_.order != Channel.Order.ORDER_UNORDERED) {
290
297
revert IBCModuleChannelOrderNotAllowed (msg_.portId, msg_.channelId, msg_.order);
291
298
}
@@ -423,4 +430,11 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
423
430
function _decodeReceiver (string memory receiver ) internal pure virtual returns (address , bool ) {
424
431
return ICS20Lib.hexStringToAddress (receiver);
425
432
}
433
+
434
+ /**
435
+ * @dev _equal compares two ShortString values.
436
+ */
437
+ function _equal (ShortString a , ShortString b ) internal pure returns (bool ) {
438
+ return ShortString.unwrap (a) == ShortString.unwrap (b);
439
+ }
426
440
}
0 commit comments