Skip to content

Solarity 3.0 #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "warn",
"reentrancy": "off",
"modifier-name-mixedcase": "off",
"reentrancy": "warn",
"modifier-name-mixedcase": "warn",
"no-empty-blocks": "off",
"func-visibility": "off",
"max-states-count": "off",
"func-visibility": ["warn", { "ignoreConstructors": true }],
"max-states-count": "warn",
"not-rely-on-time": "off",
"compiler-version": "off"
"func-name-mixedcase": "off",
"no-inline-assembly": "off",
"compiler-version": ["off"]
}
}
47 changes: 37 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![GitPOAP Badge](https://public-api.gitpoap.io/v1/repo/dl-solarity/solidity-lib/badge)](https://www.gitpoap.io/gh/dl-solarity/solidity-lib)

# Solidity Library for Savvies
# Solarity Solidity Library

Solidity modules and utilities that **go far beyond mediocre solidity**.

Expand All @@ -21,7 +21,7 @@ Solidity modules and utilities that **go far beyond mediocre solidity**.
- Hyperoptimized **uint512** BigInt library
- Utilities to ease work with memory, types, ERC20 decimals, arrays, sets, and ZK proofs

Built leveraging [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) (4.9.6).
Built with courage and aspiration to perfection.

## Overview

Expand All @@ -44,17 +44,44 @@ You will find the smart contracts in the `/contracts` directory. Feel free to pl
Once the [npm package](https://www.npmjs.com/package/@solarity/solidity-lib) is installed, one can use the library just like that:

```solidity
pragma solidity ^0.8.4;

import {OwnableContractsRegistry} from "@solarity/solidity-lib/contracts-registry/presets/OwnableContractsRegistry.sol";

contract ContractsRegistry is OwnableContractsRegistry {
. . .
pragma solidity ^0.8.21;

import {AMultiOwnable} from "@solarity/solidity-lib/access/AMultiOwnable.sol";
import {TypeCaster} from "@solarity/solidity-lib/libs/utils/TypeCaster.sol";
import {CartesianMerkleTree} from "@solarity/solidity-lib/libs/data-structures/CartesianMerkleTree.sol";
import {Groth16VerifierHelper} from "@solarity/solidity-lib/libs/zkp/Groth16VerifierHelper.sol";

contract Example is AMultiOwnable {
using CartesianMerkleTree for CartesianMerkleTree.UintCMT;
using Groth16VerifierHelper for address;

CartesianMerkleTree.UintCMT internal _uintTreaple;
address internal _treapleVerifier;

function __Example_init(address treapleVerifier_) initializer {
__AMultiOwnable_init();
_uintTreaple.initialize(40);
_treapleVerifier = treapleVerifier_;
}

function addToTree(uint256 key_) external onlyOwner {
_uintTreaple.add(key_);
}

function getMerkleProof(uint256 key_) external view returns (CartesianMerkleTree.Proof memory) {
return _uintTreaple.getProof(key_, 0);
}

function verifyZKProof(Groth16VerifierHelper.ProofPoints memory proof_) external view {
uint256[] memory pubSignals_ = TypeCaster.asSingletonArray(_uintTreaple.getRoot());

require(_treapleVerifier.verifyProof(pubSignals_, proof_), "ZKP verification failed");
}
}
```

> [!IMPORTANT]
> It is important to use the library as it is shipped and not copy-paste the code from untrusted sources.
> [!TIP]
> The library is designed to work cohesively with [hardhat-zkit](https://github.com/dl-solarity/hardhat-zkit) and [circom-lib](https://github.com/dl-solarity/circom-lib) packages.

## Contribution

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
pragma solidity ^0.8.21;

import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

Expand All @@ -17,21 +17,21 @@ import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProo
*
* Note: the branch nodes are sorted numerically.
*/
abstract contract MerkleWhitelisted {
abstract contract AMerkleWhitelisted {
using MerkleProof for bytes32[];

bytes32 private _merkleRoot;

error LeafNotWhitelisted(bytes data);
error UserNotWhitelisted(address user);

modifier onlyWhitelisted(bytes memory data_, bytes32[] memory merkleProof_) {
require(
_isWhitelisted(keccak256(data_), merkleProof_),
"MerkleWhitelisted: not whitelisted"
);
if (!_isWhitelisted(keccak256(data_), merkleProof_)) revert LeafNotWhitelisted(data_);
_;
}

modifier onlyWhitelistedUser(address user_, bytes32[] memory merkleProof_) {
require(_isWhitelistedUser(user_, merkleProof_), "MerkleWhitelisted: not whitelisted");
if (!_isWhitelistedUser(user_, merkleProof_)) revert UserNotWhitelisted(user_);
_;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
pragma solidity ^0.8.21;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
Expand All @@ -21,13 +21,16 @@ import {IMultiOwnable} from "../interfaces/access/IMultiOwnable.sol";
* This module will make available the modifier `onlyOwner`, which can be applied
* to your functions to restrict their use to the owners.
*/
abstract contract MultiOwnable is IMultiOwnable, Initializable {
abstract contract AMultiOwnable is IMultiOwnable, Initializable {
using EnumerableSet for EnumerableSet.AddressSet;
using TypeCaster for address;
using SetHelper for EnumerableSet.AddressSet;

EnumerableSet.AddressSet private _owners;

error InvalidOwner();
error UnauthorizedAccount(address account);

modifier onlyOwner() {
_checkOwner();
_;
Expand All @@ -36,7 +39,7 @@ abstract contract MultiOwnable is IMultiOwnable, Initializable {
/**
* @dev Initializes the contract setting the msg.sender as the initial owner.
*/
function __MultiOwnable_init() internal onlyInitializing {
function __AMultiOwnable_init() internal onlyInitializing {
_addOwners(msg.sender.asSingletonArray());
}

Expand Down Expand Up @@ -91,7 +94,7 @@ abstract contract MultiOwnable is IMultiOwnable, Initializable {
function _addOwners(address[] memory newOwners_) private {
_owners.add(newOwners_);

require(!_owners.contains(address(0)), "MultiOwnable: zero address can not be added");
if (_owners.contains(address(0))) revert InvalidOwner();

emit OwnersAdded(newOwners_);
}
Expand All @@ -115,6 +118,6 @@ abstract contract MultiOwnable is IMultiOwnable, Initializable {
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() private view {
require(isOwner(msg.sender), "MultiOwnable: caller is not the owner");
if (!isOwner(msg.sender)) revert UnauthorizedAccount(msg.sender);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
pragma solidity ^0.8.21;

/**
* @notice The PermanentOwnable module
Expand All @@ -12,9 +12,12 @@ pragma solidity ^0.8.4;
* This module will make available the modifier `onlyOwner`, which can be applied
* to your functions to restrict their use to the owners.
*/
abstract contract PermanentOwnable {
abstract contract APermanentOwnable {
address private immutable _OWNER;

error InvalidOwner();
error UnauthorizedAccount(address account);

/**
* @dev Throws if called by any account other than the owner.
*/
Expand All @@ -28,7 +31,7 @@ abstract contract PermanentOwnable {
* @param owner_ the address of the permanent owner.
*/
constructor(address owner_) {
require(owner_ != address(0), "PermanentOwnable: zero address can not be the owner");
if (owner_ == address(0)) revert InvalidOwner();

_OWNER = owner_;
}
Expand All @@ -42,6 +45,6 @@ abstract contract PermanentOwnable {
}

function _onlyOwner() internal view virtual {
require(_OWNER == msg.sender, "PermanentOwnable: caller is not the owner");
if (_OWNER != msg.sender) revert UnauthorizedAccount(msg.sender);
}
}
21 changes: 10 additions & 11 deletions contracts/access/RBAC.sol → contracts/access/ARBAC.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
pragma solidity ^0.8.21;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

Expand Down Expand Up @@ -30,7 +30,7 @@ import {DynamicSet} from "../libs/data-structures/DynamicSet.sol";
*
* Where ROLE is assignable to users
*/
abstract contract RBAC is IRBAC, Initializable {
abstract contract ARBAC is IRBAC, Initializable {
using DynamicSet for DynamicSet.StringSet;
using SetHelper for DynamicSet.StringSet;
using TypeCaster for string;
Expand All @@ -53,20 +53,19 @@ abstract contract RBAC is IRBAC, Initializable {

mapping(address => DynamicSet.StringSet) private _userRoles;

error EmptyRoles();
error NoPermissionForResource(address account, string permission, string resource);

modifier onlyPermission(string memory resource_, string memory permission_) {
require(
hasPermission(msg.sender, resource_, permission_),
string(
abi.encodePacked("RBAC: no ", permission_, " permission for resource ", resource_)
)
);
if (!hasPermission(msg.sender, resource_, permission_))
revert NoPermissionForResource(msg.sender, permission_, resource_);
_;
}

/**
* @notice The initialization function
*/
function __RBAC_init() internal onlyInitializing {
function __ARBAC_init() internal onlyInitializing {
_addPermissionsToRole(MASTER_ROLE, ALL_RESOURCE, ALL_PERMISSION.asSingletonArray(), true);
}

Expand All @@ -79,7 +78,7 @@ abstract contract RBAC is IRBAC, Initializable {
address to_,
string[] memory rolesToGrant_
) public virtual override onlyPermission(RBAC_RESOURCE, CREATE_PERMISSION) {
require(rolesToGrant_.length > 0, "RBAC: empty roles");
if (rolesToGrant_.length == 0) revert EmptyRoles();

_grantRoles(to_, rolesToGrant_);
}
Expand All @@ -93,7 +92,7 @@ abstract contract RBAC is IRBAC, Initializable {
address from_,
string[] memory rolesToRevoke_
) public virtual override onlyPermission(RBAC_RESOURCE, DELETE_PERMISSION) {
require(rolesToRevoke_.length > 0, "RBAC: empty roles");
if (rolesToRevoke_.length == 0) revert EmptyRoles();

_revokeRoles(from_, rolesToRevoke_);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
pragma solidity ^0.8.21;

import {IRBACGroupable} from "../../interfaces/access/extensions/IRBACGroupable.sol";

import {DynamicSet} from "../../libs/data-structures/DynamicSet.sol";
import {SetHelper} from "../../libs/arrays/SetHelper.sol";

import {RBAC} from "../RBAC.sol";
import {ARBAC} from "../ARBAC.sol";

/**
* @notice The Role Based Access Control (RBAC) module
Expand All @@ -22,7 +22,7 @@ import {RBAC} from "../RBAC.sol";
*
* Where ROLE and GROUP are assignable to users
*/
abstract contract RBACGroupable is IRBACGroupable, RBAC {
abstract contract ARBACGroupable is IRBACGroupable, ARBAC {
using DynamicSet for DynamicSet.StringSet;
using SetHelper for DynamicSet.StringSet;

Expand All @@ -31,11 +31,13 @@ abstract contract RBACGroupable is IRBACGroupable, RBAC {
mapping(address => DynamicSet.StringSet) private _userGroups;
mapping(string => DynamicSet.StringSet) private _groupRoles;

error EmptyGroups();

/**
* @notice The initialization function
*/
function __RBACGroupable_init() internal onlyInitializing {
__RBAC_init();
function __ARBACGroupable_init() internal onlyInitializing {
__ARBAC_init();
}

/**
Expand All @@ -47,7 +49,7 @@ abstract contract RBACGroupable is IRBACGroupable, RBAC {
address who_,
string[] memory groupsToAddTo_
) public virtual override onlyPermission(RBAC_RESOURCE, CREATE_PERMISSION) {
require(groupsToAddTo_.length > 0, "RBACGroupable: empty groups");
if (groupsToAddTo_.length == 0) revert EmptyGroups();

_addUserToGroups(who_, groupsToAddTo_);
}
Expand All @@ -61,7 +63,7 @@ abstract contract RBACGroupable is IRBACGroupable, RBAC {
address who_,
string[] memory groupsToRemoveFrom_
) public virtual override onlyPermission(RBAC_RESOURCE, DELETE_PERMISSION) {
require(groupsToRemoveFrom_.length > 0, "RBACGroupable: empty groups");
if (groupsToRemoveFrom_.length == 0) revert EmptyGroups();

_removeUserFromGroups(who_, groupsToRemoveFrom_);
}
Expand All @@ -75,7 +77,7 @@ abstract contract RBACGroupable is IRBACGroupable, RBAC {
string memory groupTo_,
string[] memory rolesToGrant_
) public virtual override onlyPermission(RBAC_RESOURCE, CREATE_PERMISSION) {
require(rolesToGrant_.length > 0, "RBACGroupable: empty roles");
if (rolesToGrant_.length == 0) revert EmptyRoles();

_grantGroupRoles(groupTo_, rolesToGrant_);
}
Expand All @@ -89,7 +91,7 @@ abstract contract RBACGroupable is IRBACGroupable, RBAC {
string memory groupFrom_,
string[] memory rolesToRevoke_
) public virtual override onlyPermission(RBAC_RESOURCE, DELETE_PERMISSION) {
require(rolesToRevoke_.length > 0, "RBACGroupable: empty roles");
if (rolesToRevoke_.length == 0) revert EmptyRoles();

_revokeGroupRoles(groupFrom_, rolesToRevoke_);
}
Expand Down
Loading