Skip to content

Write validators for primitive types #6

@rmw2

Description

@rmw2

Currently, all of the validators for solidity primitive types are the identity function (see src/primitives.js). We would like to replace these with conversion functions that ensure there is a single canonical javascript representation for each of the solidity primitive types we are emulating.

For example, one could conceivably represent an address as a Number or a String -- however allowing both in the same project creates ambiguity, which can lead to unexpected errors.

To provide the most ergonomic developer experience, we would also like that unambiguous but non-canonical representations of a particular solidity primitive are automatically converted to their canonical representation. For example, if '0x<20 hex bytes>' is the canonical representation for address, then we should prepend '0x' to an address argument of '<20 hex bytes>' with no prefix, rather than throwing an error.

Inversely, if a provided value is ambiguous or cannot be coerced into the canonical representation for the particular solidity type, the validator should throw an error.

To accomplish this, we can for each solidity primitive type, define a set of validation functions for each javascript primitive type. Most of these functions will simply throw an error, as there are unlikely to be multiple compatible javascript representations of each solidity primitive type.

To simplify this process, we can define a helper function which will allow us to define for each solidity primitive, an object mapping javascript type names to sub-validators for each supported javascript type, and which will automatically throw for any js type without a specified sub-validator:

/**
 * Create a validator function for the solidity type @param {String} target, by delegating
 * to the function in @param {Object} handlers for the javascript type of the input value.
 * If the type is missing in the {handlers} object, throw an error 
 */
function handleByType(target, handlers) {
   return input => {
     const jstype = typeof input
     // Call the proper sub-validator
     if (jstype in handlers) return handlers[jstype](input)
     // Throw an error for unsupported types
     throw new Error(`Cannot convert javascript type ${jstype} to solidity type ${target}`)
   }
 }

An example implementation of a handler using this helper, for the solidity address primitive, could look like this:

const validateAddress = handleByType('address', {
  string: x => x.slice(2) === '0x' ? x : `0x${x}`,
  object: x => x instanceof Buffer 
    ? `0x${x.toString('hex')}` 
    : reject('Cannot coerce object to address: must be string or Buffer')
})

Where we make use of another helper function reject, to simply throw an error with the provided message without requiring us to start a new statement block.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions