File size: 16,796 Bytes
1295969
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.13;

import {Test} from "../src/Test.sol";
import {Variable, Type, TypeKind, LibVariable} from "../src/LibVariable.sol";

contract LibVariableTest is Test {
    using LibVariable for Type;
    using LibVariable for TypeKind;

    LibVariableHelper internal helper;

    bytes internal expectedErr;
    Variable internal uninitVar;
    Variable internal boolVar;
    Variable internal addressVar;
    Variable internal bytes32Var;
    Variable internal uintVar;
    Variable internal intVar;
    Variable internal stringVar;
    Variable internal bytesVar;
    Variable internal boolArrayVar;
    Variable internal addressArrayVar;
    Variable internal bytes32ArrayVar;
    Variable internal uintArrayVar;
    Variable internal intArrayVar;
    Variable internal stringArrayVar;
    Variable internal bytesArrayVar;

    function setUp() public {
        helper = new LibVariableHelper();

        // UNINITIALIZED
        uninitVar = Variable(Type(TypeKind.None, false), "");

        // SINGLE VALUES
        boolVar = Variable(Type(TypeKind.Bool, false), abi.encode(true));
        addressVar = Variable(Type(TypeKind.Address, false), abi.encode(address(0xdeadbeef)));
        bytes32Var = Variable(Type(TypeKind.Bytes32, false), abi.encode(bytes32(uint256(42))));
        uintVar = Variable(Type(TypeKind.Uint256, false), abi.encode(uint256(123)));
        intVar = Variable(Type(TypeKind.Int256, false), abi.encode(int256(-123)));
        stringVar = Variable(Type(TypeKind.String, false), abi.encode("hello world"));
        bytesVar = Variable(Type(TypeKind.Bytes, false), abi.encode(hex"c0ffee"));

        // ARRAY VALUES
        bool[] memory bools = new bool[](2);
        bools[0] = true;
        bools[1] = false;
        boolArrayVar = Variable(Type(TypeKind.Bool, true), abi.encode(bools));

        address[] memory addrs = new address[](2);
        addrs[0] = address(0x1);
        addrs[1] = address(0x2);
        addressArrayVar = Variable(Type(TypeKind.Address, true), abi.encode(addrs));

        bytes32[] memory b32s = new bytes32[](2);
        b32s[0] = bytes32(uint256(1));
        b32s[1] = bytes32(uint256(2));
        bytes32ArrayVar = Variable(Type(TypeKind.Bytes32, true), abi.encode(b32s));

        uint256[] memory uints = new uint256[](2);
        uints[0] = 1;
        uints[1] = 2;
        uintArrayVar = Variable(Type(TypeKind.Uint256, true), abi.encode(uints));

        int256[] memory ints = new int256[](2);
        ints[0] = -1;
        ints[1] = 2;
        intArrayVar = Variable(Type(TypeKind.Int256, true), abi.encode(ints));

        string[] memory strings = new string[](2);
        strings[0] = "one";
        strings[1] = "two";
        stringArrayVar = Variable(Type(TypeKind.String, true), abi.encode(strings));

        bytes[] memory b = new bytes[](2);
        b[0] = hex"01";
        b[1] = hex"02";
        bytesArrayVar = Variable(Type(TypeKind.Bytes, true), abi.encode(b));
    }

    // -- SUCCESS CASES --------------------------------------------------------

    function test_TypeHelpers() public view {
        // TypeKind.toString()
        assertEq(TypeKind.None.toString(), "none");
        assertEq(TypeKind.Bool.toString(), "bool");
        assertEq(TypeKind.Address.toString(), "address");
        assertEq(TypeKind.Bytes32.toString(), "bytes32");
        assertEq(TypeKind.Uint256.toString(), "uint256");
        assertEq(TypeKind.Int256.toString(), "int256");
        assertEq(TypeKind.String.toString(), "string");
        assertEq(TypeKind.Bytes.toString(), "bytes");

        // TypeKind.toTomlKey()
        assertEq(TypeKind.Uint256.toTomlKey(), "uint");
        assertEq(TypeKind.Int256.toTomlKey(), "int");
        assertEq(TypeKind.Bytes32.toTomlKey(), "bytes32");

        // Type.toString()
        assertEq(boolVar.ty.toString(), "bool");
        assertEq(boolArrayVar.ty.toString(), "bool[]");
        assertEq(uintVar.ty.toString(), "uint256");
        assertEq(uintArrayVar.ty.toString(), "uint256[]");
        assertEq(uninitVar.ty.toString(), "none");

        // Type.isEqual()
        assertTrue(boolVar.ty.isEqual(Type(TypeKind.Bool, false)));
        assertFalse(boolVar.ty.isEqual(Type(TypeKind.Bool, true)));
        assertFalse(boolVar.ty.isEqual(Type(TypeKind.Address, false)));

        // Type.assertEq()
        boolVar.ty.assertEq(Type(TypeKind.Bool, false));
        uintArrayVar.ty.assertEq(Type(TypeKind.Uint256, true));
    }

    function test_Coercion() public view {
        // Single values
        assertTrue(helper.toBool(boolVar));
        assertEq(helper.toAddress(addressVar), address(0xdeadbeef));
        assertEq(helper.toBytes32(bytes32Var), bytes32(uint256(42)));
        assertEq(helper.toUint256(uintVar), 123);
        assertEq(helper.toInt256(intVar), -123);
        assertEq(helper.toString(stringVar), "hello world");
        assertEq(helper.toBytes(bytesVar), hex"c0ffee");

        // Bool array
        bool[] memory bools = helper.toBoolArray(boolArrayVar);
        assertEq(bools.length, 2);
        assertTrue(bools[0]);
        assertFalse(bools[1]);

        // Address array
        address[] memory addrs = helper.toAddressArray(addressArrayVar);
        assertEq(addrs.length, 2);
        assertEq(addrs[0], address(0x1));
        assertEq(addrs[1], address(0x2));

        // String array
        string[] memory strings = helper.toStringArray(stringArrayVar);
        assertEq(strings.length, 2);
        assertEq(strings[0], "one");
        assertEq(strings[1], "two");

        // Bytes32 array
        bytes32[] memory b32s = helper.toBytes32Array(bytes32ArrayVar);
        assertEq(b32s.length, 2);
        assertEq(b32s[0], bytes32(uint256(1)));
        assertEq(b32s[1], bytes32(uint256(2)));

        // Int array
        int256[] memory ints = helper.toInt256Array(intArrayVar);
        assertEq(ints.length, 2);
        assertEq(ints[0], -1);
        assertEq(ints[1], 2);

        // Bytes array
        bytes[] memory b = helper.toBytesArray(bytesArrayVar);
        assertEq(b.length, 2);
        assertEq(b[0], hex"01");
        assertEq(b[1], hex"02");
    }

    function test_Downcasting() public view {
        // Uint downcasting
        Variable memory v_uint_small = Variable(Type(TypeKind.Uint256, false), abi.encode(uint256(100)));
        assertEq(helper.toUint128(v_uint_small), 100);
        assertEq(helper.toUint64(v_uint_small), 100);
        assertEq(helper.toUint32(v_uint_small), 100);
        assertEq(helper.toUint16(v_uint_small), 100);
        assertEq(helper.toUint8(v_uint_small), 100);

        // Uint array downcasting
        uint256[] memory small_uints = new uint256[](2);
        small_uints[0] = 10;
        small_uints[1] = 20;
        Variable memory v_uint_array_small = Variable(Type(TypeKind.Uint256, true), abi.encode(small_uints));
        uint8[] memory u8_array = helper.toUint8Array(v_uint_array_small);
        assertEq(u8_array[0], 10);
        assertEq(u8_array[1], 20);

        // Int downcasting
        Variable memory v_int_small_pos = Variable(Type(TypeKind.Int256, false), abi.encode(int256(100)));
        Variable memory v_int_small_neg = Variable(Type(TypeKind.Int256, false), abi.encode(int256(-100)));
        assertEq(helper.toInt128(v_int_small_pos), 100);
        assertEq(helper.toInt64(v_int_small_neg), -100);
        assertEq(helper.toInt32(v_int_small_pos), 100);
        assertEq(helper.toInt16(v_int_small_neg), -100);
        assertEq(helper.toInt8(v_int_small_pos), 100);

        // Int array downcasting
        int256[] memory small_ints = new int256[](2);
        small_ints[0] = -10;
        small_ints[1] = 20;
        Variable memory intArraySmall = Variable(Type(TypeKind.Int256, true), abi.encode(small_ints));
        int8[] memory i8_array = helper.toInt8Array(intArraySmall);
        assertEq(i8_array[0], -10);
        assertEq(i8_array[1], 20);
    }

    // -- REVERT CASES ---------------------------------------------------------

    function testRevert_NotInitialized() public {
        vm.expectRevert(LibVariable.NotInitialized.selector);
        helper.toBool(uninitVar);

        vm.expectRevert(LibVariable.NotInitialized.selector);
        helper.toAddressArray(uninitVar);
    }

    function testRevert_assertExists() public {
        vm.expectRevert(LibVariable.NotInitialized.selector);
        helper.assertExists(uninitVar);
    }

    function testRevert_TypeMismatch() public {
        // Single values
        vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "uint256", "bool"));
        helper.toUint256(boolVar);

        vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "address", "string"));
        helper.toAddress(stringVar);

        // Arrays
        vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "uint256[]", "bool[]"));
        helper.toUint256Array(boolArrayVar);

        vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "address[]", "string[]"));
        helper.toAddressArray(stringArrayVar);

        // Single value to array
        vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "bool[]", "bool"));
        helper.toBoolArray(boolVar);

        // Array to single value
        vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "bool", "bool[]"));
        helper.toBool(boolArrayVar);

        // assertEq reverts
        vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "uint256", "bool"));
        helper.assertEq(boolVar.ty, Type(TypeKind.Uint256, false));
    }

    function testRevert_UnsafeCast() public {
        // uint overflow
        Variable memory uintLarge = Variable(Type(TypeKind.Uint256, false), abi.encode(uint256(type(uint128).max) + 1));
        expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value does not fit in 'uint128'");
        vm.expectRevert(expectedErr);
        helper.toUint128(uintLarge);

        // int overflow
        Variable memory intLarge = Variable(Type(TypeKind.Int256, false), abi.encode(int256(type(int128).max) + 1));
        expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value does not fit in 'int128'");

        vm.expectRevert(expectedErr);
        helper.toInt128(intLarge);

        // int underflow
        Variable memory intSmall = Variable(Type(TypeKind.Int256, false), abi.encode(int256(type(int128).min) - 1));
        expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value does not fit in 'int128'");

        vm.expectRevert(expectedErr);
        helper.toInt128(intSmall);

        // uint array overflow
        uint256[] memory uintArray = new uint256[](2);
        uintArray[0] = 10;
        uintArray[1] = uint256(type(uint64).max) + 1;
        Variable memory uintArrayLarge = Variable(Type(TypeKind.Uint256, true), abi.encode(uintArray));
        expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value in array does not fit in 'uint64'");

        vm.expectRevert(expectedErr);
        helper.toUint64Array(uintArrayLarge);

        // int array overflow
        int256[] memory intArray = new int256[](2);
        intArray[0] = 10;
        intArray[1] = int256(type(int64).max) + 1;
        Variable memory intArrayLarge = Variable(Type(TypeKind.Int256, true), abi.encode(intArray));
        expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value in array does not fit in 'int64'");

        vm.expectRevert(expectedErr);
        helper.toInt64Array(intArrayLarge);

        // int array underflow
        intArray[0] = 10;
        intArray[1] = int256(type(int64).min) - 1;
        Variable memory intArraySmall = Variable(Type(TypeKind.Int256, true), abi.encode(intArray));
        expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value in array does not fit in 'int64'");

        vm.expectRevert(expectedErr);
        helper.toInt64Array(intArraySmall);
    }
}

/// @dev We must use an external helper contract to ensure proper call depth for `vm.expectRevert`,
///      as direct library calls are inlined by the compiler, causing call depth issues.
contract LibVariableHelper {
    using LibVariable for Type;
    using LibVariable for TypeKind;

    // Assertions
    function assertExists(Variable memory v) external pure {
        v.assertExists();
    }

    function assertEq(Type memory t1, Type memory t2) external pure {
        t1.assertEq(t2);
    }

    // Single Value Coercion
    function toBool(Variable memory v) external pure returns (bool) {
        return v.toBool();
    }

    function toAddress(Variable memory v) external pure returns (address) {
        return v.toAddress();
    }

    function toBytes32(Variable memory v) external pure returns (bytes32) {
        return v.toBytes32();
    }

    function toUint256(Variable memory v) external pure returns (uint256) {
        return v.toUint256();
    }

    function toInt256(Variable memory v) external pure returns (int256) {
        return v.toInt256();
    }

    function toString(Variable memory v) external pure returns (string memory) {
        return v.toString();
    }

    function toBytes(Variable memory v) external pure returns (bytes memory) {
        return v.toBytes();
    }

    // Array Coercion
    function toBoolArray(Variable memory v) external pure returns (bool[] memory) {
        return v.toBoolArray();
    }

    function toAddressArray(Variable memory v) external pure returns (address[] memory) {
        return v.toAddressArray();
    }

    function toBytes32Array(Variable memory v) external pure returns (bytes32[] memory) {
        return v.toBytes32Array();
    }

    function toUint256Array(Variable memory v) external pure returns (uint256[] memory) {
        return v.toUint256Array();
    }

    function toInt256Array(Variable memory v) external pure returns (int256[] memory) {
        return v.toInt256Array();
    }

    function toStringArray(Variable memory v) external pure returns (string[] memory) {
        return v.toStringArray();
    }

    function toBytesArray(Variable memory v) external pure returns (bytes[] memory) {
        return v.toBytesArray();
    }

    // Uint Downcasting
    function toUint128(Variable memory v) external pure returns (uint128) {
        return v.toUint128();
    }

    function toUint64(Variable memory v) external pure returns (uint64) {
        return v.toUint64();
    }

    function toUint32(Variable memory v) external pure returns (uint32) {
        return v.toUint32();
    }

    function toUint16(Variable memory v) external pure returns (uint16) {
        return v.toUint16();
    }

    function toUint8(Variable memory v) external pure returns (uint8) {
        return v.toUint8();
    }

    // Int Downcasting
    function toInt128(Variable memory v) external pure returns (int128) {
        return v.toInt128();
    }

    function toInt64(Variable memory v) external pure returns (int64) {
        return v.toInt64();
    }

    function toInt32(Variable memory v) external pure returns (int32) {
        return v.toInt32();
    }

    function toInt16(Variable memory v) external pure returns (int16) {
        return v.toInt16();
    }

    function toInt8(Variable memory v) external pure returns (int8) {
        return v.toInt8();
    }

    // Uint Array Downcasting
    function toUint128Array(Variable memory v) external pure returns (uint128[] memory) {
        return v.toUint128Array();
    }

    function toUint64Array(Variable memory v) external pure returns (uint64[] memory) {
        return v.toUint64Array();
    }

    function toUint32Array(Variable memory v) external pure returns (uint32[] memory) {
        return v.toUint32Array();
    }

    function toUint16Array(Variable memory v) external pure returns (uint16[] memory) {
        return v.toUint16Array();
    }

    function toUint8Array(Variable memory v) external pure returns (uint8[] memory) {
        return v.toUint8Array();
    }

    // Int Array Downcasting
    function toInt128Array(Variable memory v) external pure returns (int128[] memory) {
        return v.toInt128Array();
    }

    function toInt64Array(Variable memory v) external pure returns (int64[] memory) {
        return v.toInt64Array();
    }

    function toInt32Array(Variable memory v) external pure returns (int32[] memory) {
        return v.toInt32Array();
    }

    function toInt16Array(Variable memory v) external pure returns (int16[] memory) {
        return v.toInt16Array();
    }

    function toInt8Array(Variable memory v) external pure returns (int8[] memory) {
        return v.toInt8Array();
    }
}