From 672edb3378a2d77950916e396639303091cf015b Mon Sep 17 00:00:00 2001 From: yashnevatia Date: Mon, 16 Feb 2026 16:29:54 +0000 Subject: [PATCH 1/4] changing signing/hashing algo --- .../solana/anchor-go/generator/cre.go | 4 ++-- .../solana/testdata/data_storage/types.go | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/generate-bindings/solana/anchor-go/generator/cre.go b/cmd/generate-bindings/solana/anchor-go/generator/cre.go index b17c136b..0fbc0554 100644 --- a/cmd/generate-bindings/solana/anchor-go/generator/cre.go +++ b/cmd/generate-bindings/solana/anchor-go/generator/cre.go @@ -109,8 +109,8 @@ func creWriteReportFromStructs(exportedAccountName string, g *Generator) Code { Op("&").Qual(PkgPbSdk, "ReportRequest").Values(Dict{ Id("EncodedPayload"): Id("encodedFwdReport"), Id("EncoderName"): Lit("solana"), - Id("SigningAlgo"): Lit("ed25519"), - Id("HashingAlgo"): Lit("sha256"), + Id("SigningAlgo"): Lit("ecdsa"), + Id("HashingAlgo"): Lit("keccak256"), }), ).Line() diff --git a/cmd/generate-bindings/solana/testdata/data_storage/types.go b/cmd/generate-bindings/solana/testdata/data_storage/types.go index 29470c23..c39d8a90 100644 --- a/cmd/generate-bindings/solana/testdata/data_storage/types.go +++ b/cmd/generate-bindings/solana/testdata/data_storage/types.go @@ -103,8 +103,8 @@ func (c *DataStorage) WriteReportFromAccessLogged( promise := runtime.GenerateReport(&sdk.ReportRequest{ EncodedPayload: encodedFwdReport, EncoderName: "solana", - HashingAlgo: "sha256", - SigningAlgo: "ed25519", + HashingAlgo: "keccak256", + SigningAlgo: "ecdsa", }) return cre.ThenPromise(promise, func(report *cre.Report) cre.Promise[*solana.WriteReportReply] { @@ -215,8 +215,8 @@ func (c *DataStorage) WriteReportFromDataAccount( promise := runtime.GenerateReport(&sdk.ReportRequest{ EncodedPayload: encodedFwdReport, EncoderName: "solana", - HashingAlgo: "sha256", - SigningAlgo: "ed25519", + HashingAlgo: "keccak256", + SigningAlgo: "ecdsa", }) return cre.ThenPromise(promise, func(report *cre.Report) cre.Promise[*solana.WriteReportReply] { @@ -349,8 +349,8 @@ func (c *DataStorage) WriteReportFromDynamicEvent( promise := runtime.GenerateReport(&sdk.ReportRequest{ EncodedPayload: encodedFwdReport, EncoderName: "solana", - HashingAlgo: "sha256", - SigningAlgo: "ed25519", + HashingAlgo: "keccak256", + SigningAlgo: "ecdsa", }) return cre.ThenPromise(promise, func(report *cre.Report) cre.Promise[*solana.WriteReportReply] { @@ -427,8 +427,8 @@ func (c *DataStorage) WriteReportFromNoFields( promise := runtime.GenerateReport(&sdk.ReportRequest{ EncodedPayload: encodedFwdReport, EncoderName: "solana", - HashingAlgo: "sha256", - SigningAlgo: "ed25519", + HashingAlgo: "keccak256", + SigningAlgo: "ecdsa", }) return cre.ThenPromise(promise, func(report *cre.Report) cre.Promise[*solana.WriteReportReply] { @@ -528,8 +528,8 @@ func (c *DataStorage) WriteReportFromUpdateReserves( promise := runtime.GenerateReport(&sdk.ReportRequest{ EncodedPayload: encodedFwdReport, EncoderName: "solana", - HashingAlgo: "sha256", - SigningAlgo: "ed25519", + HashingAlgo: "keccak256", + SigningAlgo: "ecdsa", }) return cre.ThenPromise(promise, func(report *cre.Report) cre.Promise[*solana.WriteReportReply] { @@ -629,8 +629,8 @@ func (c *DataStorage) WriteReportFromUserData( promise := runtime.GenerateReport(&sdk.ReportRequest{ EncodedPayload: encodedFwdReport, EncoderName: "solana", - HashingAlgo: "sha256", - SigningAlgo: "ed25519", + HashingAlgo: "keccak256", + SigningAlgo: "ecdsa", }) return cre.ThenPromise(promise, func(report *cre.Report) cre.Promise[*solana.WriteReportReply] { From 420df08d67925e4f97e01359a0cb3b1f3e1a61e7 Mon Sep 17 00:00:00 2001 From: Vladimir Shchukin Date: Fri, 3 Apr 2026 03:18:01 -0400 Subject: [PATCH 2/4] add vec len prevalidation --- .../solana/anchor-go/generator/unmarshal.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmd/generate-bindings/solana/anchor-go/generator/unmarshal.go b/cmd/generate-bindings/solana/anchor-go/generator/unmarshal.go index 5e7b6a35..da0addba 100644 --- a/cmd/generate-bindings/solana/anchor-go/generator/unmarshal.go +++ b/cmd/generate-bindings/solana/anchor-go/generator/unmarshal.go @@ -300,6 +300,18 @@ func gen_unmarshal_DefinedFieldsNamed( ), ), ) + argBody.If(Id("vecLen").Op(">").Id("decoder").Dot("Remaining").Call()).Block( + Return( + Qual(PkgAnchorGoErrors, "NewField").Call( + Lit(exportedArgName), + Qual("fmt", "Errorf").Call( + Lit("vector length %d exceeds remaining decoder bytes %d"), + Id("vecLen"), + Id("decoder").Dot("Remaining").Call(), + ), + ), + ), + ) // Create the vector: argBody.Id("obj").Dot(exportedArgName).Op("=").Make(Index().Id(enumTypeName), Id("vecLen")) // Read the vector items: From a5118d39d31e2ae7d8ba60909633fc59a0dbe790 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 26 May 2026 23:58:41 -0400 Subject: [PATCH 3/4] Ozep/fix u256 dispatch (#347) add u256/i256 explicit handlers --- .../solana/anchor-go/generator/accounts.go | 4 + .../solana/anchor-go/generator/constants.go | 70 ++++++--- .../solana/anchor-go/generator/u256_test.go | 148 ++++++++++++++++++ 3 files changed, 203 insertions(+), 19 deletions(-) create mode 100644 cmd/generate-bindings/solana/anchor-go/generator/u256_test.go diff --git a/cmd/generate-bindings/solana/anchor-go/generator/accounts.go b/cmd/generate-bindings/solana/anchor-go/generator/accounts.go index efadafe5..4edfdea8 100644 --- a/cmd/generate-bindings/solana/anchor-go/generator/accounts.go +++ b/cmd/generate-bindings/solana/anchor-go/generator/accounts.go @@ -389,6 +389,10 @@ func IDLTypeKind_ToTypeDeclCode(ts idltype.IdlType) *Statement { stat.Qual(PkgBinary, "Uint128") case *idltype.I128: stat.Qual(PkgBinary, "Int128") + case *idltype.U256: + stat.Index(Lit(32)).Byte() + case *idltype.I256: + stat.Index(Lit(32)).Byte() case *idltype.Bytes: stat.Index().Byte() case *idltype.String: diff --git a/cmd/generate-bindings/solana/anchor-go/generator/constants.go b/cmd/generate-bindings/solana/anchor-go/generator/constants.go index 591c4556..e2db01d9 100644 --- a/cmd/generate-bindings/solana/anchor-go/generator/constants.go +++ b/cmd/generate-bindings/solana/anchor-go/generator/constants.go @@ -174,25 +174,57 @@ func (g *Generator) gen_constants() (*OutputFile, error) { Return(Id("val")), ).Call() code.Line() - case *idltype.I128: - _ = ty - // "value":"-100_000_000" - cleanValue := strings.ReplaceAll(co.Value, "_", "") - bigInt := new(big.Int) - _, ok := bigInt.SetString(cleanValue, 10) - if !ok { - return nil, fmt.Errorf("failed to parse i128 constants[%d] %s: invalid format", coi, spew.Sdump(co)) - } - // Generate code that creates a big.Int from string - code.Var().Id(co.Name).Op("=").Func().Params().Op("*").Qual("math/big", "Int").Block( - Id("val").Op(",").Id("ok").Op(":=").New(Qual("math/big", "Int")).Dot("SetString").Call(Lit(cleanValue), Lit(10)), - If(Op("!").Id("ok")).Block( - Panic(Lit(fmt.Sprintf("invalid i128 constant %s", co.Name))), - ), - Return(Id("val")), - ).Call() - code.Line() - case *idltype.F32: + case *idltype.I128: + _ = ty + // "value":"-100_000_000" + cleanValue := strings.ReplaceAll(co.Value, "_", "") + bigInt := new(big.Int) + _, ok := bigInt.SetString(cleanValue, 10) + if !ok { + return nil, fmt.Errorf("failed to parse i128 constants[%d] %s: invalid format", coi, spew.Sdump(co)) + } + // Generate code that creates a big.Int from string + code.Var().Id(co.Name).Op("=").Func().Params().Op("*").Qual("math/big", "Int").Block( + Id("val").Op(",").Id("ok").Op(":=").New(Qual("math/big", "Int")).Dot("SetString").Call(Lit(cleanValue), Lit(10)), + If(Op("!").Id("ok")).Block( + Panic(Lit(fmt.Sprintf("invalid i128 constant %s", co.Name))), + ), + Return(Id("val")), + ).Call() + code.Line() + case *idltype.U256: + _ = ty + cleanValue := strings.ReplaceAll(co.Value, "_", "") + bigInt := new(big.Int) + _, ok := bigInt.SetString(cleanValue, 10) + if !ok { + return nil, fmt.Errorf("failed to parse u256 constants[%d] %s: invalid format", coi, spew.Sdump(co)) + } + code.Var().Id(co.Name).Op("=").Func().Params().Op("*").Qual("math/big", "Int").Block( + Id("val").Op(",").Id("ok").Op(":=").New(Qual("math/big", "Int")).Dot("SetString").Call(Lit(cleanValue), Lit(10)), + If(Op("!").Id("ok")).Block( + Panic(Lit(fmt.Sprintf("invalid u256 constant %s", co.Name))), + ), + Return(Id("val")), + ).Call() + code.Line() + case *idltype.I256: + _ = ty + cleanValue := strings.ReplaceAll(co.Value, "_", "") + bigInt := new(big.Int) + _, ok := bigInt.SetString(cleanValue, 10) + if !ok { + return nil, fmt.Errorf("failed to parse i256 constants[%d] %s: invalid format", coi, spew.Sdump(co)) + } + code.Var().Id(co.Name).Op("=").Func().Params().Op("*").Qual("math/big", "Int").Block( + Id("val").Op(",").Id("ok").Op(":=").New(Qual("math/big", "Int")).Dot("SetString").Call(Lit(cleanValue), Lit(10)), + If(Op("!").Id("ok")).Block( + Panic(Lit(fmt.Sprintf("invalid i256 constant %s", co.Name))), + ), + Return(Id("val")), + ).Call() + code.Line() + case *idltype.F32: _ = ty // "value":"3.14" cleanValue := strings.ReplaceAll(co.Value, "_", "") diff --git a/cmd/generate-bindings/solana/anchor-go/generator/u256_test.go b/cmd/generate-bindings/solana/anchor-go/generator/u256_test.go new file mode 100644 index 00000000..13fb77ae --- /dev/null +++ b/cmd/generate-bindings/solana/anchor-go/generator/u256_test.go @@ -0,0 +1,148 @@ +//nolint:all // Forked from anchor-go generator, maintaining original code structure +package generator + +import ( + "testing" + + "github.com/gagliardetto/anchor-go/idl" + "github.com/gagliardetto/anchor-go/idl/idltype" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIDLTypeKind_ToTypeDeclCode_U256(t *testing.T) { + assert.NotPanics(t, func() { + result := IDLTypeKind_ToTypeDeclCode(&idltype.U256{}) + assert.NotNil(t, result) + }) +} + +func TestIDLTypeKind_ToTypeDeclCode_I256(t *testing.T) { + assert.NotPanics(t, func() { + result := IDLTypeKind_ToTypeDeclCode(&idltype.I256{}) + assert.NotNil(t, result) + }) +} + +func TestGenTypeName_U256(t *testing.T) { + assert.NotPanics(t, func() { + result := genTypeName(&idltype.U256{}) + assert.NotNil(t, result) + }) +} + +func TestGenTypeName_I256(t *testing.T) { + assert.NotPanics(t, func() { + result := genTypeName(&idltype.I256{}) + assert.NotNil(t, result) + }) +} + +func TestGenConstants_U256(t *testing.T) { + idlData := &idl.Idl{ + Constants: []idl.IdlConst{ + { + Name: "MAX_SUPPLY", + Ty: &idltype.U256{}, + Value: "115792089237316195423570985008687907853269984665640564039457584007913129639935", + }, + }, + } + gen := &Generator{ + idl: idlData, + options: &GeneratorOptions{Package: "test"}, + } + + outputFile, err := gen.gen_constants() + require.NoError(t, err) + + generatedCode := outputFile.File.GoString() + assert.Contains(t, generatedCode, "var MAX_SUPPLY = func() *big.Int") + assert.Contains(t, generatedCode, ".SetString(\"115792089237316195423570985008687907853269984665640564039457584007913129639935\", 10)") +} + +func TestGenConstants_I256(t *testing.T) { + idlData := &idl.Idl{ + Constants: []idl.IdlConst{ + { + Name: "MIN_VALUE", + Ty: &idltype.I256{}, + Value: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + }, + }, + } + gen := &Generator{ + idl: idlData, + options: &GeneratorOptions{Package: "test"}, + } + + outputFile, err := gen.gen_constants() + require.NoError(t, err) + + generatedCode := outputFile.File.GoString() + assert.Contains(t, generatedCode, "var MIN_VALUE = func() *big.Int") + assert.Contains(t, generatedCode, ".SetString(\"-57896044618658097711785492504343953926634992332820282019728792003956564819968\", 10)") +} + +func TestGenConstants_U256_Invalid(t *testing.T) { + idlData := &idl.Idl{ + Constants: []idl.IdlConst{ + { + Name: "INVALID_U256", + Ty: &idltype.U256{}, + Value: "not_a_number", + }, + }, + } + gen := &Generator{ + idl: idlData, + options: &GeneratorOptions{Package: "test"}, + } + + _, err := gen.gen_constants() + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to parse u256") +} + +func TestGenConstants_I256_Invalid(t *testing.T) { + idlData := &idl.Idl{ + Constants: []idl.IdlConst{ + { + Name: "INVALID_I256", + Ty: &idltype.I256{}, + Value: "not_a_number", + }, + }, + } + gen := &Generator{ + idl: idlData, + options: &GeneratorOptions{Package: "test"}, + } + + _, err := gen.gen_constants() + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to parse i256") +} + +func TestGenConstants_U256_WithUnderscores(t *testing.T) { + idlData := &idl.Idl{ + Constants: []idl.IdlConst{ + { + Name: "LARGE_U256", + Ty: &idltype.U256{}, + Value: "1_000_000_000_000_000_000_000_000_000", + }, + }, + } + gen := &Generator{ + idl: idlData, + options: &GeneratorOptions{Package: "test"}, + } + + outputFile, err := gen.gen_constants() + require.NoError(t, err) + + generatedCode := outputFile.File.GoString() + assert.Contains(t, generatedCode, "var LARGE_U256 = func() *big.Int") + assert.Contains(t, generatedCode, ".SetString(\"1000000000000000000000000000\", 10)") +} From 8487e6f9b0c01cc173a5773d845272127fc41125 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 26 May 2026 23:59:09 -0400 Subject: [PATCH 4/4] use ParseUint for 64 bit values (#348) --- .../solana/anchor-go/generator/constants.go | 94 ++++++++++------ .../anchor-go/generator/constants_test.go | 102 ++++++++++++++++++ 2 files changed, 165 insertions(+), 31 deletions(-) diff --git a/cmd/generate-bindings/solana/anchor-go/generator/constants.go b/cmd/generate-bindings/solana/anchor-go/generator/constants.go index e2db01d9..64a0ee69 100644 --- a/cmd/generate-bindings/solana/anchor-go/generator/constants.go +++ b/cmd/generate-bindings/solana/anchor-go/generator/constants.go @@ -249,7 +249,9 @@ func (g *Generator) gen_constants() (*OutputFile, error) { _ = ty // "type":{"array":["u8",23]},"value":"[115, 101, 110, 100, 95, 119, 105, 116, 104, 95, 115, 119, 97, 112, 95, 100, 101, 108, 101, 103, 97, 116, 101]" var b []any - err := json.Unmarshal([]byte(co.Value), &b) + dec := json.NewDecoder(strings.NewReader(co.Value)) + dec.UseNumber() + err := dec.Decode(&b) if err != nil { return nil, fmt.Errorf("failed to unmarshal array constants[%d] %s: %w", coi, spew.Sdump(co), err) } @@ -292,36 +294,66 @@ func (g *Generator) gen_constants() (*OutputFile, error) { }).Op("{").ListFunc(func(byteGroup *Group) { for _, val := range b[:] { switch ty.Type.(type) { - case *idltype.U8: - byteGroup.Lit(byte(val.(float64))) - case *idltype.I8: - byteGroup.Lit(int8(val.(float64))) - case *idltype.U16: - byteGroup.Lit(uint16(val.(float64))) - case *idltype.I16: - byteGroup.Lit(int16(val.(float64))) - case *idltype.U32: - byteGroup.Lit(uint32(val.(float64))) - case *idltype.I32: - byteGroup.Lit(int32(val.(float64))) - case *idltype.U64: - byteGroup.Lit(uint64(val.(float64))) - case *idltype.I64: - byteGroup.Lit(int64(val.(float64))) - case *idltype.F32: - // TODO: is this correct? Are they encoded as strings? - v, err := strconv.ParseFloat(val.(string), 32) - if err != nil { - panic(fmt.Errorf("failed to parse f32 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) - } - byteGroup.Lit(float32(v)) - case *idltype.F64: - // TODO: is this correct? Are they encoded as strings? - v, err := strconv.ParseFloat(val.(string), 64) - if err != nil { - panic(fmt.Errorf("failed to parse f64 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) - } - byteGroup.Lit(v) + case *idltype.U8: + v, err := strconv.ParseUint(val.(json.Number).String(), 10, 8) + if err != nil { + panic(fmt.Errorf("failed to parse u8 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(byte(v)) + case *idltype.I8: + v, err := strconv.ParseInt(val.(json.Number).String(), 10, 8) + if err != nil { + panic(fmt.Errorf("failed to parse i8 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(int8(v)) + case *idltype.U16: + v, err := strconv.ParseUint(val.(json.Number).String(), 10, 16) + if err != nil { + panic(fmt.Errorf("failed to parse u16 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(uint16(v)) + case *idltype.I16: + v, err := strconv.ParseInt(val.(json.Number).String(), 10, 16) + if err != nil { + panic(fmt.Errorf("failed to parse i16 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(int16(v)) + case *idltype.U32: + v, err := strconv.ParseUint(val.(json.Number).String(), 10, 32) + if err != nil { + panic(fmt.Errorf("failed to parse u32 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(uint32(v)) + case *idltype.I32: + v, err := strconv.ParseInt(val.(json.Number).String(), 10, 32) + if err != nil { + panic(fmt.Errorf("failed to parse i32 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(int32(v)) + case *idltype.U64: + v, err := strconv.ParseUint(val.(json.Number).String(), 10, 64) + if err != nil { + panic(fmt.Errorf("failed to parse u64 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(uint64(v)) + case *idltype.I64: + v, err := strconv.ParseInt(val.(json.Number).String(), 10, 64) + if err != nil { + panic(fmt.Errorf("failed to parse i64 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(int64(v)) + case *idltype.F32: + v, err := strconv.ParseFloat(val.(json.Number).String(), 32) + if err != nil { + panic(fmt.Errorf("failed to parse f32 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(float32(v)) + case *idltype.F64: + v, err := strconv.ParseFloat(val.(json.Number).String(), 64) + if err != nil { + panic(fmt.Errorf("failed to parse f64 in constants[%d] %s: %w", coi, spew.Sdump(co), err)) + } + byteGroup.Lit(v) case *idltype.String: v, err := strconv.Unquote(val.(string)) if err != nil { diff --git a/cmd/generate-bindings/solana/anchor-go/generator/constants_test.go b/cmd/generate-bindings/solana/anchor-go/generator/constants_test.go index 92e3192c..23a516c8 100644 --- a/cmd/generate-bindings/solana/anchor-go/generator/constants_test.go +++ b/cmd/generate-bindings/solana/anchor-go/generator/constants_test.go @@ -715,6 +715,108 @@ func TestGenConstantsErrorCases(t *testing.T) { }) } +// TestGenConstantsLargeU64I64ArrayPrecision verifies that u64 and i64 array +// elements above 2^53 are emitted with full 64-bit precision. json.Unmarshal +// into []any decodes numbers as float64, which silently rounds integers larger +// than 2^53. This test catches that: if the generator still uses float64 casts, +// the expected exact values will not appear in the generated code. +func TestGenConstantsLargeU64I64ArrayPrecision(t *testing.T) { + t.Run("u64 array with values above 2^53", func(t *testing.T) { + constants := []idl.IdlConst{ + { + Name: "LARGE_U64_ARRAY", + Ty: &idltype.Array{ + Type: &idltype.U64{}, + Size: &idltype.IdlArrayLenValue{Value: 4}, + }, + // 2^53 = 9007199254740992 is the last integer float64 represents exactly. + // 2^53+1 and 2^53+3 are NOT representable in float64 and will be rounded + // to 2^53 and 2^53+4 respectively if parsed through float64. + Value: "[9007199254740993, 9007199254740995, 18446744073709551615, 9007199254740992]", + }, + } + + idlData := &idl.Idl{Constants: constants} + gen := &Generator{idl: idlData, options: &GeneratorOptions{Package: "test"}} + + outputFile, err := gen.gen_constants() + require.NoError(t, err) + + generatedCode := outputFile.File.GoString() + + // 2^53+1 = 0x20000000000001 — NOT exactly representable in float64 + assert.Contains(t, generatedCode, "uint64(0x20000000000001)", + "9007199254740993 (2^53+1) was rounded; float64 precision loss in u64 array element") + // 2^53+3 = 0x20000000000003 — NOT exactly representable in float64 + assert.Contains(t, generatedCode, "uint64(0x20000000000003)", + "9007199254740995 (2^53+3) was rounded; float64 precision loss in u64 array element") + // max u64 = 0xffffffffffffffff + assert.Contains(t, generatedCode, "uint64(0xffffffffffffffff)", + "18446744073709551615 (max u64) was rounded; float64 precision loss in u64 array element") + // 2^53 exactly representable — should always work + assert.Contains(t, generatedCode, "uint64(0x20000000000000)", + "9007199254740992 (2^53) should be emitted correctly") + }) + + t.Run("i64 array with values above 2^53", func(t *testing.T) { + constants := []idl.IdlConst{ + { + Name: "LARGE_I64_ARRAY", + Ty: &idltype.Array{ + Type: &idltype.I64{}, + Size: &idltype.IdlArrayLenValue{Value: 4}, + }, + Value: "[9007199254740993, -9007199254740993, 9223372036854775807, -9223372036854775808]", + }, + } + + idlData := &idl.Idl{Constants: constants} + gen := &Generator{idl: idlData, options: &GeneratorOptions{Package: "test"}} + + outputFile, err := gen.gen_constants() + require.NoError(t, err) + + generatedCode := outputFile.File.GoString() + + // 2^53+1 positive + assert.Contains(t, generatedCode, "int64(9007199254740993)", + "9007199254740993 (2^53+1) was rounded; float64 precision loss in i64 array element") + // 2^53+1 negative + assert.Contains(t, generatedCode, "int64(-9007199254740993)", + "-9007199254740993 was rounded; float64 precision loss in i64 array element") + // max i64 + assert.Contains(t, generatedCode, "int64(9223372036854775807)", + "max i64 was rounded; float64 precision loss in i64 array element") + // min i64 + assert.Contains(t, generatedCode, "int64(-9223372036854775808)", + "min i64 was rounded; float64 precision loss in i64 array element") + }) + + t.Run("u32 array is not affected", func(t *testing.T) { + // u32 max = 4294967295 < 2^53, so float64 is fine + constants := []idl.IdlConst{ + { + Name: "U32_ARRAY", + Ty: &idltype.Array{ + Type: &idltype.U32{}, + Size: &idltype.IdlArrayLenValue{Value: 2}, + }, + Value: "[4294967295, 0]", + }, + } + + idlData := &idl.Idl{Constants: constants} + gen := &Generator{idl: idlData, options: &GeneratorOptions{Package: "test"}} + + outputFile, err := gen.gen_constants() + require.NoError(t, err) + + generatedCode := outputFile.File.GoString() + assert.Contains(t, generatedCode, "uint32(0xffffffff)") + assert.Contains(t, generatedCode, "uint32(0x0)") + }) +} + // TestGenConstantsRealWorldExamples 测试真实世界的例子 func TestGenConstantsRealWorldExamples(t *testing.T) { t.Run("Solana program constants", func(t *testing.T) {