Skip to content

Commit 6aa040b

Browse files
committed
Add User Defined Value Types
1 parent 47047b4 commit 6aa040b

File tree

1 file changed

+101
-1
lines changed

1 file changed

+101
-1
lines changed

README.md

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,16 @@ _This cheatsheet is based on version 0.8.29_
7272
- [Gas Considerations:](#gas-considerations-1)
7373
- [Mappings](#mappings)
7474
- [Structs](#structs)
75+
- [User Defined Value Types](#user-defined-value-types)
76+
- [Motivation](#motivation)
77+
- [Syntax](#syntax)
78+
- [Wrapping and Unwrapping](#wrapping-and-unwrapping)
79+
- [Operations and Conversions](#operations-and-conversions)
7580
- [Enums](#enums)
7681
- [Enum Advantages:](#enum-advantages)
7782
- [Best Practices and Tips](#best-practices-and-tips)
7883
- [Modifiers](#modifiers)
79-
- [Syntax](#syntax)
84+
- [Syntax](#syntax-1)
8085
- [Anatomy of a Modifier](#anatomy-of-a-modifier)
8186
- [Multiple Modifiers on One Function](#multiple-modifiers-on-one-function)
8287
- [Events](#events)
@@ -113,6 +118,10 @@ _This cheatsheet is based on version 0.8.29_
113118
- [`receive()` Function](#receive-function)
114119
- [`fallback()` Function](#fallback-function)
115120
- [Best Practices](#best-practices-3)
121+
- [Data Locations](#data-locations)
122+
- [Transient Storage](#transient-storage)
123+
- [Send Ether](#send-ether)
124+
- [Call \& Delegatecall](#call--delegatecall)
116125
- [References](#references)
117126

118127
# Getting Started
@@ -1008,6 +1017,89 @@ function createUser(string memory _name, uint256 _age) external {
10081017
}
10091018
```
10101019

1020+
## User Defined Value Types
1021+
1022+
- User-Defined Value Types allow you to create a custom type name that wraps an existing built-in value type (like `uint256`, `int128`, `bytes32`, etc.).
1023+
- This feature was introduced in Solidity 0.8.8 and provides a way to give **semantic meaning** to a primitive type, helping catch logic errors and improving code readability.
1024+
1025+
### Motivation
1026+
1027+
- **Type Safety & Readability**: By assigning a descriptive name to a built-in type, you can differentiate between, say, a `UserId` and a `Balance`, even if they’re both `uint256` under the hood.
1028+
- **Compile-Time Checks**: Conversions between different user-defined value types (and between the user-defined type and its underlying type) require **explicit** wrapping/unwrapping. This helps avoid mixing up incompatible values in your code.
1029+
1030+
### Syntax
1031+
1032+
```solidity
1033+
type TypeName is UnderlyingType;
1034+
```
1035+
1036+
- `TypeName`: The name of your new type (PascalCase by convention).
1037+
- `UnderlyingType`: Any built-in value type (e.g., `uint256`, `int128`, `bool`, `bytes32`, etc.).
1038+
1039+
```solidity
1040+
type UserId is uint256;
1041+
type Price is uint128;
1042+
type Flag is bool;
1043+
```
1044+
1045+
### Wrapping and Unwrapping
1046+
1047+
- Wrapping: Converting an underlying type to a user-defined type.
1048+
- Unwrapping: Converting a user-defined type back to its underlying type.
1049+
1050+
```solidity
1051+
uint256 rawId = 123;
1052+
// Wrap a uint256 into a UserId
1053+
UserId userId = UserId.wrap(rawId);
1054+
1055+
// Unwrap a UserId to a uint256
1056+
uint256 unwrappedId = UserId.unwrap(userId);
1057+
1058+
// ❌ This will fail: Type uint256 is not implicitly convertible to UserId
1059+
UserId invalidConversion = 123;
1060+
```
1061+
1062+
### Operations and Conversions
1063+
1064+
By default, **no** arithmetic or comparison operations are defined for user-defined value types. If you need to perform operations on your custom type, you can:
1065+
1066+
1. **Unwrap** your custom type, perform the operations on the underlying type, then **re-wrap** the result.
1067+
2. Define **inline** or **library** functions that handle the logic for your custom type.
1068+
1069+
```solidity
1070+
pragma solidity ^0.8.29;
1071+
1072+
type Distance is uint256;
1073+
1074+
library DistanceLib {
1075+
function add(Distance a, Distance b) internal pure returns (Distance) {
1076+
// unwrap, add, then re-wrap
1077+
return Distance.wrap(Distance.unwrap(a) + Distance.unwrap(b));
1078+
}
1079+
1080+
function greaterThan(Distance a, Distance b) internal pure returns (bool) {
1081+
return Distance.unwrap(a) > Distance.unwrap(b);
1082+
}
1083+
}
1084+
1085+
contract Road {
1086+
using DistanceLib for Distance;
1087+
1088+
// store total length of roads
1089+
Distance public totalDistance;
1090+
1091+
function addDistance(Distance d) external {
1092+
totalDistance = totalDistance.add(d);
1093+
// internally uses DistanceLib.add
1094+
}
1095+
1096+
function compareDistances(Distance a, Distance b) external pure returns (bool) {
1097+
return a.greaterThan(b);
1098+
}
1099+
}
1100+
1101+
```
1102+
10111103
## Enums
10121104

10131105
- Enums define a finite list of constant values, improving code clarity when dealing with limited states.
@@ -1845,6 +1937,14 @@ fallback() external [payable] {
18451937
- **Event Logging**:
18461938
- If your contract receives Ether, consider emitting an event to easily track inbound transfers.
18471939

1940+
# Data Locations
1941+
1942+
# Transient Storage
1943+
1944+
# Send Ether
1945+
1946+
# Call & Delegatecall
1947+
18481948
# References
18491949

18501950
- [Solidity Docs](https://docs.soliditylang.org/en/latest/)

0 commit comments

Comments
 (0)