Skip to content
Open
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
70 changes: 32 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,60 @@
## Foundry
# Upgradable Contracts - Diamond Standard

**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
## Foundry + Hardhat Diamonds

Foundry consists of:
This is a mimimal template for [Diamonds](https://github.com/ethereum/EIPs/issues/2535) which allows facet selectors to be generated on the go in solidity tests!

- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
## Installation

## Documentation
- Clone this repo
- Install dependencies

https://book.getfoundry.sh/
```bash
$ yarn && forge update
```

## Usage
### Compile

### Build
### Foundry

```shell
```bash
$ forge build
```

### Test
### If you encounter Source "forge-std/Test.sol" not found: File not found.

```shell
$ forge test
```bash
forge install foundry-rs/forge-std
```

### Format
Then run

```shell
$ forge fmt
```bash
$ forge build
```

### Gas Snapshots
### Hardhat

```shell
$ forge snapshot
```bash
$ npx hardhat compile
```

### Anvil

```shell
$ anvil
```
## Deployment

### Deploy
### Hardhat

```shell
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
```bash
$ npx hardhat run scripts/deploy.js
```

### Cast
### Foundry

```shell
$ cast <subcommand>
```bash
$ forge t
```

### Help
`Note`: A lot of improvements are still needed so contributions are welcome!!

```shell
$ forge --help
$ anvil --help
$ cast --help
```
Bonus: The [DiamondLoupefacet](contracts/facets/DiamondLoupeFacet.sol) uses an updated [LibDiamond](contracts/libraries//LibDiamond.sol) which utilises solidity custom errors to make debugging easier especially when upgrading diamonds. Take it for a spin!!

Need some more clarity? message me [on twitter](https://twitter.com/Timidan_x), Or join the [EIP-2535 Diamonds Discord server](https://discord.gg/kQewPw2)
86 changes: 86 additions & 0 deletions contracts/upgradable-contracts/Diamond.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
*
* Implementation of a diamond.
/******************************************************************************/

import {LibDiamond} from "./libraries/LibDiamond.sol";
import {IDiamondCut} from "./interfaces/IDiamondCut.sol"; //interface modifying the facet in the diamond

contract Diamond {
// crontract owner and address containing the facet implementation
constructor(address _contractOwner, address _diamondCutFacet) payable {
LibDiamond.setContractOwner(_contractOwner);

// Add the diamondCut external function from the diamondCutFacet
// Creates an array cut of type IDiamondCut.FacetCut with one element.
IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
// Creates an array functionSelectors of type bytes4 (used to store function selectors).
bytes4[] memory functionSelectors = new bytes4[](1);

// Adding the DiamondCut Function
// Stores the function selector of diamondCut in functionSelectors[0].
functionSelectors[0] = IDiamondCut.diamondCut.selector;
// Populates cut[0]
cut[0] = IDiamondCut.FacetCut({
facetAddress: _diamondCutFacet,
action: IDiamondCut.FacetCutAction.Add,
functionSelectors: functionSelectors
});

// Executing the Diamond Cut
LibDiamond.diamondCut(cut, address(0), "");
}

// Find facet for function that is called and execute the
// function if a facet is found and return any value.
fallback() external payable {
// Retrieves Diamond storage.
// Uses assembly to get storage at DIAMOND_STORAGE_POSITION (defined in LibDiamond).

LibDiamond.DiamondStorage storage ds;
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
// get diamond storage
assembly {
ds.slot := position
}
// get facet from function selector
// Looks up the facet contract address for the called function (msg.sig).
// If the function does not exist, it reverts with "Diamond: Function does not exist".
address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
require(facet != address(0), "Diamond: Function does not exist");

// Execute external function from facet using delegatecall and return any value.
// Uses delegatecall to call the function in the facet contract.
// If it fails, it reverts.
// If it succeeds, it returns the function’s result.

assembly {
// copy function selector and any arguments
calldatacopy(0, 0, calldatasize())
// execute function call using the facet
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
// get any return value
returndatacopy(0, 0, returndatasize())
// return any return value or error back to the caller
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}

//immutable function example
function example() public pure returns (string memory) {
return "THIS IS AN EXAMPLE OF AN IMMUTABLE FUNCTION";
}

receive() external payable {}
}
27 changes: 27 additions & 0 deletions contracts/upgradable-contracts/facets/DiamondCutFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

import { IDiamondCut } from "../interfaces/IDiamondCut.sol";
import { LibDiamond } from "../libraries/LibDiamond.sol";

contract DiamondCutFacet is IDiamondCut {
/// @notice Add/replace/remove any number of functions and optionally execute
/// a function with delegatecall
/// @param _diamondCut Contains the facet addresses and function selectors
/// @param _init The address of the contract or facet to execute _calldata
/// @param _calldata A function call, including function selector and arguments
/// _calldata is executed with delegatecall on _init
function diamondCut(
FacetCut[] calldata _diamondCut,
address _init,
bytes calldata _calldata
) external override {
LibDiamond.enforceIsContractOwner();
LibDiamond.diamondCut(_diamondCut, _init, _calldata);
}
}
64 changes: 64 additions & 0 deletions contracts/upgradable-contracts/facets/DiamondLoupeFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

import { LibDiamond } from "../libraries/LibDiamond.sol";
import { IDiamondLoupe } from "../interfaces/IDiamondLoupe.sol";
import { IERC165 } from "../interfaces/IERC165.sol";

contract DiamondLoupeFacet is IDiamondLoupe, IERC165 {
// Diamond Loupe Functions
////////////////////////////////////////////////////////////////////
/// These functions are expected to be called frequently by tools.
//
// struct Facet {
// address facetAddress;
// bytes4[] functionSelectors;
// }

/// @notice Gets all facets and their selectors.
/// @return facets_ Facet
function facets() external override view returns (Facet[] memory facets_) {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
uint256 numFacets = ds.facetAddresses.length;
facets_ = new Facet[](numFacets);
for (uint256 i; i < numFacets; i++) {
address facetAddress_ = ds.facetAddresses[i];
facets_[i].facetAddress = facetAddress_;
facets_[i].functionSelectors = ds.facetFunctionSelectors[facetAddress_].functionSelectors;
}
}

/// @notice Gets all the function selectors provided by a facet.
/// @param _facet The facet address.
/// @return facetFunctionSelectors_
function facetFunctionSelectors(address _facet) external override view returns (bytes4[] memory facetFunctionSelectors_) {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
facetFunctionSelectors_ = ds.facetFunctionSelectors[_facet].functionSelectors;
}

/// @notice Get all the facet addresses used by a diamond.
/// @return facetAddresses_
function facetAddresses() external override view returns (address[] memory facetAddresses_) {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
facetAddresses_ = ds.facetAddresses;
}

/// @notice Gets the facet that supports the given selector.
/// @dev If facet is not found return address(0).
/// @param _functionSelector The function selector.
/// @return facetAddress_ The facet address.
function facetAddress(bytes4 _functionSelector) external override view returns (address facetAddress_) {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
facetAddress_ = ds.selectorToFacetAndPosition[_functionSelector].facetAddress;
}

// This implements ERC-165.
function supportsInterface(bytes4 _interfaceId) external override view returns (bool) {
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
return ds.supportedInterfaces[_interfaceId];
}
}
16 changes: 16 additions & 0 deletions contracts/upgradable-contracts/facets/OwnershipFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { LibDiamond } from "../libraries/LibDiamond.sol";
import { IERC173 } from "../interfaces/IERC173.sol";

contract OwnershipFacet is IERC173 {
function transferOwnership(address _newOwner) external override {
LibDiamond.enforceIsContractOwner();
LibDiamond.setContractOwner(_newOwner);
}

function owner() external override view returns (address owner_) {
owner_ = LibDiamond.contractOwner();
}
}
25 changes: 25 additions & 0 deletions contracts/upgradable-contracts/facets/SetterGetter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {LibAppStorage} from "../libraries/LibAppStorage.sol";

contract SetterGetter {
LibAppStorage.Layout layout;

function setName(string memory _name) external {
layout.name = _name;
}

function setAge(uint8 _age) external {
layout.age = _age;
}

function getName() external view returns (string memory name_) {
name_ = layout.name;
return name_;
}

function getAge() external view returns (uint8 age_) {
age_ = layout.age;
return age_;
}
}
32 changes: 32 additions & 0 deletions contracts/upgradable-contracts/interfaces/IDiamondCut.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

interface IDiamondCut {
enum FacetCutAction {Add, Replace, Remove}
// Add=0, Replace=1, Remove=2

struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}

/// @notice Add/replace/remove any number of functions and optionally execute
/// a function with delegatecall
/// @param _diamondCut Contains the facet addresses and function selectors
/// @param _init The address of the contract or facet to execute _calldata
/// @param _calldata A function call, including function selector and arguments
/// _calldata is executed with delegatecall on _init
function diamondCut(
FacetCut[] calldata _diamondCut,
address _init,
bytes calldata _calldata
) external;

event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}
38 changes: 38 additions & 0 deletions contracts/upgradable-contracts/interfaces/IDiamondLoupe.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

// A loupe is a small magnifying glass used to look at diamonds.
// These functions look at diamonds
interface IDiamondLoupe {
/// These functions are expected to be called frequently
/// by tools.

struct Facet {
address facetAddress;
bytes4[] functionSelectors;
}

/// @notice Gets all facet addresses and their four byte function selectors.
/// @return facets_ Facet
function facets() external view returns (Facet[] memory facets_);

/// @notice Gets all the function selectors supported by a specific facet.
/// @param _facet The facet address.
/// @return facetFunctionSelectors_
function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);

/// @notice Get all the facet addresses used by a diamond.
/// @return facetAddresses_
function facetAddresses() external view returns (address[] memory facetAddresses_);

/// @notice Gets the facet that supports the given selector.
/// @dev If facet is not found return address(0).
/// @param _functionSelector The function selector.
/// @return facetAddress_ The facet address.
function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
}
Loading
Loading