User-Defined Value Types in Solidity

Motivation:

struct Price { uint128 price; }
struct Quantity { uint128 quantity; }
function toPrice(uint128 price) returns(Price memory) {
return Price(price);
}
function fromPrice(Price memory price) returns(uint128) {
return price.price;
}
function toQuantity(uint128 quantity) returns(Quantity memory) {
return Quantity(quantity);
}
function fromQuantity(Quantity memory quantity) returns(uint128) {
return quantity.quantity;
}

The syntax for User-Defined Value Types:

pragma solidity ^0.8.8;type Price is uint128;
type Quantity is uint128;
pragma solidity ^0.8.8;type Decimal18 is uint256;interface MinimalERC20 {
function transfer(address to, Decimal18 value) external;
}
interface AnotherMinimalERC20 {
function transfer(address to, uint256 value) external;
}

Example:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.8;
// Represent a 18 decimal, 256 bit wide fixed point type
// using a user defined value type.
type UFixed is uint256;
/// A minimal library to do fixed point operations on UFixed.
library FixedMath {
uint constant multiplier = 10**18;
/// Adds two UFixed numbers. Reverts on overflow,
/// relying on checked arithmetic on uint256.
function add(UFixed a, UFixed b) internal pure returns (UFixed) {
return UFixed.wrap(UFixed.unwrap(a) + UFixed.unwrap(b));
}
/// Multiplies UFixed and uint256. Reverts on overflow,
/// relying on checked arithmetic on uint256.
function mul(UFixed a, uint256 b) internal pure returns (UFixed) {
return UFixed.wrap(UFixed.unwrap(a) * b);
}
/// Take the floor of a UFixed number.
/// @return the largest integer that does not exceed `a`.
function floor(UFixed a) internal pure returns (uint256) {
return UFixed.unwrap(a) / multiplier;
}
/// Turns a uint256 into a UFixed of the same value.
/// Reverts if the integer is too large.
function toUFixed(uint256 a) internal pure returns (UFixed) {
return UFixed.wrap(a * multiplier);
}
}

Operators and Type Rules:

/// Proposal on defining operators on user defined value types
/// Note: this does not fully compile on Solidity 0.8.8; only a concept.
type UncheckedInt8 is int8;function add(UncheckedInt8 a, UncheckedInt8 b) pure returns(UncheckedInt8) {
unchecked {
return UncheckedInt8.wrap(UncheckedInt8.unwrap(a) + UncheckedInt8.unwrap(b));
}
}
function addInt(UncheckedInt8 a, uint b) pure returns(UncheckedInt8) {
unchecked {
return UncheckedInt8.wrap(UncheckedInt8.unwrap(a) + b);
}
}
using {add as +, addInt as +} for UncheckedInt8;contract MockOperator {
UncheckedInt8 x;
function increment() external {
// This would not revert on overflow when x = 127
x = x + 1;
}
function add(UncheckedInt8 y) external {
// Similarly, this would also not revert on overflow.
x = x + y;
}
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store