@@ -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,28 @@ 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 ].Funds ,
149
+ )
150
+
151
+ balances := chainB .GetWasmApp ().BankKeeper .GetAllBalances (chainB .GetContext (), contractAddrB )
152
+ // sanity check that the balance of the contract is correct
153
+ require .Equal (t , sdk .NewCoins (
154
+ sdk .NewCoin (sdk .DefaultBondDenom , sdkmath .NewInt (100 )),
155
+ ibcCoin ,
156
+ ), balances )
139
157
140
158
// and the contract on chain A should receive a callback with the ack
141
- chainA .SmartQuery (contractAddrA .String (), QueryMsg {CallbackStats : struct {}{}}, & response )
159
+ chainA .SmartQuery (contractAddrA .String (), queryMsg {CallbackStats : struct {}{}}, & response )
142
160
assert .Len (t , response .IBCAckCallbacks , 1 )
143
161
assert .Empty (t , response .IBCTimeoutCallbacks )
144
162
assert .Empty (t , response .IBCDestinationCallbacks )
@@ -150,14 +168,14 @@ func TestIBCCallbacks(t *testing.T) {
150
168
require .NoError (t , wasmibctesting .TimeoutPendingPackets (coord , path ))
151
169
152
170
// then the contract on chain B should not receive anything
153
- var response QueryResp
154
- chainB .SmartQuery (contractAddrB .String (), QueryMsg {CallbackStats : struct {}{}}, & response )
171
+ var response queryResp
172
+ chainB .SmartQuery (contractAddrB .String (), queryMsg {CallbackStats : struct {}{}}, & response )
155
173
assert .Empty (t , response .IBCAckCallbacks )
156
174
assert .Empty (t , response .IBCTimeoutCallbacks )
157
175
assert .Empty (t , response .IBCDestinationCallbacks )
158
176
159
177
// and the contract on chain A should receive a callback with the timeout result
160
- chainA .SmartQuery (contractAddrA .String (), QueryMsg {CallbackStats : struct {}{}}, & response )
178
+ chainA .SmartQuery (contractAddrA .String (), queryMsg {CallbackStats : struct {}{}}, & response )
161
179
assert .Empty (t , response .IBCAckCallbacks )
162
180
assert .Len (t , response .IBCTimeoutCallbacks , 1 )
163
181
assert .Empty (t , response .IBCDestinationCallbacks )
@@ -166,6 +184,78 @@ func TestIBCCallbacks(t *testing.T) {
166
184
}
167
185
}
168
186
187
+ func TestIBCDestinationCallbackFunds (t * testing.T ) {
188
+ // scenario:
189
+ // given two chains
190
+ // with an ics-20 channel established
191
+ // and an ibc-callbacks contract deployed on chain A
192
+ // when someone sends an ibc transfer to chain B and back to the contract on A
193
+ // then the contract on A should receive a destination chain callback with correct funds
194
+
195
+ coord := wasmibctesting .NewCoordinator (t , 2 )
196
+ chainA := wasmibctesting .NewWasmTestChain (coord .GetChain (ibctesting .GetChainID (1 )))
197
+ chainB := wasmibctesting .NewWasmTestChain (coord .GetChain (ibctesting .GetChainID (2 )))
198
+
199
+ actorChainA := sdk .AccAddress (chainA .SenderPrivKey .PubKey ().Address ())
200
+ actorChainB := sdk .AccAddress (chainB .SenderPrivKey .PubKey ().Address ())
201
+
202
+ path := wasmibctesting .NewWasmPath (chainA , chainB )
203
+ path .EndpointA .ChannelConfig = & ibctesting.ChannelConfig {
204
+ PortID : ibctransfertypes .PortID ,
205
+ Version : ibctransfertypes .V1 ,
206
+ Order : channeltypes .UNORDERED ,
207
+ }
208
+ path .EndpointB .ChannelConfig = & ibctesting.ChannelConfig {
209
+ PortID : ibctransfertypes .PortID ,
210
+ Version : ibctransfertypes .V1 ,
211
+ Order : channeltypes .UNORDERED ,
212
+ }
213
+ // with an ics-20 transfer channel setup between both chains
214
+ coord .Setup (& path .Path )
215
+
216
+ oneToken := sdk .NewCoins (sdk .NewCoin (sdk .DefaultBondDenom , sdkmath .NewInt (1 )))
217
+ ibcDenom := ibctransfertypes .NewDenom (
218
+ sdk .DefaultBondDenom ,
219
+ ibctransfertypes .NewHop (path .EndpointB .ChannelConfig .PortID , path .EndpointB .ChannelID ),
220
+ ).IBCDenom ()
221
+ ibcCoin := sdk .NewCoin (ibcDenom , sdkmath .NewInt (1 ))
222
+
223
+ // with an ibc-callbacks contract deployed on chain A
224
+ codeID := chainA .StoreCodeFile ("./testdata/ibc_callbacks.wasm" ).CodeID
225
+
226
+ contractAddr := chainA .InstantiateContract (codeID , []byte (`{}` ))
227
+ require .NotEmpty (t , contractAddr )
228
+
229
+ // when someone sends an ibc transfer to chain B and back to the contract on A
230
+ chainA .SendMsgs (
231
+ ibctransfertypes .NewMsgTransfer (path .EndpointA .ChannelConfig .PortID , path .EndpointA .ChannelID ,
232
+ oneToken [0 ], actorChainA .String (), actorChainB .String (), chainA .GetTimeoutHeight (), 0 , "" ),
233
+ )
234
+
235
+ wasmibctesting .RelayAndAckPendingPackets (path )
236
+
237
+ // and it is transferred back to the contract on A
238
+ chainB .SendMsgs (
239
+ ibctransfertypes .NewMsgTransfer (path .EndpointB .ChannelConfig .PortID , path .EndpointB .ChannelID ,
240
+ ibcCoin , actorChainB .String (), contractAddr .String (), chainB .GetTimeoutHeight (), 0 ,
241
+ fmt .Sprintf (`{"dest_callback":{"address":"%s"}}` , contractAddr .String ())),
242
+ )
243
+ wasmibctesting .RelayAndAckPendingPackets (path )
244
+
245
+ // then the contract on A should receive a destination chain callback with correct funds
246
+ var response queryResp
247
+ chainA .SmartQuery (contractAddr .String (), queryMsg {CallbackStats : struct {}{}}, & response )
248
+ assert .Empty (t , response .IBCAckCallbacks )
249
+ assert .Empty (t , response .IBCTimeoutCallbacks )
250
+ assert .Len (t , response .IBCDestinationCallbacks , 1 )
251
+ assert .Equal (t , []byte ("{\" result\" :\" AQ==\" }" ), response .IBCDestinationCallbacks [0 ].Ack .Data )
252
+ // the denom should be reversed back correctly to the original denom
253
+ assert .Equal (t ,
254
+ wasmvmtypes.Array [wasmvmtypes.Coin ]{wasmvmtypes .NewCoin (1 , sdk .DefaultBondDenom )},
255
+ response .IBCDestinationCallbacks [0 ].Funds ,
256
+ )
257
+ }
258
+
169
259
func TestIBCCallbacksWithoutEntrypoints (t * testing.T ) {
170
260
// scenario:
171
261
// given two chains
0 commit comments