2

In this comment of pjs github, it says that we should never do the following:

const something = api.createType('SomeType', { foo: 'bar' });
...
await api.tx.somewhere.something(something);
...

and always let the API create the types as

await api.tx.somewhere.something({ foo: 'bar' });

does this apply to every single case? is it safe to assume we should not use createType at all within our development while using polkadot-js?

1 Answer 1

2

Summary

As a fore-warning I am not the maintainer of polkadot-js but a super user instead. The below is just my opinion, and does not sit as the "Be all end all" solution for createType under the ApiPromise class.

It is important to notice that using createType for creating Codec types that are generated from the metadata is unsafe and may cause bugs, and refactoring headaches in the future since those types may change in the metadata. It is best to let polkadot-js handle resolving those Codec types for when you call api.tx.something.somewhere. This can be done by just passing the args in as their expected structure using javascript types. That being said there might be a type you want to build that is not within the metadata provided by the chain but intrinsic to polkadot-js. That IMO is still perfectly acceptable. Below I will cover some examples of when using createType might be okay such as, Testing, Calls, ExtrinsicPayloads.

Testing

As mentioned in my comment on the issue, createType is still very useful for mocking any call you may invoke on the ApiPromise. This allows for fluid mock testing, and mirrored responses. Here is the example I mention in the issue with some more explanations:

// Create a ApiPromise that will not connect to a provider but instead will give all the mechanics
// necessary to create the calls and types you may need.
export function createApiWithAugmentations(metadataHex: `0x${string}`): ApiPromise {
    const registry = new TypeRegistry();
    const metadata = new Metadata(registry, metadataHex);

    registry.setMetadata(metadata);

    const api = new ApiPromise({
        provider: new WsProvider('ws://', false),
        registry,
    });

    api.injectMetadata(metadata, true, registry);

    return api;
}

const mockApi = createApiWithAugmentations(<some_metadata>);

// This is a mock call, that will return what the node would return if you were to call -
// `api.query.polkadotXcm.safeXcmVersion`
const getSafeXcmVersion = () =>
    Promise.resolve().then(() => {
        return mockApi.registry.createType('Option<u32>', 2);
    });

// This is the mock Api that create locally. This could be expanded even further with more 
// keys that exist on the ApiPromise type.
const adjustedMockApi = {
    registry: mockApi.registry
    query: {
        polkadotXcm: {
            safeXcmVersion: getSafeXcmVersion
        }
    }
}

Creating Calls

Let's say for example you want to build a call. This call is going to be used to send a sudo instruction to chain. It can be done like the following:

    const xcmMsg = api.tx.xcmPallet.send(<some_multilocation>, <some_xcm_msg>);
    const xcmCall = api.createType('Call', {
        callIndex: xcmMsg.callIndex,
        args: xcmMsg.args,
    });


    // Sudo accepts the following arg types:
    // (call: string | Uint8Array | IMethod<AnyTuple, any> | Call)
    await api.tx.sudo.sudo(xcmCall).signAndSend(alice);

This can be useful for multiple usecases where the call needs to be constructed, and used somewhere else.

Another usecase is if you wanted to decode a given call, you can create the type and key into the appropriate args you want to decode.

Creating ExtrinsicPayloads

Similar to call above you can also decode encoded transactions with the ExtrinsicPayload type.

For example lets assume you have an encoded transaction and you want the method to be abstracted and decoded from the following:

const encodedTx = 'some_encoded_tx';

const extrinsicPayload = api.registry.createType('ExtrinsicPayload', encodedTx, {
    version: EXTRINSIC_VERSION,
});

const call = api.registry.createType('Call', extrinsicPayload.method);
const decodedMethodInfo = JSON.stringify(call.toHuman());

Overall

All in all I think it's important to consider the use case where you are using createType, and whether the type you are building is directly from the metadata, or the type is a utility based reason provided from polkadot-js.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.