Getting `Cannot construct unknown type Result` error on deployment
When deploying this contract via the UI, the UI just displays this:
Uncaught error. Something went wrong with the query and rendering of this component. createType(Result):: Cannot construct unknown type Result
The console shows:
react.01.5ff66cee.js:2 Error: createType(Result):: Cannot construct unknown type Result
at u (polkadot.01.dc51e7e6.js:1)
at io (main.01be78c1.js:2)
at main.01be78c1.js:2
at Object.useMemo (react.01.5ff66cee.js:2)
at t.useMemo (react.01.5ff66cee.js:2)
at e (main.01be78c1.js:2)
at Gi (react.01.5ff66cee.js:2)
at xl (react.01.5ff66cee.js:2)
at ps (react.01.5ff66cee.js:2)
at ds (react.01.5ff66cee.js:2)
I think the reason is that the specific contract has this ink! message:
/// Returns an error.
#[ink(message)]
pub fn return_err(&self) -> Result<()> {
Err(Error::MyError)
}
I debugged the error. The contract is sending a return message and then the UI does a lookup in the registry to dynamically infer the message type from it.
The message is carrying a returnType object of type TypeDef. This object is carrying both optional parameters type and displayName. When the UI tries to dynamically create the type from the given return type, it is trying to match the type of the message against the registry by using then displayName property, or at least it is prioritizing it in favor of the type property.
Changing the formatData function in the Data.tsx component from:
function formatData(registry: Registry, data: AnyJson, type: TypeDef | undefined): Codec {
return createTypeUnsafe(type?.displayName || registry, type?.type || "Raw", [data]);
}
to
function formatData(registry: Registry, data: AnyJson, type: TypeDef | undefined): Codec {
return createTypeUnsafe( registry, type?.type || type?.displayName || "Raw", [data]);
}
leads to a correct execution of the call.
However, I'm wondering if it makes any sense at all to infer the displayName for type creation?
@achimcc I tool your suggested changes to formatData for a spin and found that while that worked, the subsequently returned ReactElement was not falling into the if statement for Option, thus returning a hexidecimal representation of the Codec.
The issue is due to value being checked against instanceof Option instead of codec. Changing references to value to codec results in the correct Some string being returned in a component.
The issue / fix is outlined below, with incorrect logic commented out and subsequent line showing the correct logic.
// if (type.info === TypeDefInfo.Option && value instanceof Option) { <-- INCORRECT
if (type.info === TypeDefInfo.Option && codec instanceof Option) {
//const isSome = value.isSome;
const isSome = codec.isSome;
const subType = type.sub as TypeDef;
if (asJson) {
// return `${isSome ? 'Some' : 'None'}${isSome ? `(${value.toString()})` : ''}`; <-- INCORRECT
return `${isSome ? 'Some' : 'None'}${isSome ? `(${codec.unwrap().toString()})` : ''}`;
}
return (
<div className='enum'>
{isSome ? 'Some' : 'None'}
{isSome && (
<>
{'('}
<div className='inner'>
<Data
registry={registry}
type={subType}
//value={value.toString()} <-- INCORRECT
value={codec.unwrap().toString()}
/>
</div>
{')'}
</>
)}
</div>
);
}
I'm happy to open a PR containing both your fix to formatData and my fix for Option, if no one else is looking at this issue.
One more note regarding decoding Option.
The second thing that's done in function Data is
if (isNull(value) || (Array.isArray(value) && value.length === 0)) {
return '()';
}
An Option that isNone has isNull(value) === true which then immediately returns () instead of going through the formatData and then returning the intended component.
Would it be safe to remove the check for null above and let formatData and the subsequent code do its thing?