Semantic tests for User Defined Value types

This commit is contained in:
hrkrshnn 2021-08-17 11:29:45 +02:00
parent 229f50eef1
commit 5f393d1694
11 changed files with 555 additions and 0 deletions

View File

@ -0,0 +1,65 @@
pragma abicoder v2;
type MyAddress is address;
contract C {
MyAddress[] public addresses;
function f(MyAddress[] calldata _addresses) external {
for (uint i = 0; i < _addresses.length; i++) {
MyAddress.unwrap(_addresses[i]).call("");
}
addresses = _addresses;
}
function g(MyAddress[] memory _addresses) external {
for (uint i = 0; i < _addresses.length; i++) {
MyAddress.unwrap(_addresses[i]).call("");
}
addresses = _addresses;
}
function test_f() external returns (bool) {
clean();
MyAddress[] memory test = new MyAddress[](3);
test[0] = MyAddress.wrap(address(21));
test[1] = MyAddress.wrap(address(22));
test[2] = MyAddress.wrap(address(23));
this.f(test);
test_equality(test);
return true;
}
function test_g() external returns (bool) {
clean();
MyAddress[] memory test = new MyAddress[](5);
test[0] = MyAddress.wrap(address(24));
test[1] = MyAddress.wrap(address(25));
test[2] = MyAddress.wrap(address(26));
test[3] = MyAddress.wrap(address(27));
test[4] = MyAddress.wrap(address(28));
this.g(test);
test_equality(test);
return true;
}
function clean() internal {
delete addresses;
}
function test_equality(MyAddress[] memory _addresses) internal view {
require (_addresses.length == addresses.length);
for (uint i = 0; i < _addresses.length; i++) {
require(MyAddress.unwrap(_addresses[i]) == MyAddress.unwrap(addresses[i]));
}
}
}
// ====
// compileViaYul: also
// ----
// test_f() -> true
// gas irOptimized: 122655
// gas legacy: 125037
// gas legacyOptimized: 122605
// test_g() -> true
// gas irOptimized: 95940
// gas legacy: 100656
// gas legacyOptimized: 96057
// addresses(uint256): 0 -> 0x18
// addresses(uint256): 1 -> 0x19
// addresses(uint256): 3 -> 0x1b
// addresses(uint256): 4 -> 0x1c
// addresses(uint256): 5 -> FAILURE

View File

@ -0,0 +1,44 @@
pragma abicoder v2;
type MyUInt8 is uint8;
// Note that this wraps from a uint256
function wrap(uint x) pure returns (MyUInt8 y) { assembly { y := x } }
function unwrap(MyUInt8 x) pure returns (uint8 y) { assembly { y := x } }
contract C {
uint8 a;
MyUInt8 b;
uint8 c;
function ret() external returns(MyUInt8) {
return wrap(0x1ff);
}
function f(MyUInt8 x) external returns(MyUInt8) {
return x;
}
function mem() external returns (MyUInt8[] memory) {
MyUInt8[] memory x = new MyUInt8[](2);
x[0] = wrap(0x1ff);
x[1] = wrap(0xff);
require(unwrap(x[0]) == unwrap(x[1]));
assembly {
mstore(add(x, 0x20), 0x1ff)
}
require(unwrap(x[0]) == unwrap(x[1]));
return x;
}
function stor() external returns (uint8, MyUInt8, uint8) {
a = 1;
c = 2;
b = wrap(0x1ff);
return (a, b, c);
}
}
// ====
// compileViaYul: also
// ----
// ret() -> 0xff
// f(uint8): 0x1ff -> FAILURE
// f(uint8): 0xff -> 0xff
// mem() -> 0x20, 2, 0xff, 0xff
// stor() -> 1, 0xff, 2

View File

@ -0,0 +1,44 @@
pragma abicoder v1;
type MyUInt8 is uint8;
// Note that this wraps from a uint256
function wrap(uint x) pure returns (MyUInt8 y) { assembly { y := x } }
function unwrap(MyUInt8 x) pure returns (uint8 y) { assembly { y := x } }
contract C {
uint8 a;
MyUInt8 b;
uint8 c;
function ret() external returns(MyUInt8) {
return wrap(0x1ff);
}
function f(MyUInt8 x) external returns(MyUInt8) {
return x;
}
function mem() external returns (MyUInt8[] memory) {
MyUInt8[] memory x = new MyUInt8[](2);
x[0] = wrap(0x1ff);
x[1] = wrap(0xff);
require(unwrap(x[0]) == unwrap(x[1]));
assembly {
mstore(add(x, 0x20), 0x1ff)
}
require(unwrap(x[0]) == unwrap(x[1]));
return x;
}
function stor() external returns (uint8, MyUInt8, uint8) {
a = 1;
c = 2;
b = wrap(0x1ff);
return (a, b, c);
}
}
// ====
// compileViaYul: false
// ----
// ret() -> 0xff
// f(uint8): 0x1ff -> 0xff
// f(uint8): 0xff -> 0xff
// mem() -> 0x20, 2, 0x01ff, 0xff
// stor() -> 1, 0xff, 2

View File

@ -0,0 +1,57 @@
pragma abicoder v2;
type MyUInt8 is uint8;
type MyInt8 is int8;
type MyUInt16 is uint16;
contract C {
function f(uint a) external returns(MyUInt8) {
return MyUInt8.wrap(uint8(a));
}
function g(uint a) external returns(MyInt8) {
return MyInt8.wrap(int8(int(a)));
}
function h(MyUInt8 a) external returns (MyInt8) {
return MyInt8.wrap(int8(MyUInt8.unwrap(a)));
}
function i(MyUInt8 a) external returns(MyUInt16) {
return MyUInt16.wrap(MyUInt8.unwrap(a));
}
function j(MyUInt8 a) external returns (uint) {
return MyUInt8.unwrap(a);
}
function k(MyUInt8 a) external returns (MyUInt16) {
return MyUInt16.wrap(MyUInt8.unwrap(a));
}
function m(MyUInt16 a) external returns (MyUInt8) {
return MyUInt8.wrap(uint8(MyUInt16.unwrap(a)));
}
}
// ====
// compileViaYul: also
// ----
// f(uint256): 1 -> 1
// f(uint256): 2 -> 2
// f(uint256): 257 -> 1
// g(uint256): 1 -> 1
// g(uint256): 2 -> 2
// g(uint256): 255 -> -1
// g(uint256): 257 -> 1
// h(uint8): 1 -> 1
// h(uint8): 2 -> 2
// h(uint8): 255 -> -1
// h(uint8): 257 -> FAILURE
// i(uint8): 250 -> 250
// j(uint8): 1 -> 1
// j(uint8): 2 -> 2
// j(uint8): 255 -> 0xff
// j(uint8): 257 -> FAILURE
// k(uint8): 1 -> 1
// k(uint8): 2 -> 2
// k(uint8): 255 -> 0xff
// k(uint8): 257 -> FAILURE
// m(uint16): 1 -> 1
// m(uint16): 2 -> 2
// m(uint16): 255 -> 0xff
// m(uint16): 257 -> 1

View File

@ -0,0 +1,57 @@
pragma abicoder v1;
type MyUInt8 is uint8;
type MyInt8 is int8;
type MyUInt16 is uint16;
contract C {
function f(uint a) external returns(MyUInt8) {
return MyUInt8.wrap(uint8(a));
}
function g(uint a) external returns(MyInt8) {
return MyInt8.wrap(int8(int(a)));
}
function h(MyUInt8 a) external returns (MyInt8) {
return MyInt8.wrap(int8(MyUInt8.unwrap(a)));
}
function i(MyUInt8 a) external returns(MyUInt16) {
return MyUInt16.wrap(MyUInt8.unwrap(a));
}
function j(MyUInt8 a) external returns (uint) {
return MyUInt8.unwrap(a);
}
function k(MyUInt8 a) external returns (MyUInt16) {
return MyUInt16.wrap(MyUInt8.unwrap(a));
}
function m(MyUInt16 a) external returns (MyUInt8) {
return MyUInt8.wrap(uint8(MyUInt16.unwrap(a)));
}
}
// ====
// compileViaYul: false
// ----
// f(uint256): 1 -> 1
// f(uint256): 2 -> 2
// f(uint256): 257 -> 1
// g(uint256): 1 -> 1
// g(uint256): 2 -> 2
// g(uint256): 255 -> -1
// g(uint256): 257 -> 1
// h(uint8): 1 -> 1
// h(uint8): 2 -> 2
// h(uint8): 255 -> -1
// h(uint8): 257 -> 1
// i(uint8): 250 -> 250
// j(uint8): 1 -> 1
// j(uint8): 2 -> 2
// j(uint8): 255 -> 0xff
// j(uint8): 257 -> 1
// k(uint8): 1 -> 1
// k(uint8): 2 -> 2
// k(uint8): 255 -> 0xff
// k(uint8): 257 -> 1
// m(uint16): 1 -> 1
// m(uint16): 2 -> 2
// m(uint16): 255 -> 0xff
// m(uint16): 257 -> 1

View File

@ -0,0 +1,147 @@
pragma abicoder v2;
// A rewrite of the test/libsolidity/semanticTests/various/erc20.sol, but using user defined value
// types.
// User defined type name. Indicating a type with 18 decimals.
type UFixed18 is uint256;
library FixedMath
{
function add(UFixed18 a, UFixed18 b) internal pure returns (UFixed18 c) {
return UFixed18.wrap(UFixed18.unwrap(a) + UFixed18.unwrap(b));
}
function sub(UFixed18 a, UFixed18 b) internal pure returns (UFixed18 c) {
return UFixed18.wrap(UFixed18.unwrap(a) - UFixed18.unwrap(b));
}
}
contract ERC20 {
using FixedMath for UFixed18;
event Transfer(address indexed from, address indexed to, UFixed18 value);
event Approval(address indexed owner, address indexed spender, UFixed18 value);
mapping (address => UFixed18) private _balances;
mapping (address => mapping (address => UFixed18)) private _allowances;
UFixed18 private _totalSupply;
constructor() {
_mint(msg.sender, UFixed18.wrap(20));
}
function totalSupply() public view returns (UFixed18) {
return _totalSupply;
}
function balanceOf(address owner) public view returns (UFixed18) {
return _balances[owner];
}
function allowance(address owner, address spender) public view returns (UFixed18) {
return _allowances[owner][spender];
}
function transfer(address to, UFixed18 value) public returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function approve(address spender, UFixed18 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transferFrom(address from, address to, UFixed18 value) public returns (bool) {
_transfer(from, to, value);
// The subtraction here will revert on overflow.
_approve(from, msg.sender, _allowances[from][msg.sender].sub(value));
return true;
}
function increaseAllowance(address spender, UFixed18 addedValue) public returns (bool) {
// The addition here will revert on overflow.
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}
function decreaseAllowance(address spender, UFixed18 subtractedValue) public returns (bool) {
// The subtraction here will revert on overflow.
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}
function _transfer(address from, address to, UFixed18 value) internal {
require(to != address(0), "ERC20: transfer to the zero address");
// The subtraction and addition here will revert on overflow.
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(from, to, value);
}
function _mint(address account, UFixed18 value) internal {
require(account != address(0), "ERC20: mint to the zero address");
// The additions here will revert on overflow.
_totalSupply = _totalSupply.add(value);
_balances[account] = _balances[account].add(value);
emit Transfer(address(0), account, value);
}
function _burn(address account, UFixed18 value) internal {
require(account != address(0), "ERC20: burn from the zero address");
// The subtractions here will revert on overflow.
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
function _approve(address owner, address spender, UFixed18 value) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _burnFrom(address account, UFixed18 value) internal {
_burn(account, value);
_approve(account, msg.sender, _allowances[account][msg.sender].sub(value));
}
}
// ====
// compileViaYul: also
// ----
// constructor()
// ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14
// gas irOptimized: 460447
// gas legacy: 861547
// gas legacyOptimized: 420959
// totalSupply() -> 20
// gas irOptimized: 23378
// gas legacy: 23653
// gas legacyOptimized: 23368
// transfer(address,uint256): 2, 5 -> true
// ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05
// gas irOptimized: 48514
// gas legacy: 49572
// gas legacyOptimized: 48575
// decreaseAllowance(address,uint256): 2, 0 -> true
// ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00
// gas irOptimized: 26316
// gas legacy: 27204
// gas legacyOptimized: 26317
// decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11
// gas irOptimized: 24040
// gas legacy: 24506
// gas legacyOptimized: 24077
// transfer(address,uint256): 2, 14 -> true
// ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e
// gas irOptimized: 28614
// gas legacy: 29672
// gas legacyOptimized: 28675
// transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11
// gas irOptimized: 24052
// gas legacy: 24492
// gas legacyOptimized: 24074

View File

@ -0,0 +1,42 @@
// Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type.
type UFixed256x18 is uint256;
/// A minimal library to do fixed point operations on UFixed256x18.
library FixedMath {
/// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked arithmetic on
/// uint256.
function add(UFixed256x18 a, UFixed256x18 b) internal returns (UFixed256x18) {
return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b));
}
/// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked arithmetic on
/// uint256.
function mul(UFixed256x18 a, uint256 b) internal returns (UFixed256x18) {
return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b);
}
/// Truncates UFixed256x18 to the nearest uint256 number.
function truncate(UFixed256x18 a) internal returns (uint256) {
return UFixed256x18.unwrap(a) / 10**18;
}
}
contract TestFixedMath {
function add(UFixed256x18 a, UFixed256x18 b) external returns (UFixed256x18) {
return FixedMath.add(a, b);
}
function mul(UFixed256x18 a, uint256 b) external returns (UFixed256x18) {
return FixedMath.mul(a, b);
}
function truncate(UFixed256x18 a) external returns (uint256) {
return FixedMath.truncate(a);
}
}
// ====
// compileViaYul: also
// ----
// add(uint256,uint256): 0, 0 -> 0
// add(uint256,uint256): 25, 45 -> 0x46
// add(uint256,uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935, 10 -> FAILURE, hex"4e487b71", 0x11
// mul(uint256,uint256): 340282366920938463463374607431768211456, 45671926166590716193865151022383844364247891968 -> FAILURE, hex"4e487b71", 0x11
// mul(uint256,uint256): 340282366920938463463374607431768211456, 20 -> 6805647338418769269267492148635364229120
// truncate(uint256): 11579208923731619542357098500868790785326998665640564039457584007913129639930 -> 11579208923731619542357098500868790785326998665640564039457
// truncate(uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935 -> 115792089237316195423570985008687907853269984665640564039457

View File

@ -0,0 +1,32 @@
// Implementation of OpenZepplin's
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
// using user defined value types.
contract Ownable {
type Owner is address;
Owner public owner = Owner.wrap(msg.sender);
error OnlyOwner();
modifier onlyOwner() {
if (Owner.unwrap(owner) != msg.sender)
revert OnlyOwner();
_;
}
event OwnershipTransferred(Owner indexed previousOwner, Owner indexed newOwner);
function setOwner(Owner newOwner) onlyOwner external {
emit OwnershipTransferred({previousOwner: owner, newOwner: newOwner});
owner = newOwner;
}
function renounceOwnership() onlyOwner external {
owner = Owner.wrap(address(0));
}
}
// ====
// compileViaYul: also
// ----
// owner() -> 0x1212121212121212121212121212120000000012
// setOwner(address): 0x1212121212121212121212121212120000000012 ->
// ~ emit OwnershipTransferred(address,address): #0x1212121212121212121212121212120000000012, #0x1212121212121212121212121212120000000012
// renounceOwnership() ->
// owner() -> 0
// setOwner(address): 0x1212121212121212121212121212120000000012 -> FAILURE, hex"5fc483c5"

View File

@ -0,0 +1,42 @@
pragma abicoder v2;
type MyAddress is address;
contract C {
function id(MyAddress a) external returns (MyAddress b) {
b = a;
}
function unwrap_assembly(MyAddress a) external returns (address b) {
assembly { b := a }
}
function wrap_assembly(address a) external returns (MyAddress b) {
assembly { b := a }
}
function unwrap(MyAddress a) external returns (address b) {
b = MyAddress.unwrap(a);
}
function wrap(address a) external returns (MyAddress b) {
b = MyAddress.wrap(a);
}
}
// ====
// compileViaYul: also
// ----
// id(address): 5 -> 5
// id(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
// id(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
// unwrap(address): 5 -> 5
// unwrap(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
// unwrap(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
// wrap(address): 5 -> 5
// wrap(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
// wrap(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
// unwrap_assembly(address): 5 -> 5
// unwrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
// unwrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
// wrap_assembly(address): 5 -> 5
// wrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff
// wrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE

View File

@ -0,0 +1,14 @@
type MyInt is int;
contract C {
function f() external pure returns (MyInt a) {
}
function g() external pure returns (MyInt b, MyInt c) {
b = MyInt.wrap(int(1));
c = MyInt.wrap(1);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0
// g() -> 1, 1

View File

@ -0,0 +1,11 @@
type MyAddress is address;
contract C {
function f() pure public {
MyAddress.wrap;
MyAddress.unwrap;
}
}
// ====
// compileViaYul: also
// ----
// f() ->