Skip to content

Commit 812cae8

Browse files
fix(operations): preserve numeric precision in typeReport (#738)
This was raised as part of a support request - https://chainlink-core.slack.com/archives/C08QF1BEW4T/p1771021085387719 ## What changed - Updated `typeReport` to decode intermediate JSON using `UseNumber` so numeric values that pass through `any` are not coerced to `float64`. - Added tests ## Why - Without `UseNumber`, large integers (for example values originating from `big.Int`) lose precision when decoded through `any` because they become `float64`. - This fix ensures typed report reconstruction preserves numeric fidelity. Jira: https://smartcontract-it.atlassian.net/browse/OPT-416
1 parent d976134 commit 812cae8

File tree

3 files changed

+37
-3
lines changed

3 files changed

+37
-3
lines changed

.changeset/humble-pears-rule.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"chainlink-deployments-framework": patch
3+
---
4+
5+
fix(operations): preserve numeric precision in typeReport

operations/report.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package operations
22

33
import (
4+
"bytes"
45
"encoding/json"
56
"errors"
67
"fmt"
@@ -286,7 +287,7 @@ func typeReport[IN, OUT any](r Report[any, any]) (Report[IN, OUT], bool) {
286287
return Report[IN, OUT]{}, false
287288
}
288289
var input IN
289-
if err = json.Unmarshal(inputBytes, &input); err != nil {
290+
if err = decode(inputBytes, &input); err != nil {
290291
return Report[IN, OUT]{}, false
291292
}
292293

@@ -296,7 +297,7 @@ func typeReport[IN, OUT any](r Report[any, any]) (Report[IN, OUT], bool) {
296297
}
297298

298299
var output OUT
299-
if err := json.Unmarshal(outputBytes, &output); err != nil {
300+
if err := decode(outputBytes, &output); err != nil {
300301
return Report[IN, OUT]{}, false
301302
}
302303

@@ -311,3 +312,10 @@ func typeReport[IN, OUT any](r Report[any, any]) (Report[IN, OUT], bool) {
311312
ExecutionSeries: r.ExecutionSeries,
312313
}, true
313314
}
315+
316+
func decode(data []byte, out any) error {
317+
dec := json.NewDecoder(bytes.NewReader(data))
318+
dec.UseNumber()
319+
320+
return dec.Decode(out)
321+
}

operations/report_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package operations
33
import (
44
"encoding/json"
55
"errors"
6+
"math/big"
67
"testing"
78
"time"
89

@@ -137,7 +138,7 @@ func Test_typeReport(t *testing.T) {
137138
assert.True(t, ok)
138139
assert.Equal(t, "1", res.ID)
139140
assert.Equal(t, Definition{}, res.Def)
140-
assert.Equal(t, map[string]interface{}{"a": float64(1)}, res.Input)
141+
assert.Equal(t, map[string]interface{}{"a": json.Number("1")}, res.Input)
141142
assert.InEpsilon(t, float64(2), res.Output, 0)
142143
assert.Equal(t, &now, res.Timestamp)
143144
assert.Nil(t, res.Err)
@@ -157,6 +158,26 @@ func Test_typeReport(t *testing.T) {
157158
assert.False(t, ok)
158159
}
159160

161+
func Test_typeReport_PreservesBigIntInInputAndOutput(t *testing.T) {
162+
t.Parallel()
163+
164+
report := Report[any, any]{
165+
ID: "1",
166+
Def: Definition{},
167+
Input: map[string]interface{}{
168+
"a": big.NewInt(1000000000000000000),
169+
},
170+
Output: map[string]interface{}{
171+
"b": big.NewInt(2000000000000000000),
172+
},
173+
}
174+
175+
res, ok := typeReport[map[string]interface{}, map[string]interface{}](report)
176+
assert.True(t, ok)
177+
assert.Equal(t, map[string]interface{}{"a": json.Number("1000000000000000000")}, res.Input)
178+
assert.Equal(t, map[string]interface{}{"b": json.Number("2000000000000000000")}, res.Output)
179+
}
180+
160181
var reportJSON = `
161182
{
162183
"id" : "6c5d66ad-f1e8-45b6-b83b-4f289b04045f",

0 commit comments

Comments
 (0)