diff --git a/spregistry/contract.go b/spregistry/contract.go index 1e27919..cee5795 100644 --- a/spregistry/contract.go +++ b/spregistry/contract.go @@ -483,25 +483,8 @@ func (c *Contract) GetProviderWithProduct(ctx context.Context, providerID *big.I return nil, fmt.Errorf("getProviderWithProduct call failed: %w", err) } - var res struct { - ProviderID *big.Int `abi:"providerId"` - ProviderInfo struct { - ServiceProvider common.Address `abi:"serviceProvider"` - Payee common.Address `abi:"payee"` - Name string `abi:"name"` - Description string `abi:"description"` - IsActive bool `abi:"isActive"` - } `abi:"providerInfo"` - Product struct { - ProductType uint8 `abi:"productType"` - CapabilityKeys []string `abi:"capabilityKeys"` - IsActive bool `abi:"isActive"` - } `abi:"product"` - ProductCapabilityValues [][]byte `abi:"productCapabilityValues"` - } - - err = c.abi.UnpackIntoInterface(&res, "getProviderWithProduct", result) - if err != nil { + var res getProviderWithProductOutput + if err := unpackSingleTuple(c.abi, "getProviderWithProduct", result, &res); err != nil { return nil, fmt.Errorf("failed to unpack getProviderWithProduct result: %w", err) } @@ -523,6 +506,23 @@ func (c *Contract) GetProviderWithProduct(ctx context.Context, providerID *big.I }, nil } +// getProviderWithProductOutput mirrors the (providerId, providerInfo, +// product, productCapabilityValues) tuple getProviderWithProduct +// returns. Same json-tagged shape pattern as getProviderByAddressOutput +// for unpackSingleTuple. +type getProviderWithProductOutput struct { + ProviderID *big.Int `json:"providerId"` + ProviderInfo getProviderByAddressOutputInfo `json:"providerInfo"` + Product getProviderWithProductOutputProduct `json:"product"` + ProductCapabilityValues [][]byte `json:"productCapabilityValues"` +} + +type getProviderWithProductOutputProduct struct { + ProductType uint8 `json:"productType"` + CapabilityKeys []string `json:"capabilityKeys"` + IsActive bool `json:"isActive"` +} + func (c *Contract) GetAllActiveProviders(ctx context.Context, offset, limit *big.Int) ([]*big.Int, bool, error) { data, err := c.abi.Pack("getAllActiveProviders", offset, limit) if err != nil { diff --git a/spregistry/contract_test.go b/spregistry/contract_test.go index d7c6417..3431d34 100644 --- a/spregistry/contract_test.go +++ b/spregistry/contract_test.go @@ -75,3 +75,86 @@ func TestUnpackSingleTuple_GetProviderByAddress(t *testing.T) { t.Errorf("IsActive = false, want true") } } + +// TestUnpackSingleTuple_GetProviderWithProduct exercises the same unpack +// path for getProviderWithProduct, which has a deeper return tuple +// (providerInfo + product + productCapabilityValues) and was missed by +// d91ba0c. +func TestUnpackSingleTuple_GetProviderWithProduct(t *testing.T) { + parsedABI, err := abi.JSON(strings.NewReader(SPRegistryABIJSON)) + if err != nil { + t.Fatalf("parse ABI: %v", err) + } + + method, ok := parsedABI.Methods["getProviderWithProduct"] + if !ok { + t.Fatalf("getProviderWithProduct not found in ABI") + } + + type providerInfoT struct { + ServiceProvider common.Address `abi:"serviceProvider"` + Payee common.Address `abi:"payee"` + Name string `abi:"name"` + Description string `abi:"description"` + IsActive bool `abi:"isActive"` + } + type productT struct { + ProductType uint8 `abi:"productType"` + CapabilityKeys []string `abi:"capabilityKeys"` + IsActive bool `abi:"isActive"` + } + type outT struct { + ProviderID *big.Int `abi:"providerId"` + ProviderInfo providerInfoT `abi:"providerInfo"` + Product productT `abi:"product"` + ProductCapabilityValues [][]byte `abi:"productCapabilityValues"` + } + want := outT{ + ProviderID: big.NewInt(24), + ProviderInfo: providerInfoT{ + ServiceProvider: common.HexToAddress("0xE3e842B9D89ed2Ee3976b9b8916827302618c29e"), + Payee: common.HexToAddress("0xE3e842B9D89ed2Ee3976b9b8916827302618c29e"), + Name: "sp-playground", + Description: "calibnet test SP", + IsActive: true, + }, + Product: productT{ + ProductType: 0, + CapabilityKeys: []string{"serviceURL", "minPieceSizeInBytes"}, + IsActive: true, + }, + ProductCapabilityValues: [][]byte{ + []byte("https://pdp.sp-playground.xyz"), + {0x10, 0x00, 0x00}, + }, + } + + payload, err := method.Outputs.Pack(want) + if err != nil { + t.Fatalf("pack synthetic return: %v", err) + } + + var got getProviderWithProductOutput + if err := unpackSingleTuple(parsedABI, "getProviderWithProduct", payload, &got); err != nil { + t.Fatalf("unpackSingleTuple: %v", err) + } + + if got.ProviderID == nil || got.ProviderID.Cmp(big.NewInt(24)) != 0 { + t.Errorf("ProviderID = %v, want 24", got.ProviderID) + } + if got.ProviderInfo.Name != want.ProviderInfo.Name { + t.Errorf("ProviderInfo.Name = %q, want %q", got.ProviderInfo.Name, want.ProviderInfo.Name) + } + if got.Product.ProductType != want.Product.ProductType { + t.Errorf("Product.ProductType = %d, want %d", got.Product.ProductType, want.Product.ProductType) + } + if len(got.Product.CapabilityKeys) != len(want.Product.CapabilityKeys) { + t.Errorf("CapabilityKeys len = %d, want %d", len(got.Product.CapabilityKeys), len(want.Product.CapabilityKeys)) + } + if len(got.ProductCapabilityValues) != len(want.ProductCapabilityValues) { + t.Errorf("ProductCapabilityValues len = %d, want %d", len(got.ProductCapabilityValues), len(want.ProductCapabilityValues)) + } + if string(got.ProductCapabilityValues[0]) != string(want.ProductCapabilityValues[0]) { + t.Errorf("ProductCapabilityValues[0] = %q, want %q", string(got.ProductCapabilityValues[0]), string(want.ProductCapabilityValues[0])) + } +}