@@ -35,21 +35,118 @@ contract Toy is Base {
35
35
}
36
36
}
37
37
38
+ contract TokenToy is TokenSender , TokenReceiver {
39
+ constructor (address _wormholeRelayer , address _bridge , address _wormhole ) TokenBase (_wormholeRelayer, _bridge, _wormhole) {}
40
+
41
+ uint256 constant GAS_LIMIT = 250_000 ;
42
+
43
+ function quoteCrossChainDeposit (uint16 targetChain ) public view returns (uint256 cost ) {
44
+ // Cost of delivering token and payload to targetChain
45
+ uint256 deliveryCost;
46
+ (deliveryCost,) = wormholeRelayer.quoteEVMDeliveryPrice (targetChain, 0 , GAS_LIMIT);
47
+
48
+ // Total cost: delivery cost + cost of publishing the 'sending token' wormhole message
49
+ cost = deliveryCost + wormhole.messageFee ();
50
+ }
51
+
52
+ function sendCrossChainDeposit (
53
+ uint16 targetChain ,
54
+ address recipient ,
55
+ uint256 amount ,
56
+ address token
57
+ ) public payable {
58
+ uint256 cost = quoteCrossChainDeposit (targetChain);
59
+ require (msg .value == cost, "msg.value must be quoteCrossChainDeposit(targetChain) " );
60
+
61
+ IERC20 (token).transferFrom (msg .sender , address (this ), amount);
62
+
63
+ bytes memory payload = abi.encode (recipient);
64
+ sendTokenWithPayloadToEvm (
65
+ targetChain,
66
+ fromWormholeFormat (registeredSenders[targetChain]), // address (on targetChain) to send token and payload to
67
+ payload,
68
+ 0 , // receiver value
69
+ GAS_LIMIT,
70
+ token, // address of IERC20 token contract
71
+ amount
72
+ );
73
+ }
74
+
75
+ function sendCrossChainDeposit (
76
+ uint16 targetChain ,
77
+ address recipient ,
78
+ uint256 amount ,
79
+ address token ,
80
+ uint16 refundChain ,
81
+ address refundAddress
82
+ ) public payable {
83
+ uint256 cost = quoteCrossChainDeposit (targetChain);
84
+ require (msg .value == cost, "msg.value must be quoteCrossChainDeposit(targetChain) " );
85
+
86
+ IERC20 (token).transferFrom (msg .sender , address (this ), amount);
87
+
88
+ bytes memory payload = abi.encode (recipient);
89
+ sendTokenWithPayloadToEvm (
90
+ targetChain,
91
+ fromWormholeFormat (registeredSenders[targetChain]), // address (on targetChain) to send token and payload to
92
+ payload,
93
+ 0 , // receiver value
94
+ GAS_LIMIT,
95
+ token, // address of IERC20 token contract
96
+ amount,
97
+ refundChain,
98
+ refundAddress
99
+ );
100
+ }
101
+
102
+ function receivePayloadAndTokens (
103
+ bytes memory payload ,
104
+ TokenReceived[] memory receivedTokens ,
105
+ bytes32 sourceAddress ,
106
+ uint16 sourceChain ,
107
+ bytes32 // deliveryHash
108
+ ) internal override onlyWormholeRelayer isRegisteredSender (sourceChain, sourceAddress) {
109
+ require (receivedTokens.length == 1 , "Expected 1 token transfers " );
110
+ address recipient = abi.decode (payload, (address ));
111
+ IERC20 (receivedTokens[0 ].tokenAddress).transfer (recipient, receivedTokens[0 ].amount);
112
+ }
113
+ }
114
+
38
115
contract WormholeSDKTest is WormholeRelayerBasicTest {
39
116
Toy toySource;
40
117
Toy toyTarget;
118
+ TokenToy tokenToySource;
119
+ TokenToy tokenToyTarget;
120
+ ERC20Mock public token;
41
121
42
122
function setUpSource () public override {
43
123
toySource = new Toy (address (relayerSource), address (wormholeSource));
44
124
toySource.setRegisteredSender (targetChain, toWormholeFormat (address (this )));
125
+
126
+ tokenToySource = new TokenToy (address (relayerSource), address (tokenBridgeSource), address (wormholeSource));
127
+
128
+ token = createAndAttestToken (sourceChain);
45
129
}
46
130
47
131
function setUpTarget () public override {
48
132
toyTarget = new Toy (address (relayerTarget), address (wormholeTarget));
49
133
toyTarget.setRegisteredSender (sourceChain, toWormholeFormat (address (this )));
134
+
135
+ tokenToyTarget = new TokenToy (address (relayerTarget), address (tokenBridgeTarget), address (wormholeTarget));
136
+ }
137
+
138
+ function setUpGeneral () public override {
139
+ vm.selectFork (sourceFork);
140
+ tokenToySource.setRegisteredSender (targetChain, toWormholeFormat (address (tokenToyTarget)));
141
+
142
+
143
+ vm.selectFork (targetFork);
144
+ tokenToyTarget.setRegisteredSender (sourceChain, toWormholeFormat (address (tokenToySource)));
145
+
50
146
}
51
147
52
148
function testSendMessage () public {
149
+
53
150
vm.recordLogs ();
54
151
(uint256 cost ,) = relayerSource.quoteEVMDeliveryPrice (targetChain, 1e17 , 100_000 );
55
152
relayerSource.sendPayloadToEvm {value: cost}(targetChain, address (toyTarget), abi.encode (55 ), 1e17 , 100_000 );
@@ -70,4 +167,53 @@ contract WormholeSDKTest is WormholeRelayerBasicTest {
70
167
vm.selectFork (sourceFork);
71
168
require (56 == toySource.payloadReceived ());
72
169
}
170
+
171
+ function testSendToken () public {
172
+
173
+ vm.selectFork (sourceFork);
174
+
175
+ uint256 amount = 19e17 ;
176
+ token.approve (address (tokenToySource), amount);
177
+
178
+ vm.selectFork (targetFork);
179
+ address recipient = 0x1234567890123456789012345678901234567890 ;
180
+
181
+ vm.selectFork (sourceFork);
182
+ uint256 cost = tokenToySource.quoteCrossChainDeposit (targetChain);
183
+
184
+ vm.recordLogs ();
185
+ tokenToySource.sendCrossChainDeposit {value: cost}(
186
+ targetChain, recipient, amount, address (token)
187
+ );
188
+ performDelivery ();
189
+
190
+ vm.selectFork (targetFork);
191
+ address wormholeWrappedToken = tokenBridgeTarget.wrappedAsset (sourceChain, toWormholeFormat (address (token)));
192
+ assertEq (IERC20 (wormholeWrappedToken).balanceOf (recipient), amount);
193
+ }
194
+
195
+ function testSendTokenWithRefund () public {
196
+
197
+ vm.selectFork (sourceFork);
198
+
199
+ uint256 amount = 19e17 ;
200
+ token.approve (address (tokenToySource), amount);
201
+
202
+ vm.selectFork (targetFork);
203
+ address recipient = 0x1234567890123456789012345678901234567890 ;
204
+ address refundAddress = 0x2234567890123456789012345678901234567890 ;
205
+ vm.selectFork (sourceFork);
206
+ uint256 cost = tokenToySource.quoteCrossChainDeposit (targetChain);
207
+
208
+ vm.recordLogs ();
209
+ tokenToySource.sendCrossChainDeposit {value: cost}(
210
+ targetChain, recipient, amount, address (token), targetChain, refundAddress
211
+ );
212
+ performDelivery ();
213
+
214
+ vm.selectFork (targetFork);
215
+ address wormholeWrappedToken = tokenBridgeTarget.wrappedAsset (sourceChain, toWormholeFormat (address (token)));
216
+ assertEq (IERC20 (wormholeWrappedToken).balanceOf (recipient), amount);
217
+ assertTrue (refundAddress.balance > 0 );
218
+ }
73
219
}
0 commit comments