@@ -17,37 +17,49 @@ import (
17
17
"github.com/smartcontractkit/chainlink/deployment"
18
18
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
19
19
"github.com/smartcontractkit/chainlink/integration-tests/testsetups"
20
-
21
20
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
21
+ "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp"
22
22
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
23
23
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677"
24
24
"github.com/smartcontractkit/chainlink/v2/core/logger"
25
25
)
26
26
27
+ /*
28
+ * Chain topology for this test
29
+ * chainA (USDC, MY_TOKEN)
30
+ * |
31
+ * | ------- chainC (USDC, MY_TOKEN)
32
+ * |
33
+ * chainB (USDC)
34
+ */
27
35
func TestUSDCTokenTransfer (t * testing.T ) {
28
36
lggr := logger .TestLogger (t )
29
37
config := & changeset.TestConfigs {
30
38
IsUSDC : true ,
31
39
}
32
40
tenv , _ , _ := testsetups .NewLocalDevEnvironmentWithDefaultPrice (t , lggr , config )
33
- //tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, 2 , 4, config)
41
+ //tenv := changeset.NewMemoryEnvironmentWithJobsAndContracts(t, lggr, 3 , 4, config)
34
42
35
43
e := tenv .Env
36
44
state , err := changeset .LoadOnchainState (e )
37
45
require .NoError (t , err )
38
46
39
47
allChainSelectors := maps .Keys (e .Chains )
40
- sourceChain := allChainSelectors [0 ]
41
- destChain := allChainSelectors [1 ]
48
+ chainA := allChainSelectors [0 ]
49
+ chainC := allChainSelectors [1 ]
50
+ chainB := allChainSelectors [2 ]
51
+
52
+ aChainUSDC , cChainUSDC , err := changeset .ConfigureUSDCTokenPools (lggr , e .Chains , chainA , chainC , state )
53
+ require .NoError (t , err )
42
54
43
- srcUSDC , dstUSDC , err := changeset .ConfigureUSDCTokenPools (lggr , e .Chains , sourceChain , destChain , state )
55
+ bChainUSDC , _ , err := changeset .ConfigureUSDCTokenPools (lggr , e .Chains , chainB , chainC , state )
44
56
require .NoError (t , err )
45
57
46
- srcToken , _ , dstToken , _ , err := changeset .DeployTransferableToken (
58
+ aChainToken , _ , cChainToken , _ , err := changeset .DeployTransferableToken (
47
59
lggr ,
48
60
tenv .Env .Chains ,
49
- sourceChain ,
50
- destChain ,
61
+ chainA ,
62
+ chainC ,
51
63
state ,
52
64
e .ExistingAddresses ,
53
65
"MY_TOKEN" ,
@@ -58,97 +70,106 @@ func TestUSDCTokenTransfer(t *testing.T) {
58
70
require .NoError (t , changeset .AddLanesForAll (e , state ))
59
71
60
72
mintAndAllow (t , e , state , map [uint64 ][]* burn_mint_erc677.BurnMintERC677 {
61
- sourceChain : {srcUSDC , srcToken },
62
- destChain : {dstUSDC , dstToken },
73
+ chainA : {aChainUSDC , aChainToken },
74
+ chainB : {bChainUSDC },
75
+ chainC : {cChainUSDC , cChainToken },
63
76
})
64
77
65
- err = changeset .UpdateFeeQuoterForUSDC (lggr , e .Chains [sourceChain ], state .Chains [sourceChain ], destChain , srcUSDC )
78
+ err = changeset .UpdateFeeQuoterForUSDC (lggr , e .Chains [chainA ], state .Chains [chainA ], chainC , aChainUSDC )
66
79
require .NoError (t , err )
67
80
68
- err = changeset .UpdateFeeQuoterForUSDC (lggr , e .Chains [destChain ], state .Chains [destChain ], sourceChain , dstUSDC )
81
+ err = changeset .UpdateFeeQuoterForUSDC (lggr , e .Chains [chainB ], state .Chains [chainB ], chainC , bChainUSDC )
82
+ require .NoError (t , err )
83
+
84
+ err = changeset .UpdateFeeQuoterForUSDC (lggr , e .Chains [chainC ], state .Chains [chainC ], chainA , cChainUSDC )
69
85
require .NoError (t , err )
70
86
71
87
// MockE2EUSDCTransmitter always mint 1, see MockE2EUSDCTransmitter.sol for more details
72
88
tinyOneCoin := new (big.Int ).SetUint64 (1 )
73
89
74
90
tcs := []struct {
75
- name string
76
- receiver common.Address
77
- sourceChain uint64
78
- destChain uint64
79
- tokens []router.ClientEVMTokenAmount
80
- data []byte
81
- expectedTokenBalances map [common.Address ]* big.Int
91
+ name string
92
+ receiver common.Address
93
+ sourceChain uint64
94
+ destChain uint64
95
+ tokens []router.ClientEVMTokenAmount
96
+ data []byte
97
+ expectedTokenBalances map [common.Address ]* big.Int
98
+ expectedExecutionState int
82
99
}{
83
100
{
84
101
name : "single USDC token transfer to EOA" ,
85
102
receiver : utils .RandomAddress (),
86
- sourceChain : destChain ,
87
- destChain : sourceChain ,
103
+ sourceChain : chainC ,
104
+ destChain : chainA ,
88
105
tokens : []router.ClientEVMTokenAmount {
89
106
{
90
- Token : dstUSDC .Address (),
107
+ Token : cChainUSDC .Address (),
91
108
Amount : tinyOneCoin ,
92
109
}},
93
110
expectedTokenBalances : map [common.Address ]* big.Int {
94
- srcUSDC .Address (): tinyOneCoin ,
111
+ aChainUSDC .Address (): tinyOneCoin ,
95
112
},
113
+ expectedExecutionState : changeset .EXECUTION_STATE_SUCCESS ,
96
114
},
97
115
{
98
116
name : "multiple USDC tokens within the same message" ,
99
117
receiver : utils .RandomAddress (),
100
- sourceChain : destChain ,
101
- destChain : sourceChain ,
118
+ sourceChain : chainC ,
119
+ destChain : chainA ,
102
120
tokens : []router.ClientEVMTokenAmount {
103
121
{
104
- Token : dstUSDC .Address (),
122
+ Token : cChainUSDC .Address (),
105
123
Amount : tinyOneCoin ,
106
124
},
107
125
{
108
- Token : dstUSDC .Address (),
126
+ Token : cChainUSDC .Address (),
109
127
Amount : tinyOneCoin ,
110
128
},
111
129
},
112
130
expectedTokenBalances : map [common.Address ]* big.Int {
113
131
// 2 coins because of the same receiver
114
- srcUSDC .Address (): new (big.Int ).Add (tinyOneCoin , tinyOneCoin ),
132
+ aChainUSDC .Address (): new (big.Int ).Add (tinyOneCoin , tinyOneCoin ),
115
133
},
134
+ expectedExecutionState : changeset .EXECUTION_STATE_SUCCESS ,
116
135
},
117
136
{
118
137
name : "USDC token together with another token transferred to EOA" ,
119
138
receiver : utils .RandomAddress (),
120
- sourceChain : sourceChain ,
121
- destChain : destChain ,
139
+ sourceChain : chainA ,
140
+ destChain : chainC ,
122
141
tokens : []router.ClientEVMTokenAmount {
123
142
{
124
- Token : srcUSDC .Address (),
143
+ Token : aChainUSDC .Address (),
125
144
Amount : tinyOneCoin ,
126
145
},
127
146
{
128
- Token : srcToken .Address (),
147
+ Token : aChainToken .Address (),
129
148
Amount : new (big.Int ).Mul (tinyOneCoin , big .NewInt (10 )),
130
149
},
131
150
},
132
151
expectedTokenBalances : map [common.Address ]* big.Int {
133
- dstUSDC .Address (): tinyOneCoin ,
134
- dstToken .Address (): new (big.Int ).Mul (tinyOneCoin , big .NewInt (10 )),
152
+ cChainUSDC .Address (): tinyOneCoin ,
153
+ cChainToken .Address (): new (big.Int ).Mul (tinyOneCoin , big .NewInt (10 )),
135
154
},
155
+ expectedExecutionState : changeset .EXECUTION_STATE_SUCCESS ,
136
156
},
137
157
{
138
158
name : "programmable token transfer to valid contract receiver" ,
139
- receiver : state .Chains [destChain ].Receiver .Address (),
140
- sourceChain : sourceChain ,
141
- destChain : destChain ,
159
+ receiver : state .Chains [chainC ].Receiver .Address (),
160
+ sourceChain : chainA ,
161
+ destChain : chainC ,
142
162
tokens : []router.ClientEVMTokenAmount {
143
163
{
144
- Token : srcUSDC .Address (),
164
+ Token : aChainUSDC .Address (),
145
165
Amount : tinyOneCoin ,
146
166
},
147
167
},
148
168
data : []byte ("hello world" ),
149
169
expectedTokenBalances : map [common.Address ]* big.Int {
150
- dstUSDC .Address (): tinyOneCoin ,
170
+ cChainUSDC .Address (): tinyOneCoin ,
151
171
},
172
+ expectedExecutionState : changeset .EXECUTION_STATE_SUCCESS ,
152
173
},
153
174
}
154
175
@@ -169,6 +190,7 @@ func TestUSDCTokenTransfer(t *testing.T) {
169
190
tt .tokens ,
170
191
tt .receiver ,
171
192
tt .data ,
193
+ tt .expectedExecutionState ,
172
194
)
173
195
174
196
for token , balance := range tt .expectedTokenBalances {
@@ -177,6 +199,52 @@ func TestUSDCTokenTransfer(t *testing.T) {
177
199
}
178
200
})
179
201
}
202
+
203
+ t .Run ("multi-source USDC transfer targeting the same dest receiver" , func (t * testing.T ) {
204
+ sendSingleTokenTransfer := func (source , dest uint64 , token common.Address , receiver common.Address ) (* onramp.OnRampCCIPMessageSent , changeset.SourceDestPair ) {
205
+ msg := changeset .TestSendRequest (t , e , state , source , dest , false , router.ClientEVM2AnyMessage {
206
+ Receiver : common .LeftPadBytes (receiver .Bytes (), 32 ),
207
+ Data : []byte {},
208
+ TokenAmounts : []router.ClientEVMTokenAmount {{Token : token , Amount : tinyOneCoin }},
209
+ FeeToken : common .HexToAddress ("0x0" ),
210
+ ExtraArgs : nil ,
211
+ })
212
+ return msg , changeset.SourceDestPair {
213
+ SourceChainSelector : source ,
214
+ DestChainSelector : dest ,
215
+ }
216
+ }
217
+
218
+ receiver := utils .RandomAddress ()
219
+
220
+ startBlocks := make (map [uint64 ]* uint64 )
221
+ expectedSeqNum := make (map [changeset.SourceDestPair ]uint64 )
222
+ expectedSeqNumExec := make (map [changeset.SourceDestPair ][]uint64 )
223
+
224
+ latesthdr , err := e .Chains [chainC ].Client .HeaderByNumber (testcontext .Get (t ), nil )
225
+ require .NoError (t , err )
226
+ block := latesthdr .Number .Uint64 ()
227
+ startBlocks [chainC ] = & block
228
+
229
+ message1 , message1ID := sendSingleTokenTransfer (chainA , chainC , aChainUSDC .Address (), receiver )
230
+ expectedSeqNum [message1ID ] = message1 .SequenceNumber
231
+ expectedSeqNumExec [message1ID ] = []uint64 {message1 .SequenceNumber }
232
+
233
+ message2 , message2ID := sendSingleTokenTransfer (chainB , chainC , bChainUSDC .Address (), receiver )
234
+ expectedSeqNum [message2ID ] = message2 .SequenceNumber
235
+ expectedSeqNumExec [message2ID ] = []uint64 {message2 .SequenceNumber }
236
+
237
+ changeset .ConfirmCommitForAllWithExpectedSeqNums (t , e , state , expectedSeqNum , startBlocks )
238
+ states := changeset .ConfirmExecWithSeqNrsForAll (t , e , state , expectedSeqNumExec , startBlocks )
239
+
240
+ require .Equal (t , changeset .EXECUTION_STATE_SUCCESS , states [message1ID ][message1.SequenceNumber ])
241
+ require .Equal (t , changeset .EXECUTION_STATE_SUCCESS , states [message2ID ][message2.SequenceNumber ])
242
+
243
+ // We sent 1 coin from each source chain, so we should have 2 coins on the destination chain
244
+ // Receiver is randomly generated so we don't need to get the initial balance first
245
+ expectedBalance := new (big.Int ).Add (tinyOneCoin , tinyOneCoin )
246
+ waitForTheTokenBalance (t , cChainUSDC .Address (), receiver , e .Chains [chainC ], expectedBalance )
247
+ })
180
248
}
181
249
182
250
// mintAndAllow mints tokens for deployers and allow router to spend them
@@ -216,7 +284,13 @@ func transferAndWaitForSuccess(
216
284
tokens []router.ClientEVMTokenAmount ,
217
285
receiver common.Address ,
218
286
data []byte ,
287
+ expectedStatus int ,
219
288
) {
289
+ identifier := changeset.SourceDestPair {
290
+ SourceChainSelector : sourceChain ,
291
+ DestChainSelector : destChain ,
292
+ }
293
+
220
294
startBlocks := make (map [uint64 ]* uint64 )
221
295
expectedSeqNum := make (map [changeset.SourceDestPair ]uint64 )
222
296
expectedSeqNumExec := make (map [changeset.SourceDestPair ][]uint64 )
@@ -233,20 +307,15 @@ func transferAndWaitForSuccess(
233
307
FeeToken : common .HexToAddress ("0x0" ),
234
308
ExtraArgs : nil ,
235
309
})
236
- expectedSeqNum [changeset.SourceDestPair {
237
- SourceChainSelector : sourceChain ,
238
- DestChainSelector : destChain ,
239
- }] = msgSentEvent .SequenceNumber
240
- expectedSeqNumExec [changeset.SourceDestPair {
241
- SourceChainSelector : sourceChain ,
242
- DestChainSelector : destChain ,
243
- }] = []uint64 {msgSentEvent .SequenceNumber }
310
+ expectedSeqNum [identifier ] = msgSentEvent .SequenceNumber
311
+ expectedSeqNumExec [identifier ] = []uint64 {msgSentEvent .SequenceNumber }
244
312
245
313
// Wait for all commit reports to land.
246
314
changeset .ConfirmCommitForAllWithExpectedSeqNums (t , env , state , expectedSeqNum , startBlocks )
247
315
248
316
// Wait for all exec reports to land
249
- changeset .ConfirmExecWithSeqNrsForAll (t , env , state , expectedSeqNumExec , startBlocks )
317
+ states := changeset .ConfirmExecWithSeqNrsForAll (t , env , state , expectedSeqNumExec , startBlocks )
318
+ require .Equal (t , expectedStatus , states [identifier ][msgSentEvent.SequenceNumber ])
250
319
}
251
320
252
321
func waitForTheTokenBalance (
0 commit comments