Skip to content

Commit 167a3c8

Browse files
authored
Merge pull request #35 from metacontract/improve/devkit-testing
Improve Type Safety & DevKit Testing
2 parents 115b26c + 206e18e commit 167a3c8

File tree

88 files changed

+3097
-2583
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+3097
-2583
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@ jobs:
4242
run: |
4343
forge test
4444
45+
install-with-template-test:
46+
runs-on: ubuntu-latest
47+
steps:
48+
- uses: actions/checkout@v4
49+
with:
50+
submodules: recursive
51+
- name: Install Foundry
52+
uses: foundry-rs/foundry-toolchain@v1
53+
with:
54+
version: nightly
55+
- name: Run forge install
56+
run: |
57+
mkdir /tmp/install-with-template-test
58+
cd /tmp/install-with-template-test
59+
forge init mc-example-project -t metacontract/template
60+
forge test
61+
4562
# slither:
4663
# runs-on: ubuntu-latest
4764
# steps:

.gitmodules

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,3 @@
44
[submodule "lib/ucs-contracts"]
55
path = lib/ucs-contracts
66
url = https://github.com/ecdysisxyz/ucs-contracts
7-
[submodule "lib/openzeppelin-contracts"]
8-
path = lib/openzeppelin-contracts
9-
url = https://github.com/openzeppelin/openzeppelin-contracts

devkit/MCDevKit.sol

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,29 @@
22
pragma solidity ^0.8.24;
33

44
// Registries
5-
import {FunctionRegistry} from "devkit/ucs/functions/FunctionRegistry.sol";
6-
import {DictionaryRegistry} from "devkit/ucs/dictionary/DictionaryRegistry.sol";
7-
import {ProxyRegistry} from "devkit/ucs/proxy/ProxyRegistry.sol";
5+
import {StdRegistry} from "devkit/registry/StdRegistry.sol";
6+
import {FunctionRegistry} from "devkit/registry/FunctionRegistry.sol";
7+
import {BundleRegistry} from "devkit/registry/BundleRegistry.sol";
8+
import {DictionaryRegistry} from "devkit/registry/DictionaryRegistry.sol";
9+
import {ProxyRegistry} from "devkit/registry/ProxyRegistry.sol";
810

911
// Global Methods
10-
import {MCSetupLib} from "devkit/global/MCSetupLib.sol";
11-
import {MCBundleLib} from "devkit/global/MCBundleLib.sol";
12-
import {MCDeployLib} from "devkit/global/MCDeployLib.sol";
13-
import {MCFinderLib} from "devkit/global/MCFinderLib.sol";
14-
import {MCContextLib} from "devkit/global/MCContextLib.sol";
15-
import {MCTestLib} from "devkit/global/MCTestLib.sol";
16-
import {MCDebugLib} from "devkit/global/MCDebugLib.sol";
12+
import {MCSetupLib} from "devkit/utils/global/MCSetupLib.sol";
13+
import {MCBundleLib} from "devkit/utils/global/MCBundleLib.sol";
14+
import {MCDeployLib} from "devkit/utils/global/MCDeployLib.sol";
15+
import {MCFinderLib} from "devkit/utils/global/MCFinderLib.sol";
16+
import {MCContextLib} from "devkit/utils/global/MCContextLib.sol";
17+
import {MCTestLib} from "devkit/utils/global/MCTestLib.sol";
18+
import {MCDebugLib} from "devkit/utils/global/MCDebugLib.sol";
1719

1820

1921
/********************************
2022
🌟 Meta Contract DevKit
2123
*********************************/
2224
struct MCDevKit {
25+
StdRegistry std;
2326
FunctionRegistry functions;
27+
BundleRegistry bundle;
2428
DictionaryRegistry dictionary;
2529
ProxyRegistry proxy;
2630
}

devkit/MCScript.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.24;
33

4-
import {Config} from "devkit/config/Config.sol";
4+
import {System} from "devkit/system/System.sol";
55

66
// 💬 ABOUT
77
// Meta Contract's default Script based on Forge Std Script
@@ -12,16 +12,16 @@ import {MCScriptBase} from "./MCBase.sol";
1212
// ⭐️ MC SCRIPT
1313
abstract contract MCScript is MCScriptBase {
1414
constructor() {
15-
Config().load();
16-
if (Config().DEBUG_MODE) mc.startDebug();
17-
if (Config().SETUP_STD_FUNCS) mc.setupStdFunctions();
15+
mc.loadConfig();
16+
mc.startDebug();
17+
if (System.Config().SETUP_STD_FUNCS) mc.setupStdFunctions();
1818
}
1919
}
2020

2121
// ⭐️ MC SCRIPT without Setup
2222
abstract contract MCScriptWithoutSetup is MCScriptBase {
2323
constructor() {
24-
Config().load();
25-
if (Config().DEBUG_MODE) mc.startDebug();
24+
mc.loadConfig();
25+
mc.startDebug();
2626
}
2727
}

devkit/MCTest.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.24;
33

4-
import {Config} from "devkit/config/Config.sol";
5-
import {DecodeErrorString} from "./error/DecodeErrorString.sol";
4+
import {System} from "devkit/system/System.sol";
5+
import {DecodeErrorString} from "devkit/system/message/DecodeErrorString.sol";
66

77
// 💬 ABOUT
88
// Meta Contract's default Test based on Forge Std Test
@@ -13,9 +13,9 @@ import {MCTestBase} from "./MCBase.sol";
1313
// ⭐️ MC TEST
1414
abstract contract MCTest is MCTestBase {
1515
constructor() {
16-
Config().load();
17-
if (Config().DEBUG_MODE) mc.startDebug();
18-
if (Config().SETUP_STD_FUNCS) mc.setupStdFunctions();
16+
System.Config().load();
17+
if (System.Config().DEBUG_MODE) mc.startDebug();
18+
if (System.Config().SETUP_STD_FUNCS) mc.setupStdFunctions();
1919
}
2020
}
2121

@@ -47,7 +47,7 @@ abstract contract MCStateFuzzingTest is MCTestBase {
4747
// 🌟 MC TEST for DevKit
4848
abstract contract MCDevKitTest is MCTestBase {
4949
constructor() {
50-
Config().load();
50+
System.Config().load();
5151
mc.stopLog();
5252
}
5353
}

devkit/core/Bundle.sol

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.24;
3+
/**---------------------
4+
Support Methods
5+
-----------------------*/
6+
import {ProcessLib} from "devkit/system/debug/Process.sol";
7+
using ProcessLib for Bundle global;
8+
import {Inspector} from "devkit/types/Inspector.sol";
9+
using Inspector for Bundle global;
10+
import {TypeGuard, TypeStatus} from "devkit/types/TypeGuard.sol";
11+
using TypeGuard for Bundle global;
12+
// Validation
13+
import {Validate} from "devkit/system/validate/Validate.sol";
14+
15+
// Core Type
16+
import {Function} from "devkit/core/Function.sol";
17+
18+
19+
/**================
20+
🗂️ Bundle
21+
==================*/
22+
using BundleLib for Bundle global;
23+
struct Bundle {
24+
string name;
25+
Function[] functions;
26+
address facade;
27+
TypeStatus status;
28+
}
29+
library BundleLib {
30+
31+
/**--------------------
32+
📛 Assign Name
33+
----------------------*/
34+
function assignName(Bundle storage bundle, string memory name) internal returns(Bundle storage) {
35+
uint pid = bundle.startProcess("assignName");
36+
Validate.MUST_BundleNotLocked(bundle);
37+
Validate.MUST_NotEmptyName(name);
38+
bundle.name = name;
39+
return bundle.building().finishProcess(pid);
40+
}
41+
42+
/**-------------------------
43+
🧩 Push Function(s)
44+
---------------------------*/
45+
function pushFunction(Bundle storage bundle, Function storage func) internal returns(Bundle storage) {
46+
uint pid = bundle.startProcess("pushFunction");
47+
Validate.MUST_BundleNotLocked(bundle);
48+
Validate.MUST_completed(func);
49+
Validate.MUST_notHave(bundle, func);
50+
bundle.functions.push(func);
51+
return bundle.building().finishProcess(pid);
52+
}
53+
function pushFunctions(Bundle storage bundle, Function[] storage functions) internal returns(Bundle storage) {
54+
uint pid = bundle.startProcess("pushFunctions");
55+
for (uint i; i < functions.length; ++i) {
56+
bundle.pushFunction(functions[i]);
57+
}
58+
return bundle.building().finishProcess(pid);
59+
}
60+
61+
/**----------------------
62+
🪟 Assign Facade
63+
------------------------*/
64+
function assignFacade(Bundle storage bundle, address facade) internal returns(Bundle storage) {
65+
uint pid = bundle.startProcess("assignFacade");
66+
Validate.MUST_AddressIsContract(facade);
67+
bundle.facade = facade;
68+
return bundle.building().finishProcess(pid);
69+
}
70+
71+
}

devkit/core/Dictionary.sol

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.24;
3+
/**---------------------
4+
Support Methods
5+
-----------------------*/
6+
import {ProcessLib} from "devkit/system/debug/Process.sol";
7+
using ProcessLib for Dictionary global;
8+
import {Params} from "devkit/system/debug/Params.sol";
9+
import {Inspector} from "devkit/types/Inspector.sol";
10+
using Inspector for Dictionary global;
11+
using Inspector for bytes4;
12+
import {ForgeHelper} from "devkit/utils/ForgeHelper.sol";
13+
// Validation
14+
import {Validate} from "devkit/system/validate/Validate.sol";
15+
import {TypeGuard, TypeStatus} from "devkit/types/TypeGuard.sol";
16+
using TypeGuard for Dictionary global;
17+
18+
// Mock
19+
import {DictionaryMock} from "devkit/mocks/DictionaryMock.sol";
20+
// External Libs
21+
import {IDictionary} from "@ucs.mc/dictionary/IDictionary.sol";
22+
import {Dictionary as UCSDictionary} from "@ucs.mc/dictionary/Dictionary.sol";
23+
24+
// Core Types
25+
import {Function} from "devkit/core/Function.sol";
26+
import {Bundle} from "devkit/core/Bundle.sol";
27+
28+
29+
/**====================
30+
📚 Dictionary
31+
======================*/
32+
using DictionaryLib for Dictionary global;
33+
struct Dictionary {
34+
address addr;
35+
DictionaryKind kind;
36+
TypeStatus status;
37+
}
38+
library DictionaryLib {
39+
/**~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40+
🚀 Deploy Dictionary
41+
🔂 Duplicate Dictionary
42+
🧩 Set Function or Bundle
43+
🪟 Upgrade Facade
44+
🤖 Create Dictionary Mock
45+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
46+
47+
/**-------------------------
48+
🚀 Deploy Dictionary
49+
---------------------------*/
50+
function deploy(address owner) internal returns(Dictionary memory) {
51+
uint pid = ProcessLib.startDictionaryLibProcess("deploy");
52+
Validate.MUST_AddressIsNotZero(owner);
53+
/// @dev Until Etherscan supports UCS, we are deploying contracts with additional features for Etherscan compatibility by default.
54+
return Dictionary({
55+
addr: address(new UCSDictionary(owner)),
56+
kind: DictionaryKind.Verifiable,
57+
status: TypeStatus.Building
58+
}).finishProcess(pid);
59+
}
60+
61+
/**----------------------------
62+
🔂 Duplicate Dictionary
63+
------------------------------*/
64+
function duplicate(Dictionary memory toDictionary, Dictionary memory fromDictionary) internal returns(Dictionary memory) {
65+
uint pid = ProcessLib.startDictionaryLibProcess("duplicate");
66+
Validate.MUST_haveContract(toDictionary);
67+
Validate.MUST_haveContract(fromDictionary);
68+
69+
address toAddr = toDictionary.addr;
70+
address fromAddr = fromDictionary.addr;
71+
72+
bytes4[] memory _selectors = IDictionary(fromAddr).supportsInterfaces();
73+
for (uint i; i < _selectors.length; ++i) {
74+
bytes4 _selector = _selectors[i];
75+
if (_selector.isEmpty()) continue;
76+
toDictionary.set(_selector, IDictionary(fromAddr).getImplementation(_selector));
77+
}
78+
79+
return toDictionary.finishProcess(pid);
80+
}
81+
function duplicate(Dictionary memory fromDictionary) internal returns(Dictionary memory) {
82+
return duplicate(deploy(ForgeHelper.msgSender()), fromDictionary);
83+
}
84+
85+
/**-----------------------------
86+
🧩 Set Function or Bundle
87+
-------------------------------*/
88+
function set(Dictionary memory dictionary, bytes4 selector, address implementation) internal returns(Dictionary memory) {
89+
uint pid = ProcessLib.startDictionaryLibProcess("set", Params.append(selector, implementation));
90+
Validate.MUST_haveContract(dictionary);
91+
Validate.MUST_Bytes4NotEmpty(selector);
92+
Validate.MUST_AddressIsContract(implementation);
93+
IDictionary(dictionary.addr).setImplementation({
94+
functionSelector: selector,
95+
implementation: implementation
96+
});
97+
return dictionary.finishProcess(pid);
98+
}
99+
function set(Dictionary memory dictionary, Function memory func) internal returns(Dictionary memory) {
100+
uint pid = ProcessLib.startDictionaryLibProcess("set", Params.append(func.name));
101+
return set(dictionary, func.selector, func.implementation).finishProcess(pid);
102+
}
103+
function set(Dictionary memory dictionary, Bundle storage bundle) internal returns(Dictionary memory) {
104+
uint pid = ProcessLib.startDictionaryLibProcess("set", Params.append(bundle.name));
105+
106+
Function[] memory functions = bundle.functions;
107+
108+
for (uint i; i < functions.length; ++i) {
109+
set(dictionary, functions[i]);
110+
}
111+
112+
// TODO Generate Facade
113+
// if (dictionary.isVerifiable()) {
114+
// dictionary.upgradeFacade(bundle.facade);
115+
// }
116+
117+
return dictionary.finishProcess(pid);
118+
}
119+
120+
/**----------------------
121+
🪟 Upgrade Facade
122+
------------------------*/
123+
function upgradeFacade(Dictionary memory dictionary, address newFacade) internal returns(Dictionary memory) {
124+
uint pid = ProcessLib.startDictionaryLibProcess("upgradeFacade");
125+
Validate.MUST_AddressIsContract(newFacade);
126+
Validate.MUST_Verifiable(dictionary);
127+
IDictionary(dictionary.addr).upgradeFacade(newFacade);
128+
return dictionary.finishProcess(pid);
129+
}
130+
131+
/**------------------------------
132+
🤖 Create Dictionary Mock
133+
--------------------------------*/
134+
function createMock(address owner, Function[] memory functions) internal returns(Dictionary memory) {
135+
uint pid = ProcessLib.startDictionaryLibProcess("createMock");
136+
return Dictionary({
137+
addr: address(new DictionaryMock(owner, functions)),
138+
kind: DictionaryKind.Mock,
139+
status: TypeStatus.Building
140+
}).finishProcess(pid);
141+
}
142+
143+
}
144+
145+
146+
/**--------------------
147+
Dictionary Kind
148+
----------------------*/
149+
enum DictionaryKind {
150+
undefined,
151+
Verifiable,
152+
Mock
153+
}
154+
using Inspector for DictionaryKind global;

0 commit comments

Comments
 (0)