Code Monkey home page Code Monkey logo

diamond-1-hardhat's Introduction

Diamond-1-Hardhat Implementation

This is a reference implementation for EIP-2535 Diamonds. To learn about other implementations go here: https://github.com/mudgen/diamond

Note: The loupe functions in DiamondLoupeFacet.sol MUST be added to a diamond and are required by the EIP-2535 Diamonds standard.

Note: In this implementation the loupe functions are NOT gas optimized. The facets, facetFunctionSelectors, facetAddresses loupe functions are not meant to be called on-chain and may use too much gas or run out of gas when called in on-chain transactions. In this implementation these functions should be called by off-chain software like websites and Javascript libraries etc., where gas costs do not matter.

Installation

  1. Clone this repo:
git clone [email protected]:mudgen/diamond-1-hardhat.git
  1. Install NPM packages:
cd diamond-1-hardhat
npm install

Deployment

npx hardhat run scripts/deploy.js

How the scripts/deploy.js script works

The scripts/deploy.js deployment script includes comments to explain how it works.

How a diamond is deployed is not part of the EIP-2535 Diamonds standard. This implementation shows an example.

Run tests:

npx hardhat test

Upgrade a diamond

Check the test/diamondTest.js file for examples of upgrades.

Note that upgrade functionality is optional. It is possible to deploy a diamond that can't be upgraded, which is a 'Single Cut Diamond'. It is also possible to deploy an upgradeable diamond and at a later date remove its diamondCut function so it can't be upgraded any more.

Note that any number of functions from any number of facets can be added/replaced/removed on a diamond in a single transaction. In addition an initialization function can be executed in the same transaction as an upgrade to initialize any state variables required for an upgrade. This 'everything done in a single transaction' capability ensures a diamond maintains a correct and consistent state during upgrades.

Facet Information

Note: In this implementation the loupe functions are NOT gas optimized. The facets, facetFunctionSelectors, facetAddresses loupe functions are not meant to be called on-chain and may use too much gas or run out of gas when called in on-chain transactions. In this implementation these functions should be called by off-chain software like websites and Javascript libraries etc., where gas costs do not matter as much.

However the facetAddress loupe function is gas efficient and can be called in on-chain transactions.

The contracts/Diamond.sol file shows an example of implementing a diamond.

The contracts/facets/DiamondCutFacet.sol file shows how to implement the diamondCut external function.

The contracts/facets/DiamondLoupeFacet.sol file shows how to implement the four standard loupe functions.

The contracts/libraries/LibDiamond.sol file shows how to implement Diamond Storage and a diamondCut internal function.

The scripts/deploy.js file shows how to deploy a diamond.

The test/diamondTest.js file gives tests for the diamondCut function and the Diamond Loupe functions.

How to Get Started Making Your Diamond

  1. Reading and understand EIP-2535 Diamonds. If something is unclear let me know!

  2. Use a diamond reference implementation. You are at the right place because this is the README for a diamond reference implementation.

This diamond implementation is boilerplate code that makes a diamond compliant with EIP-2535 Diamonds.

Specifically you can copy and use the DiamondCutFacet.sol and DiamondLoupeFacet.sol contracts. They implement the diamondCut function and the loupe functions.

The Diamond.sol contract could be used as is, or it could be used as a starting point and customized. This contract is the diamond. Its deployment creates a diamond. It's address is a stable diamond address that does not change.

The LibDiamond.sol library could be used as is. It shows how to implement Diamond Storage. This contract includes contract ownership which you might want to change if you want to implement DAO-based ownership or other form of contract ownership. Go for it. Diamonds can work with any kind of contract ownership strategy. This library contains an internal function version of diamondCut that can be used in the constructor of a diamond or other places.

Calling Diamond Functions

In order to call a function that exists in a diamond you need to use the ABI information of the facet that has the function.

Here is an example that uses web3.js:

const myUsefulFacet = new web3.eth.Contract(MyUsefulFacet.abi, diamondAddress);

In the code above we create a contract variable so we can call contract functions with it.

In this example we know we will use a diamond because we pass a diamond's address as the second argument. But we are using an ABI from the MyUsefulFacet facet so we can call functions that are defined in that facet. MyUsefulFacet's functions must have been added to the diamond (using diamondCut) in order for the diamond to use the function information provided by the ABI of course.

Here is another example that uses hardhat:

const diamondLoupeFacet = await ethers.getContractAt('DiamondLoupeFacet', diamondAddress)

Similarly you need to use the ABI of a facet in Solidity code in order to call functions from a diamond. Here's an example of Solidity code that calls a function from a diamond:

string result = MyUsefulFacet(address(diamondContract)).getResult()

Get Help and Join the Community

If you need help or would like to discuss diamonds then send me a message on twitter, or email me. Or join the EIP-2535 Diamonds Discord server.

Useful Links

  1. EIP-2535 Diamonds
  2. Introduction to the Diamond Standard, EIP-2535 Diamonds
  3. EIP2535 Diamonds Documentation
  4. Awesome Diamonds

Author

This example implementation was written by Nick Mudge.

Contact:

License

MIT license. See the license file. Anyone can use or modify this software for their purposes.

diamond-1-hardhat's People

Contributors

mudgen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

diamond-1-hardhat's Issues

Optimization fallback() function

In Diamond.sol fallback function

 LibDiamond.DiamondStorage storage ds;
        bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
        // get diamond storage
        assembly {
            ds.slot := position
        }

why not use LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();?

IDiamond.sol defined but never used

I noticed that IDiamond.sol is included in the interfaces folder but the Diamond contract does not inherit from it. Is that an oversight or a conscious decision to have it like that?

Comments are flagged as invalid doc tags

Importing some of these contracts results in an invalid doc tag solidity compiler error when using this repo as a submodule, importing them, and compiling the contracts with foundry.
image

DiamondLoupeFacet required, yet not added in constructor?

Hi @mudgen, this is mostly just a question for you, and may not be a big issue.

I noticed the following language in this and the other reference implementations:

Note: The loupe functions in DiamondLoupeFacet.sol MUST be added to a diamond and are required by the EIP-2535 Diamonds standard.

If this is the case, why is the DiamondLoupeFacet not being added in the constructor, as is done with the DiamondCut facet?

I am a grad student and researcher working on a survey of current upgradeability patterns, so it would be great to connect with you!

-Bill

encodeFunctionData for `multiInit` function inside `DiamondMultiInit` library fails

Hi @mudgen !

First of all thanks a lot for this implementation example πŸ™

I'm hitting a very strange error when trying to deploy & initialise my Diamond along with multiple facets in one transaction.

The function encodeFunctionData used to create the calldata passed into the diamondArgs initCalldata field returns the following error: Error: no matching function (argument="name", value="multiInit", code=INVALID_ARGUMENT, version=abi/5.7.0)

Here is a part of the deploy flow:

  // Just above I deploy my facets & add them to the facetCuts array like in your script 
  // I also use encodeFunctionData to create the calldata needed to create initializeErc20Call, initializeFacetBCall & initializeFacetCCall and it works fine

  const diamondMultiInitFactory = await ethers.getContractFactory("DiamondMultiInit");
    const diamondMultiInit = await diamondMultiInitFactory.deploy();
  await diamondMultiInit.deployed()
  let multiInitCallData = diamondMultiInit.interface.encodeFunctionData('multiInit', [
    [erc20.address, facetB.address, facetC.address],
    [initializeErc20Call, initializeFacetBCall, initializeFacetCCall]
  ])

  const diamondArgs = {
    owner: myOwner.address,
    init: diamondMultiInit.address,
    initCalldata: multiInitCallData
  }

  const diamond = await diamondFactory.deploy(facetCuts, diamondArgs)
  await diamond.deployed()
  console.log('Diamond deployed:', diamond.address)

And when I look into the interface of the deployed DiamondMultiInit library I get:

 interface: Interface {
    fragments: [ [ErrorFragment], [ErrorFragment], [ErrorFragment] ],
    _abiCoder: AbiCoder { coerceFunc: null },
    functions: {}, // Empty is weird
    errors: {
      'AddressAndCalldataLengthDoNotMatch(uint256,uint256)': [ErrorFragment],
      'InitializationFunctionReverted(address,bytes)': [ErrorFragment],
      'NoBytecodeAtAddress(address,string)': [ErrorFragment]
    },
    events: {},
    structs: {},
    deploy: ConstructorFragment {
      name: null,
      type: 'constructor',
      inputs: [],
      payable: false,
      stateMutability: 'nonpayable',
      gas: null,
      _isFragment: true
    },
    _isInterface: true
  }

Now I've tried to play around a bit with the multiInit function to investigate if there was a problem with the arguments I was passing etc and I ended up realising that when I change this multiInit function to a pure or view function (so commenting out the LibDiamond.initializeDiamondCut(_addresses[i], _calldata[i]); code) then I no longer get this no matching function error. And the interface looks correct this time:

interface: Interface {
    fragments: [ [ErrorFragment], [FunctionFragment] ],
    _abiCoder: AbiCoder { coerceFunc: null },
    functions: { 'multiInit(address[],bytes[])': [FunctionFragment] }, // Now I see the multiInit
    errors: {
      'AddressAndCalldataLengthDoNotMatch(uint256,uint256)': [ErrorFragment] // Here I weirdly no longer see InitializationFunctionReverted & NoBytecodeAtAddress errors now, they were present above..
    },
    events: {},
    structs: {},
    deploy: ConstructorFragment {
      name: null,
      type: 'constructor',
      inputs: [],
      payable: false,
      stateMutability: 'nonpayable',
      gas: null,
      _isFragment: true
    },
    _isInterface: true
  }

Also worth noting it behaves correctly when I turn the library into a normal contract, so maybe it's related ?

I can provide the full deploy flow script & facets code if that helps πŸ‘

Thanks a lot in advance for your help :))

How to upgrade a Library

Considering supportive Libraries aren't deployed on their own I wonder how the DiamondCut can broadcast an updated library to all facets. Would be great if this was added to the examples in the /test/ directory. Thank you!

Purpose of `DiamondInit` contract?

I don't quite follow how the DiamondInit contract fits into this example project.

  • It's like a facet of the diamond (since it uses the diamond's storage) but it's never registered as a facet?
  • It's not necessary for setting the diamond's state/calling functions during deployment since we can just as well do that in the diamond's constructor?
    // Code can be added here to perform actions and set state variables.
  • It's also not necessary for setting the state/calling functions during upgrades since we can just as well do that when calling diamondCutβ€”most likely by using the address of the facet that was just upgraded as the _init address?
    /// @param _init The address of the contract or facet to execute _calldata

So then what is the purpose of DiamondInit exactly? Is there a use case that cannot be accomplished without it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.