22pragma solidity ^ 0.8.20 ;
33
44import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
5+ import {ShortString, ShortStrings} from "@openzeppelin/contracts/utils/ShortStrings.sol " ;
56import {IBCAppBase} from "../commons/IBCAppBase.sol " ;
67import {Packet} from "../../core/04-channel/IIBCChannel.sol " ;
78import {IIBCModule} from "../../core/26-router/IIBCModule.sol " ;
8- import {Height} from "../../proto/Client.sol " ;
99import {Channel} from "../../proto/Channel.sol " ;
1010import {ICS20Lib} from "./ICS20Lib.sol " ;
1111import {IICS20Errors} from "./IICS20Errors.sol " ;
1212import {IIBCHandler} from "../../core/25-handler/IIBCHandler.sol " ;
1313
1414contract ICS20Transfer is IBCAppBase , IICS20Errors {
15- string public constant ICS20_VERSION = "ics20-1 " ;
15+ using ShortStrings for string ;
16+ using ShortStrings for ShortString;
1617
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 " ;
1920
2021 /// @dev IIBCHandler instance
2122 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;
2228
2329 /// @param ibcHandler_ IIBCHandler instance
24- constructor (IIBCHandler ibcHandler_ ) {
30+ /// @param port_ port identifier
31+ constructor (IIBCHandler ibcHandler_ , string memory port_ ) {
2532 ibcHandler = ibcHandler_;
33+ port = port_.toShortString ();
2634 }
2735
2836 // ------------------------------ Public Functions ------------------------------ //
2937
3038 /**
3139 * @dev sendTransfer sends a transfer packet to the destination chain.
40+ * @param sourceChannel source channel of the packet
3241 * @param denom denomination of the token. It can assume the denom string is escaped or not required to be escaped.
3342 * @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.
3844 */
3945 function sendTransfer (
46+ string calldata sourceChannel ,
4047 string calldata denom ,
4148 uint256 amount ,
4249 string calldata receiver ,
43- string calldata sourcePort ,
44- string calldata sourceChannel ,
45- uint64 timeoutHeight
50+ ICS20Lib.Timeout calldata timeout
4651 ) external returns (uint64 ) {
4752 if (! ICS20Lib.isEscapedJSONString (receiver)) {
4853 revert ICS20InvalidReceiverAddress (receiver);
4954 }
55+ string memory sourcePort = port.toString ();
5056 bytes memory denomPrefix = ICS20Lib.denomPrefix (sourcePort, sourceChannel);
5157 bytes memory denomBytes = bytes (denom);
5258 if (
@@ -62,28 +68,24 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
6268 _burnVoucher (_msgSender (), denom, amount);
6369 }
6470 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);
6872 }
6973
7074 /**
7175 * @dev depositSendTransfer sends a transfer packet to the destination chain after depositing the token.
76+ * @param sourceChannel source channel of the packet
7277 * @param tokenContract address of the token contract
7378 * @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.
7880 */
7981 function depositSendTransfer (
82+ string calldata sourceChannel ,
8083 address tokenContract ,
8184 uint256 amount ,
8285 string calldata receiver ,
83- string calldata sourcePort ,
84- string calldata sourceChannel ,
85- uint64 timeoutHeight
86+ ICS20Lib.Timeout calldata timeout
8687 ) external returns (uint64 ) {
88+ string memory sourcePort = port.toString ();
8789 if (! ICS20Lib.isEscapedJSONString (receiver)) {
8890 revert ICS20InvalidReceiverAddress (receiver);
8991 }
@@ -97,42 +99,40 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
9799 _mintVoucher (getVoucherEscrow (sourceChannel), tokenContract, amount);
98100 bytes memory packetData =
99101 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);
103103 }
104104
105105 /**
106106 * @dev deposit deposits the ERC20 token to the contract.
107+ * @param to address to deposit the token
107108 * @param tokenContract address of the token contract
108109 * @param amount amount of the token
109- * @param to address to deposit the token
110110 */
111111 function deposit (address to , address tokenContract , uint256 amount ) public {
112112 if (tokenContract == address (0 )) {
113113 revert ICS20InvalidTokenContract (tokenContract);
114114 }
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);
118118 }
119119 _mintVoucher (to, tokenContract, amount);
120120 }
121121
122122 /**
123123 * @dev withdraw withdraws the ERC20 token from the contract.
124+ * @param to address to withdraw the token
124125 * @param tokenContract address of the token contract
125126 * @param amount amount of the token
126- * @param to address to withdraw the token
127127 */
128128 function withdraw (address to , address tokenContract , uint256 amount ) public {
129129 if (tokenContract == address (0 )) {
130130 revert ICS20InvalidTokenContract (tokenContract);
131131 }
132- address from = _msgSender ();
133- _burnVoucher (from , ICS20Lib.addressToHexString (tokenContract), amount);
132+ address sender = _msgSender ();
133+ _burnVoucher (sender , ICS20Lib.addressToHexString (tokenContract), amount);
134134 if (! IERC20 (tokenContract).transfer (to, amount)) {
135- revert ICS20FailedERC20TransferFrom (tokenContract, from , address (this ), to, amount);
135+ revert ICS20FailedERC20TransferFrom (tokenContract, sender , address (this ), to, amount);
136136 }
137137 }
138138
@@ -171,6 +171,7 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
171171 /**
172172 * @dev getVoucherEscrow returns the voucher escrow address for the given channel.
173173 * @param channelId channel identifier
174+ * @return voucher escrow address
174175 */
175176 function getVoucherEscrow (string calldata channelId ) public view virtual returns (address ) {
176177 return address (uint160 (uint256 (keccak256 (abi.encode (address (this ), channelId)))));
@@ -266,6 +267,9 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
266267 onlyIBC
267268 returns (address , string memory )
268269 {
270+ if (! _equal (msg_.portId.toShortString (), port)) {
271+ revert ICS20UnexpectedPort (msg_.portId, port.toString ());
272+ }
269273 if (msg_.order != Channel.Order.ORDER_UNORDERED) {
270274 revert IBCModuleChannelOrderNotAllowed (msg_.portId, msg_.channelId, msg_.order);
271275 }
@@ -286,6 +290,9 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
286290 onlyIBC
287291 returns (address , string memory )
288292 {
293+ if (! _equal (msg_.portId.toShortString (), port)) {
294+ revert ICS20UnexpectedPort (msg_.portId, port.toString ());
295+ }
289296 if (msg_.order != Channel.Order.ORDER_UNORDERED) {
290297 revert IBCModuleChannelOrderNotAllowed (msg_.portId, msg_.channelId, msg_.order);
291298 }
@@ -423,4 +430,11 @@ contract ICS20Transfer is IBCAppBase, IICS20Errors {
423430 function _decodeReceiver (string memory receiver ) internal pure virtual returns (address , bool ) {
424431 return ICS20Lib.hexStringToAddress (receiver);
425432 }
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+ }
426440}
0 commit comments