@@ -2,22 +2,16 @@ package evm
22
33import (
44 "fmt"
5- "math/big"
65 "testing"
76
8- "github.com/ethereum/go-ethereum/accounts/abi"
9- "github.com/ethereum/go-ethereum/common"
107 "github.com/leanovate/gopter"
118 "github.com/leanovate/gopter/gen"
12- "github.com/leanovate/gopter/prop"
139 "github.com/shopspring/decimal"
1410 "github.com/stretchr/testify/assert"
1511 "github.com/stretchr/testify/require"
1612
1713 "github.com/smartcontractkit/libocr/offchainreporting2/types"
1814
19- ubig "github.com/smartcontractkit/chainlink-data-streams/llo/reportcodecs/evm/utils"
20-
2115 llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo"
2216 "github.com/smartcontractkit/chainlink-data-streams/llo"
2317)
@@ -74,252 +68,7 @@ func TestReportCodecEVMABIEncodeUnpackedExpr_Encode(t *testing.T) {
7468 codec := ReportCodecEVMABIEncodeUnpackedExpr {}
7569 _ , err = codec .Encode (report , cd )
7670 require .Error (t , err )
77- assert .Contains (t , err .Error (), "ABI and values length mismatch" )
78- })
79-
80- t .Run ("DEX-based asset schema example" , func (t * testing.T ) {
81- expectedDEXBasedAssetSchema := abi .Arguments ([]abi.Argument {
82- {Name : "feedId" , Type : mustNewABIType ("bytes32" )},
83- {Name : "validFromTimestamp" , Type : mustNewABIType ("uint32" )},
84- {Name : "observationsTimestamp" , Type : mustNewABIType ("uint32" )},
85- {Name : "nativeFee" , Type : mustNewABIType ("uint192" )},
86- {Name : "linkFee" , Type : mustNewABIType ("uint192" )},
87- {Name : "expiresAt" , Type : mustNewABIType ("uint32" )},
88- {Name : "price" , Type : mustNewABIType ("int192" )},
89- {Name : "baseMarketDepth" , Type : mustNewABIType ("int192" )},
90- {Name : "quoteMarketDepth" , Type : mustNewABIType ("int192" )},
91- })
92-
93- properties := gopter .NewProperties (nil )
94-
95- runTest := func (sampleFeedID common.Hash , sampleObservationTimestampNanoseconds , sampleValidAfterNanoseconds uint64 , sampleExpirationWindow uint32 , priceMultiplier , marketDepthMultiplier * ubig.Big , sampleBaseUSDFee , sampleLinkBenchmarkPrice , sampleNativeBenchmarkPrice , sampleDexBasedAssetPrice , sampleBaseMarketDepth , sampleQuoteMarketDepth decimal.Decimal ) bool {
96- report := llo.Report {
97- ConfigDigest : types.ConfigDigest {0x01 },
98- SeqNr : 0x02 ,
99- ChannelID : llotypes .ChannelID (0x03 ),
100- ValidAfterNanoseconds : sampleValidAfterNanoseconds ,
101- ObservationTimestampNanoseconds : sampleObservationTimestampNanoseconds ,
102- Values : []llo.StreamValue {
103- & llo.Quote {Bid : decimal .NewFromFloat (6.1 ), Benchmark : sampleLinkBenchmarkPrice , Ask : decimal .NewFromFloat (8.2332 )}, // Link price
104- & llo.Quote {Bid : decimal .NewFromFloat (9.4 ), Benchmark : sampleNativeBenchmarkPrice , Ask : decimal .NewFromFloat (11.33 )}, // Native price
105- llo .ToDecimal (sampleDexBasedAssetPrice ), // DEX-based asset price
106- llo .ToDecimal (sampleBaseMarketDepth ), // Base market depth
107- llo .ToDecimal (sampleQuoteMarketDepth ), // Quote market depth
108- },
109- Specimen : false ,
110- }
111-
112- opts := ReportFormatEVMABIEncodeOpts {
113- BaseUSDFee : sampleBaseUSDFee ,
114- ExpirationWindow : sampleExpirationWindow ,
115- FeedID : sampleFeedID ,
116- TimestampPrecision : PrecisionSeconds ,
117- ABI : []ABIEncoder {
118- // benchmark price
119- newSingleABIEncoder ("int192" , priceMultiplier ),
120- // base market depth
121- newSingleABIEncoder ("int192" , marketDepthMultiplier ),
122- // quote market depth
123- newSingleABIEncoder ("int192" , marketDepthMultiplier ),
124- },
125- }
126- serializedOpts , err := opts .Encode ()
127- require .NoError (t , err )
128-
129- cd := llotypes.ChannelDefinition {
130- ReportFormat : llotypes .ReportFormatEVMABIEncodeUnpackedExpr ,
131- Streams : []llotypes.Stream {
132- {
133- Aggregator : llotypes .AggregatorMedian ,
134- },
135- {
136- Aggregator : llotypes .AggregatorMedian ,
137- },
138- {
139- Aggregator : llotypes .AggregatorQuote ,
140- },
141- {
142- Aggregator : llotypes .AggregatorMedian ,
143- },
144- {
145- Aggregator : llotypes .AggregatorMedian ,
146- },
147- },
148- Opts : serializedOpts ,
149- }
150-
151- codec := ReportCodecEVMABIEncodeUnpackedExpr {}
152- encoded , err := codec .Encode (report , cd )
153- require .NoError (t , err )
154-
155- values , err := expectedDEXBasedAssetSchema .Unpack (encoded )
156- require .NoError (t , err )
157-
158- require .Len (t , values , len (expectedDEXBasedAssetSchema ))
159-
160- // doesn't crash if values are nil
161- for i := range report .Values {
162- report .Values [i ] = nil
163- }
164- _ , err = codec .Encode (report , cd )
165- require .Error (t , err )
166-
167- return true
168- }
169-
170- properties .Property ("Encodes values" , prop .ForAll (
171- runTest ,
172- genFeedID (),
173- genObservationTimestampNanoseconds (),
174- genValidAfterNanoseconds (),
175- genExpirationWindow (),
176- genMultiplier (),
177- genMultiplier (),
178- genBaseUSDFee (),
179- genLinkBenchmarkPrice (),
180- genNativeBenchmarkPrice (),
181- genDexBasedAssetPrice (),
182- genMarketDepth (),
183- genMarketDepth (),
184- ))
185-
186- properties .TestingRun (t )
187- })
188-
189- t .Run ("varying timestamp precision schemas" , func (t * testing.T ) {
190- runTest := func (sampleFeedID common.Hash , sampleObservationTimestampNanoseconds , sampleValidAfterNanoseconds uint64 , sampleExpirationWindow uint32 , priceMultiplier , marketDepthMultiplier * ubig.Big , sampleBaseUSDFee , sampleLinkBenchmarkPrice , sampleNativeBenchmarkPrice , sampleDexBasedAssetPrice , sampleBaseMarketDepth , sampleQuoteMarketDepth decimal.Decimal , sampleTimestampPrecision TimestampPrecision ) bool {
191- // Determine timestamp type based on precision
192- timestampType := "uint64"
193- if sampleTimestampPrecision == PrecisionSeconds {
194- timestampType = "uint32"
195- }
196-
197- schema := abi .Arguments ([]abi.Argument {
198- {Name : "feedId" , Type : mustNewABIType ("bytes32" )},
199- {Name : "validFromTimestamp" , Type : mustNewABIType (timestampType )},
200- {Name : "observationsTimestamp" , Type : mustNewABIType (timestampType )},
201- {Name : "nativeFee" , Type : mustNewABIType ("uint192" )},
202- {Name : "linkFee" , Type : mustNewABIType ("uint192" )},
203- {Name : "expiresAt" , Type : mustNewABIType (timestampType )},
204- {Name : "price" , Type : mustNewABIType ("int192" )},
205- {Name : "baseMarketDepth" , Type : mustNewABIType ("int192" )},
206- {Name : "quoteMarketDepth" , Type : mustNewABIType ("int192" )},
207- })
208-
209- report := llo.Report {
210- ConfigDigest : types.ConfigDigest {0x01 },
211- SeqNr : 0x02 ,
212- ChannelID : llotypes .ChannelID (0x03 ),
213- ValidAfterNanoseconds : sampleValidAfterNanoseconds ,
214- ObservationTimestampNanoseconds : sampleObservationTimestampNanoseconds ,
215- Values : []llo.StreamValue {
216- & llo.Quote {Bid : decimal .NewFromFloat (9.4 ), Benchmark : sampleNativeBenchmarkPrice , Ask : decimal .NewFromFloat (11.33 )}, // Native price
217- & llo.Quote {Bid : decimal .NewFromFloat (6.1 ), Benchmark : sampleLinkBenchmarkPrice , Ask : decimal .NewFromFloat (8.2332 )}, // Link price
218- llo .ToDecimal (sampleDexBasedAssetPrice ), // DEX-based asset price
219- llo .ToDecimal (sampleBaseMarketDepth ), // Base market depth
220- llo .ToDecimal (sampleQuoteMarketDepth ), // Quote market depth
221- },
222- Specimen : false ,
223- }
224-
225- opts := ReportFormatEVMABIEncodeOpts {
226- BaseUSDFee : sampleBaseUSDFee ,
227- ExpirationWindow : sampleExpirationWindow ,
228- FeedID : sampleFeedID ,
229- TimestampPrecision : sampleTimestampPrecision ,
230- ABI : []ABIEncoder {
231- // benchmark price
232- newSingleABIEncoder ("int192" , priceMultiplier ),
233- // base market depth
234- newSingleABIEncoder ("int192" , marketDepthMultiplier ),
235- // quote market depth
236- newSingleABIEncoder ("int192" , marketDepthMultiplier ),
237- },
238- }
239- serializedOpts , err := opts .Encode ()
240- require .NoError (t , err )
241-
242- cd := llotypes.ChannelDefinition {
243- ReportFormat : llotypes .ReportFormatEVMABIEncodeUnpackedExpr ,
244- Streams : []llotypes.Stream {
245- {
246- Aggregator : llotypes .AggregatorMedian ,
247- },
248- {
249- Aggregator : llotypes .AggregatorMedian ,
250- },
251- {
252- Aggregator : llotypes .AggregatorQuote ,
253- },
254- {
255- Aggregator : llotypes .AggregatorMedian ,
256- },
257- {
258- Aggregator : llotypes .AggregatorMedian ,
259- },
260- },
261- Opts : serializedOpts ,
262- }
263-
264- codec := ReportCodecEVMABIEncodeUnpackedExpr {}
265- encoded , err := codec .Encode (report , cd )
266- require .NoError (t , err )
267-
268- values , err := schema .Unpack (encoded )
269- require .NoError (t , err )
270- require .Len (t , values , len (schema ))
271-
272- expectedLinkFee := CalculateFee (sampleLinkBenchmarkPrice , sampleBaseUSDFee )
273- expectedNativeFee := CalculateFee (sampleNativeBenchmarkPrice , sampleBaseUSDFee )
274-
275- checks := []bool {
276- assert .Equal (t , sampleFeedID , (common .Hash )(values [0 ].([32 ]byte ))), //nolint:testifylint // false positive
277- assert .Equal (t , sampleDexBasedAssetPrice .Mul (decimal .NewFromBigInt (priceMultiplier .ToInt (), 0 )).BigInt (), values [6 ].(* big.Int )),
278- assert .Equal (t , sampleBaseMarketDepth .Mul (decimal .NewFromBigInt (marketDepthMultiplier .ToInt (), 0 )).BigInt (), values [7 ].(* big.Int )),
279- assert .Equal (t , sampleQuoteMarketDepth .Mul (decimal .NewFromBigInt (marketDepthMultiplier .ToInt (), 0 )).BigInt (), values [8 ].(* big.Int )),
280- assert .Equal (t , expectedNativeFee .String (), values [3 ].(* big.Int ).String ()),
281- assert .Equal (t , expectedLinkFee .String (), values [4 ].(* big.Int ).String ()),
282- }
283-
284- // Verify timestamps per precision type
285- expectedValidFrom := ConvertTimestamp (sampleValidAfterNanoseconds , sampleTimestampPrecision ) + 1
286- expectedObservationTimestamp := ConvertTimestamp (sampleObservationTimestampNanoseconds , sampleTimestampPrecision )
287- expectedExpiresAt := expectedObservationTimestamp + uint64 (sampleExpirationWindow )
288- if timestampType == "uint32" {
289- checks = append (checks ,
290- assert .Equal (t , uint32 (expectedValidFrom ), values [1 ].(uint32 )),
291- assert .Equal (t , uint32 (expectedObservationTimestamp ), values [2 ].(uint32 )),
292- assert .Equal (t , uint32 (expectedExpiresAt ), values [5 ].(uint32 )),
293- )
294- } else {
295- checks = append (checks ,
296- assert .Equal (t , expectedValidFrom , values [1 ].(uint64 )),
297- assert .Equal (t , expectedObservationTimestamp , values [2 ].(uint64 )),
298- assert .Equal (t , expectedExpiresAt , values [5 ].(uint64 )),
299- )
300- }
301-
302- return AllTrue (checks )
303- }
304-
305- properties := gopter .NewProperties (nil )
306- properties .Property ("Encodes values" , prop .ForAll (
307- runTest ,
308- genFeedID (),
309- genObservationTimestampNanoseconds (),
310- genValidAfterNanoseconds (),
311- genExpirationWindow (),
312- genMultiplier (),
313- genMultiplier (),
314- genBaseUSDFee (),
315- genLinkBenchmarkPrice (),
316- genNativeBenchmarkPrice (),
317- genDexBasedAssetPrice (),
318- genMarketDepth (),
319- genMarketDepth (),
320- genTimestampPrecision (),
321- ))
322- properties .TestingRun (t )
71+ assert .Contains (t , err .Error (), "missing values for calculated streams" )
32372 })
32473}
32574
0 commit comments