Skip to content

Maintenance #138

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 21 commits into from
Mar 28, 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
151 changes: 84 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,83 +7,100 @@

# Solarity Solidity Library

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

- Implementation of the [**Contracts Registry**](https://eips.ethereum.org/EIPS/eip-6224) pattern
- State-of-the-art cryptography primitives (**ECDSA over 256-bit, 384-bit, and 512-bit curves**, **RSASSA-PSS**)
- Advanced data structures (**Vector**, **DynamicSet**, **PriorityQueue**, **AVLTree**)
- ZK-friendly [**Cartesian Merkle Tree**](https://medium.com/@Arvolear/cartesian-merkle-tree-the-new-breed-a30b005ecf27), [**Sparse Merkle Tree**](https://docs.iden3.io/publications/pdfs/Merkle-Tree.pdf), and [**Incremental Merkle Tree**](https://github.com/runtimeverification/deposit-contract-verification/blob/master/deposit-contract-verification.pdf) implementations
- Versatile access control smart contracts (**Merkle whitelists**, **RBAC**)
- Enhanced and simplified [**Diamond**](https://eips.ethereum.org/EIPS/eip-2535) pattern
- Flexible finance instruments (**Staking**, **Vesting**)
- Robust UniswapV2 and UniswapV3 oracles
- Lightweight SBT implementation
- Hyperoptimized **uint512** BigInt library
- Utilities to ease work with memory, types, ERC20 decimals, arrays, sets, and ZK proofs
Solidity contracts and utilities that **go far beyond mediocre solidity**.

## Contracts

```ml
contracts
├── access
│ ├── AMerkleWhitelisted — "Whitelists via Merkle proofs"
│ ├── AMultiOwnable — "Multiple owners with the equal access level"
│ ├── ARBAC — "A powerful implementation of a true RBAC"
│ └── extensions
│ └── ARBACGroupable — "Groupable extension of ARBAC"
├── contracts—registry
│ ├── AContractsRegistry — "Reference registry implementation of ERC-6224 pattern"
│ ├── ADependant — "Reference dependant implementation of ERC-6224 pattern"
│ └── pools
│ ├── APoolContractsRegistry — "Adaptation of ERC-6224 for factory-like contracts"
│ └── APoolFactory — "Factory implementation for a pooled registry"
├── diamond
│ ├── ADiamondStorage — "The storage part of ERC-2535 diamond"
│ ├── Diamond — "Revised ERC-2535 diamond implementation"
│ └── utils
│ ├── AInitializableStorage — "Initializable logic for diamond facets"
│ └── DiamondERC165 — "ERC-165 introspection for diamond facets"
├── finance
│ ├── compound—rate—keeper
│ │ └── ACompoundRateKeeper — "Complex percentage calculator used in lending protocols"
│ ├── staking
│ │ ├── AStaking — "Flexible rewards staking implementation"
│ │ └── AValueDistributor — "Efficient distribution algorithm implementation"
│ └── vesting
│ └── AVesting — "Linear and exponential vesting implementation"
├── libs
│ ├── arrays
│ │ ├── ArrayHelper — "Common functions to work with arrays"
│ │ ├── Paginator — "Return array slices from view function"
│ │ └── SetHelper — "Array abstraction over sets"
│ ├── bn
│ │ └── U512 — "A hyperoptimized uint512 implementation"
│ ├── crypto
│ │ ├── ECDSA256 — "ECDSA verification over any 256-bit curves"
│ │ ├── ECDSA384 — "ECDSA verification over any 384-bit curves"
│ │ ├── ECDSA512 — "ECDSA verification over any 512-bit curves"
│ │ └── RSASSAPSS — "RSASSA-PSS verification with MGF1"
│ ├── data—structures
│ │ ├── AvlTree — "AVL tree implementation with an iterator traversal"
│ │ ├── CartesianMerkleTree — "CMT reference implementation"
│ │ ├── DynamicSet — "Set for strings and bytes"
│ │ ├── IncrementalMerkleTree — "IMT implementation with flexible tree height"
│ │ ├── PriorityQueue — "Max queue heap implementation"
│ │ ├── SparseMerkleTree — "SMT optimized implementation"
│ │ └── memory
│ │ └── Vector — "A pushable memory array"
│ ├── utils
│ │ ├── DecimalsConverter — "Simplify interaction with ERC-20 decimals"
│ │ ├── MemoryUtils — "Functions for memory manipulation"
│ │ ├── ReturnDataProxy — "Bypass extra returndata copy when returning data"
│ │ └── Typecaster — "Cast between various Solidity types"
│ └── zkp
│ ├── Groth16VerifierHelper — "Simplify integration with Groth16 proofs"
│ └── PlonkVerifierHelper — "Simplify integration with Plonk proofs"
├── oracles
│ ├── AUniswapV2Oracle — "Uniswap V2 oracle with custom TWAP"
│ └── UniswapV3Oracle — "Uniswap V3 oracle with a clean interface"
├── proxy
│ └── adminable
│ ├── AdminableProxy — "A slight modification of a transparent proxy"
│ └── AdminableProxyUpgrader — "A slight modification of a proxy admin"
├── tokens
│ └── ASBT — "A minimal implementation of an SBT"
├── utils
│ ├── ABlockGuard — "Protect against flashloans"
│ └── Globals — "Some commonly used constants"
├── presets — "Presets for the library contracts"
├── interfaces — "Interfaces for the library contracts"
└── mock — "Mocks for testing purposes"
```

Built with courage and aspiration to perfection.

## Overview
> [!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.

### Installation
## Installation

```console
$ npm install @solarity/solidity-lib
```bash
npm install @solarity/solidity-lib
```

The latest stable version is always in the `master` branch.

### Documentation

Check out the project's [documentation](https://docs.solarity.dev) with broad explanations and usage examples of every module. Full `natspec` guides are also available in the source code.

## Usage

You will find the smart contracts in the `/contracts` directory. Feel free to play around and check the project's structure.

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.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;
## Documentation

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(proof_, pubSignals_), "ZKP verification failed");
}
}
```

This example showcases the basic usage of a `CartesianMerkleTree` with ZK proofs. The contract's `MultiOwner` may add elements to the tree to then privately prove their existence. Also, the `Groth16VerifierHelper` library is used to simplify the interaction with the ZK verifier.

> [!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.
Check out the project's [documentation](https://docs.solarity.dev) with broad explanations and usage examples of every contract. Full `natspec` guides are also available in the source code.

## Contribution

Expand Down
88 changes: 64 additions & 24 deletions contracts/contracts-registry/AContractsRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@ import {ADependant} from "./ADependant.sol";
* Users may also fetch all the contracts present in the system as they are now located in a single place.
*/
abstract contract AContractsRegistry is Initializable {
AdminableProxyUpgrader private _proxyUpgrader;
struct AContractsRegistryStorage {
AdminableProxyUpgrader proxyUpgrader;
mapping(string name => address contractAddress) contracts;
mapping(address contractAddress => bool isProxy) isProxy;
}

mapping(string => address) private _contracts;
mapping(address => bool) private _isProxy;
// bytes32(uint256(keccak256("solarity.contract.AContractsRegistry")) - 1)
bytes32 private constant A_CONTRACTS_REGISTRY_STORAGE =
0x769f3b456cd81d706504548e533f55ce8f4cb7a5f9b80697cfd5d8146de0ca61;

event ContractAdded(string name, address contractAddress);
event ProxyContractAdded(string name, address contractAddress, address implementation);
Expand All @@ -56,7 +61,9 @@ abstract contract AContractsRegistry is Initializable {
* @notice The initialization function
*/
function __AContractsRegistry_init() internal onlyInitializing {
_proxyUpgrader = new AdminableProxyUpgrader(address(this));
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

$.proxyUpgrader = new AdminableProxyUpgrader(address(this));
}

/**
Expand All @@ -65,7 +72,9 @@ abstract contract AContractsRegistry is Initializable {
* @return the address of the contract
*/
function getContract(string memory name_) public view returns (address) {
address contractAddress_ = _contracts[name_];
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

address contractAddress_ = $.contracts[name_];

_checkIfMappingExist(contractAddress_, name_);

Expand All @@ -78,15 +87,19 @@ abstract contract AContractsRegistry is Initializable {
* @return true if the contract is present in the registry
*/
function hasContract(string memory name_) public view returns (bool) {
return _contracts[name_] != address(0);
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

return $.contracts[name_] != address(0);
}

/**
* @notice The function that returns the admin of the added proxy contracts
* @return the proxy admin address
*/
function getProxyUpgrader() public view returns (address) {
return address(_proxyUpgrader);
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

return address($.proxyUpgrader);
}

/**
Expand All @@ -95,12 +108,14 @@ abstract contract AContractsRegistry is Initializable {
* @return the implementation address
*/
function getImplementation(string memory name_) public view returns (address) {
address contractProxy_ = _contracts[name_];
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

address contractProxy_ = $.contracts[name_];

if (contractProxy_ == address(0)) revert NoMappingExists(name_);
if (!_isProxy[contractProxy_]) revert NotAProxy(name_, contractProxy_);
if (!$.isProxy[contractProxy_]) revert NotAProxy(name_, contractProxy_);

return _proxyUpgrader.getImplementation(contractProxy_);
return $.proxyUpgrader.getImplementation(contractProxy_);
}

/**
Expand All @@ -120,7 +135,9 @@ abstract contract AContractsRegistry is Initializable {
string memory name_,
bytes memory data_
) internal virtual {
address contractAddress_ = _contracts[name_];
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

address contractAddress_ = $.contracts[name_];

_checkIfMappingExist(contractAddress_, name_);

Expand Down Expand Up @@ -152,12 +169,14 @@ abstract contract AContractsRegistry is Initializable {
address newImplementation_,
bytes memory data_
) internal virtual {
address contractToUpgrade_ = _contracts[name_];
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

address contractToUpgrade_ = $.contracts[name_];

if (contractToUpgrade_ == address(0)) revert NoMappingExists(name_);
if (!_isProxy[contractToUpgrade_]) revert NotAProxy(name_, contractToUpgrade_);
if (!$.isProxy[contractToUpgrade_]) revert NotAProxy(name_, contractToUpgrade_);

_proxyUpgrader.upgrade(contractToUpgrade_, newImplementation_, data_);
$.proxyUpgrader.upgrade(contractToUpgrade_, newImplementation_, data_);

emit ProxyContractUpgraded(name_, newImplementation_);
}
Expand All @@ -171,7 +190,9 @@ abstract contract AContractsRegistry is Initializable {
function _addContract(string memory name_, address contractAddress_) internal virtual {
if (contractAddress_ == address(0)) revert ZeroAddressProvided(name_);

_contracts[name_] = contractAddress_;
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

$.contracts[name_] = contractAddress_;

emit ContractAdded(name_, contractAddress_);
}
Expand Down Expand Up @@ -200,10 +221,12 @@ abstract contract AContractsRegistry is Initializable {
) internal virtual {
if (contractAddress_ == address(0)) revert ZeroAddressProvided(name_);

address proxyAddr_ = _deployProxy(contractAddress_, address(_proxyUpgrader), data_);
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

address proxyAddr_ = _deployProxy(contractAddress_, address($.proxyUpgrader), data_);

_contracts[name_] = proxyAddr_;
_isProxy[proxyAddr_] = true;
$.contracts[name_] = proxyAddr_;
$.isProxy[proxyAddr_] = true;

emit ProxyContractAdded(name_, proxyAddr_, contractAddress_);
}
Expand All @@ -221,13 +244,15 @@ abstract contract AContractsRegistry is Initializable {
) internal virtual {
if (contractAddress_ == address(0)) revert ZeroAddressProvided(name_);

_contracts[name_] = contractAddress_;
_isProxy[contractAddress_] = true;
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

$.contracts[name_] = contractAddress_;
$.isProxy[contractAddress_] = true;

emit ProxyContractAdded(
name_,
contractAddress_,
_proxyUpgrader.getImplementation(contractAddress_)
$.proxyUpgrader.getImplementation(contractAddress_)
);
}

Expand All @@ -236,12 +261,14 @@ abstract contract AContractsRegistry is Initializable {
* @param name_ the associated name with the contract
*/
function _removeContract(string memory name_) internal virtual {
address contractAddress_ = _contracts[name_];
AContractsRegistryStorage storage $ = _getAContractsRegistryStorage();

address contractAddress_ = $.contracts[name_];

_checkIfMappingExist(contractAddress_, name_);

delete _isProxy[contractAddress_];
delete _contracts[name_];
delete $.isProxy[contractAddress_];
delete $.contracts[name_];

emit ContractRemoved(name_);
}
Expand All @@ -264,4 +291,17 @@ abstract contract AContractsRegistry is Initializable {
function _checkIfMappingExist(address contractAddress_, string memory name_) internal pure {
if (contractAddress_ == address(0)) revert NoMappingExists(name_);
}

/**
* @dev Returns a pointer to the storage namespace
*/
function _getAContractsRegistryStorage()
private
pure
returns (AContractsRegistryStorage storage $)
{
assembly {
$.slot := A_CONTRACTS_REGISTRY_STORAGE
}
}
}
Loading