@@ -22,6 +22,25 @@ import (
22
22
"github.com/CosmWasm/wasmd/x/wasm/types"
23
23
)
24
24
25
+ type transferExecMsg struct {
26
+ ToAddress string `json:"to_address"`
27
+ ChannelID string `json:"channel_id"`
28
+ TimeoutSeconds uint32 `json:"timeout_seconds"`
29
+ }
30
+
31
+ // executeMsg is the ibc-callbacks contract's execute msg
32
+ type executeMsg struct {
33
+ Transfer * transferExecMsg `json:"transfer"`
34
+ }
35
+ type queryMsg struct {
36
+ CallbackStats struct {} `json:"callback_stats"`
37
+ }
38
+ type queryResp struct {
39
+ IBCAckCallbacks []wasmvmtypes.IBCPacketAckMsg `json:"ibc_ack_callbacks"`
40
+ IBCTimeoutCallbacks []wasmvmtypes.IBCPacketTimeoutMsg `json:"ibc_timeout_callbacks"`
41
+ IBCDestinationCallbacks []wasmvmtypes.IBCDestinationCallbackMsg `json:"ibc_destination_callbacks"`
42
+ }
43
+
25
44
func TestIBCCallbacks (t * testing.T ) {
26
45
// scenario:
27
46
// given two chains
@@ -35,7 +54,6 @@ func TestIBCCallbacks(t *testing.T) {
35
54
chainB := wasmibctesting .NewWasmTestChain (coord .GetChain (ibctesting .GetChainID (2 )))
36
55
37
56
actorChainA := sdk .AccAddress (chainA .SenderPrivKey .PubKey ().Address ())
38
- oneToken := sdk .NewCoins (sdk .NewCoin (sdk .DefaultBondDenom , sdkmath .NewInt (1 )))
39
57
40
58
path := wasmibctesting .NewWasmPath (chainA , chainB )
41
59
path .EndpointA .ChannelConfig = & ibctesting.ChannelConfig {
@@ -51,47 +69,36 @@ func TestIBCCallbacks(t *testing.T) {
51
69
// with an ics-20 transfer channel setup between both chains
52
70
coord .Setup (& path .Path )
53
71
72
+ oneToken := sdk .NewCoins (sdk .NewCoin (sdk .DefaultBondDenom , sdkmath .NewInt (1 )))
73
+ ibcDenom := ibctransfertypes .NewDenom (
74
+ sdk .DefaultBondDenom ,
75
+ ibctransfertypes .NewHop (path .EndpointB .ChannelConfig .PortID , path .EndpointB .ChannelID ),
76
+ ).IBCDenom ()
77
+ ibcCoin := sdk .NewCoin (ibcDenom , sdkmath .NewInt (1 ))
78
+
54
79
// with an ibc-callbacks contract deployed on chain A
55
80
codeIDonA := chainA .StoreCodeFile ("./testdata/ibc_callbacks.wasm" ).CodeID
56
81
57
82
// and on chain B
58
83
codeIDonB := chainB .StoreCodeFile ("./testdata/ibc_callbacks.wasm" ).CodeID
59
84
60
- type TransferExecMsg struct {
61
- ToAddress string `json:"to_address"`
62
- ChannelID string `json:"channel_id"`
63
- TimeoutSeconds uint32 `json:"timeout_seconds"`
64
- }
65
- // ExecuteMsg is the ibc-callbacks contract's execute msg
66
- type ExecuteMsg struct {
67
- Transfer * TransferExecMsg `json:"transfer"`
68
- }
69
- type QueryMsg struct {
70
- CallbackStats struct {} `json:"callback_stats"`
71
- }
72
- type QueryResp struct {
73
- IBCAckCallbacks []wasmvmtypes.IBCPacketAckMsg `json:"ibc_ack_callbacks"`
74
- IBCTimeoutCallbacks []wasmvmtypes.IBCPacketTimeoutMsg `json:"ibc_timeout_callbacks"`
75
- IBCDestinationCallbacks []wasmvmtypes.IBCDestinationCallbackMsg `json:"ibc_destination_callbacks"`
76
- }
77
-
78
85
specs := map [string ]struct {
79
- contractMsg ExecuteMsg
86
+ contractMsg executeMsg
80
87
// expAck is true if the packet is relayed, false if it times out
81
88
expAck bool
82
89
}{
83
90
"success" : {
84
- contractMsg : ExecuteMsg {
85
- Transfer : & TransferExecMsg {
91
+ contractMsg : executeMsg {
92
+ Transfer : & transferExecMsg {
86
93
ChannelID : path .EndpointA .ChannelID ,
87
94
TimeoutSeconds : 100 ,
88
95
},
89
96
},
90
97
expAck : true ,
91
98
},
92
99
"timeout" : {
93
- contractMsg : ExecuteMsg {
94
- Transfer : & TransferExecMsg {
100
+ contractMsg : executeMsg {
101
+ Transfer : & transferExecMsg {
95
102
ChannelID : path .EndpointA .ChannelID ,
96
103
TimeoutSeconds : 1 ,
97
104
},
@@ -128,17 +135,29 @@ func TestIBCCallbacks(t *testing.T) {
128
135
wasmibctesting .RelayAndAckPendingPackets (path )
129
136
130
137
// then the contract on chain B should receive a receive callback
131
- var response QueryResp
132
- chainB .SmartQuery (contractAddrB .String (), QueryMsg {CallbackStats : struct {}{}}, & response )
138
+ var response queryResp
139
+ chainB .SmartQuery (contractAddrB .String (), queryMsg {CallbackStats : struct {}{}}, & response )
133
140
assert .Empty (t , response .IBCAckCallbacks )
134
141
assert .Empty (t , response .IBCTimeoutCallbacks )
135
142
assert .Len (t , response .IBCDestinationCallbacks , 1 )
136
143
137
144
// and the receive callback should contain the ack
138
145
assert .Equal (t , []byte ("{\" result\" :\" AQ==\" }" ), response .IBCDestinationCallbacks [0 ].Ack .Data )
146
+ assert .Equal (t ,
147
+ wasmvmtypes.Array [wasmvmtypes.Coin ]{wasmvmtypes .NewCoin (1 , ibcDenom )},
148
+ response .IBCDestinationCallbacks [0 ].Transfer .Funds ,
149
+ )
150
+ assert .Equal (t , contractAddrA .String (), response .IBCDestinationCallbacks [0 ].Transfer .Receiver )
151
+
152
+ balances := chainB .GetWasmApp ().BankKeeper .GetAllBalances (chainB .GetContext (), contractAddrB )
153
+ // sanity check that the balance of the contract is correct
154
+ require .Equal (t , sdk .NewCoins (
155
+ sdk .NewCoin (sdk .DefaultBondDenom , sdkmath .NewInt (100 )),
156
+ ibcCoin ,
157
+ ), balances )
139
158
140
159
// and the contract on chain A should receive a callback with the ack
141
- chainA .SmartQuery (contractAddrA .String (), QueryMsg {CallbackStats : struct {}{}}, & response )
160
+ chainA .SmartQuery (contractAddrA .String (), queryMsg {CallbackStats : struct {}{}}, & response )
142
161
assert .Len (t , response .IBCAckCallbacks , 1 )
143
162
assert .Empty (t , response .IBCTimeoutCallbacks )
144
163
assert .Empty (t , response .IBCDestinationCallbacks )
@@ -150,14 +169,14 @@ func TestIBCCallbacks(t *testing.T) {
150
169
require .NoError (t , wasmibctesting .TimeoutPendingPackets (coord , path ))
151
170
152
171
// then the contract on chain B should not receive anything
153
- var response QueryResp
154
- chainB .SmartQuery (contractAddrB .String (), QueryMsg {CallbackStats : struct {}{}}, & response )
172
+ var response queryResp
173
+ chainB .SmartQuery (contractAddrB .String (), queryMsg {CallbackStats : struct {}{}}, & response )
155
174
assert .Empty (t , response .IBCAckCallbacks )
156
175
assert .Empty (t , response .IBCTimeoutCallbacks )
157
176
assert .Empty (t , response .IBCDestinationCallbacks )
158
177
159
178
// and the contract on chain A should receive a callback with the timeout result
160
- chainA .SmartQuery (contractAddrA .String (), QueryMsg {CallbackStats : struct {}{}}, & response )
179
+ chainA .SmartQuery (contractAddrA .String (), queryMsg {CallbackStats : struct {}{}}, & response )
161
180
assert .Empty (t , response .IBCAckCallbacks )
162
181
assert .Len (t , response .IBCTimeoutCallbacks , 1 )
163
182
assert .Empty (t , response .IBCDestinationCallbacks )
@@ -166,6 +185,80 @@ func TestIBCCallbacks(t *testing.T) {
166
185
}
167
186
}
168
187
188
+ func TestIBCDestinationCallbackTransfer (t * testing.T ) {
189
+ // scenario:
190
+ // given two chains
191
+ // with an ics-20 channel established
192
+ // and an ibc-callbacks contract deployed on chain A
193
+ // when someone sends an ibc transfer to chain B and back to the contract on A
194
+ // then the contract on A should receive a destination chain callback with correct transfer info
195
+
196
+ coord := wasmibctesting .NewCoordinator (t , 2 )
197
+ chainA := wasmibctesting .NewWasmTestChain (coord .GetChain (ibctesting .GetChainID (1 )))
198
+ chainB := wasmibctesting .NewWasmTestChain (coord .GetChain (ibctesting .GetChainID (2 )))
199
+
200
+ actorChainA := sdk .AccAddress (chainA .SenderPrivKey .PubKey ().Address ())
201
+ actorChainB := sdk .AccAddress (chainB .SenderPrivKey .PubKey ().Address ())
202
+
203
+ path := wasmibctesting .NewWasmPath (chainA , chainB )
204
+ path .EndpointA .ChannelConfig = & ibctesting.ChannelConfig {
205
+ PortID : ibctransfertypes .PortID ,
206
+ Version : ibctransfertypes .V1 ,
207
+ Order : channeltypes .UNORDERED ,
208
+ }
209
+ path .EndpointB .ChannelConfig = & ibctesting.ChannelConfig {
210
+ PortID : ibctransfertypes .PortID ,
211
+ Version : ibctransfertypes .V1 ,
212
+ Order : channeltypes .UNORDERED ,
213
+ }
214
+ // with an ics-20 transfer channel setup between both chains
215
+ coord .Setup (& path .Path )
216
+
217
+ oneToken := sdk .NewCoins (sdk .NewCoin (sdk .DefaultBondDenom , sdkmath .NewInt (1 )))
218
+ ibcDenom := ibctransfertypes .NewDenom (
219
+ sdk .DefaultBondDenom ,
220
+ ibctransfertypes .NewHop (path .EndpointB .ChannelConfig .PortID , path .EndpointB .ChannelID ),
221
+ ).IBCDenom ()
222
+ ibcCoin := sdk .NewCoin (ibcDenom , sdkmath .NewInt (1 ))
223
+
224
+ // with an ibc-callbacks contract deployed on chain A
225
+ codeID := chainA .StoreCodeFile ("./testdata/ibc_callbacks.wasm" ).CodeID
226
+
227
+ contractAddr := chainA .InstantiateContract (codeID , []byte (`{}` ))
228
+ require .NotEmpty (t , contractAddr )
229
+
230
+ // when someone sends an ibc transfer to chain B
231
+ chainA .SendMsgs (
232
+ ibctransfertypes .NewMsgTransfer (path .EndpointA .ChannelConfig .PortID , path .EndpointA .ChannelID ,
233
+ oneToken [0 ], actorChainA .String (), actorChainB .String (), chainA .GetTimeoutHeight (), 0 , "" ),
234
+ )
235
+
236
+ wasmibctesting .RelayAndAckPendingPackets (path )
237
+
238
+ // and it is transferred back to the contract on A
239
+ chainB .SendMsgs (
240
+ ibctransfertypes .NewMsgTransfer (path .EndpointB .ChannelConfig .PortID , path .EndpointB .ChannelID ,
241
+ ibcCoin , actorChainB .String (), contractAddr .String (), chainB .GetTimeoutHeight (), 0 ,
242
+ fmt .Sprintf (`{"dest_callback":{"address":"%s"}}` , contractAddr .String ())),
243
+ )
244
+ wasmibctesting .RelayAndAckPendingPackets (path )
245
+
246
+ // then the contract on A should receive a destination chain callback with correct funds
247
+ var response queryResp
248
+ chainA .SmartQuery (contractAddr .String (), queryMsg {CallbackStats : struct {}{}}, & response )
249
+ assert .Empty (t , response .IBCAckCallbacks )
250
+ assert .Empty (t , response .IBCTimeoutCallbacks )
251
+ assert .Len (t , response .IBCDestinationCallbacks , 1 )
252
+ assert .Equal (t , []byte ("{\" result\" :\" AQ==\" }" ), response .IBCDestinationCallbacks [0 ].Ack .Data )
253
+ // the denom should be reversed back correctly to the original denom
254
+ assert .Equal (t ,
255
+ wasmvmtypes.Array [wasmvmtypes.Coin ]{wasmvmtypes .NewCoin (1 , sdk .DefaultBondDenom )},
256
+ response .IBCDestinationCallbacks [0 ].Transfer .Funds ,
257
+ )
258
+ assert .Equal (t , contractAddr .String (), response .IBCDestinationCallbacks [0 ].Transfer .Receiver )
259
+ assert .Equal (t , actorChainB .String (), response .IBCDestinationCallbacks [0 ].Transfer .Sender )
260
+ }
261
+
169
262
func TestIBCCallbacksWithoutEntrypoints (t * testing.T ) {
170
263
// scenario:
171
264
// given two chains
0 commit comments