mudgen / diamond-3 Goto Github PK
View Code? Open in Web Editor NEWEIP-2535 Diamonds reference implementation.
License: MIT License
EIP-2535 Diamonds reference implementation.
License: MIT License
Hi, Nick!
Brilliant conception, awesome ideas!
Did you tested next case:
Hi, love the project :).
I'm using the latest version from Master. And the default tests are exceeding the block limit.
`evert@EvertX1:/piedao/Diamond$ git checkout master/piedao/Diamond$ truffle test
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
evert@EvertX1:
Using network 'development'.
Everything is up to date, there is nothing to compile.
Contract: Cache bug test
✓ should not exhibit the cache bug
Contract: DiamondTest
✓ should have three facets -- call to facetAddresses function
✓ facets should have the right function selectors -- call to facetFunctionSelectors function (51ms)
✓ selectors should be associated to facets correctly -- multiple calls to facetAddress function (42ms)
✓ should get all the facets and function selectors of the diamond -- call to facets function
✓ should add test1 functions (139ms)
✓ should add test2 functions (171ms)
✓ should remove some test2 functions (90ms)
1) should remove some test1 functions
> No events were emitted
2) remove all functions and facets accept diamondCut and facets
> No events were emitted
3) add most functions and facets
> No events were emitted
8 passing (3s)
3 failing
Contract: DiamondTest
should remove some test1 functions:
Error: Returned error: Exceeds block gas limit
at Object.ErrorResponse (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/src/errors.js:29:1)
at /home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-requestmanager/src/index.js:140:1
at /home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:112:1
at XMLHttpRequest.request.onreadystatechange (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-providers-http/src/index.js:96:1)
at XMLHttpRequestEventTarget.dispatchEvent (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)
at XMLHttpRequest._setReadyState (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)
at XMLHttpRequest._onHttpResponseEnd (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)
at IncomingMessage. (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:47)
at endReadableNT (_stream_readable.js:1145:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
Contract: DiamondTest
remove all functions and facets accept diamondCut and facets:
Error: Returned error: Exceeds block gas limit
at Object.ErrorResponse (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/src/errors.js:29:1)
at /home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-requestmanager/src/index.js:140:1
at /home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:112:1
at XMLHttpRequest.request.onreadystatechange (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-providers-http/src/index.js:96:1)
at XMLHttpRequestEventTarget.dispatchEvent (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)
at XMLHttpRequest._setReadyState (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)
at XMLHttpRequest._onHttpResponseEnd (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)
at IncomingMessage. (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:47)
at endReadableNT (_stream_readable.js:1145:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
Contract: DiamondTest
add most functions and facets:
Error: Returned error: Exceeds block gas limit
at Object.ErrorResponse (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-helpers/src/errors.js:29:1)
at /home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-core-requestmanager/src/index.js:140:1
at /home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:112:1
at XMLHttpRequest.request.onreadystatechange (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/web3-providers-http/src/index.js:96:1)
at XMLHttpRequestEventTarget.dispatchEvent (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)
at XMLHttpRequest._setReadyState (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)
at XMLHttpRequest._onHttpResponseEnd (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)
at IncomingMessage. (/home/evert/.npm-global/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:47)
at endReadableNT (_stream_readable.js:1145:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
`
Hi Nick!
Thanks for your idea - Brilliant Proxy :)
Do you know are there any ways to avoid problems with carruption of data?
For example, we have two structs with one pointer:
bytes32 constant POINTER = keccak256("any.pointer");
struct DataStruct {
uint32 value;
mapping(address => uint256) authsIndex;
address[] auths;
}
struct DataStructV2 {
uint256 value;
}
function getDataStruct() internal pure returns (DataStruct storage ds) {
bytes32 position = POINTER;
assembly {
ds.slot := position
}
}
function getDataStructV2() internal pure returns (DataStructV2 storage ds) {
bytes32 position = POINTER;
assembly {
ds.slot := position
}
}
If i write 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
in ds.DataStruct2.value
, after writing 341234
in ds.DataStruct.value
, then the second value overrides the first one. But its not ok)
Are there any ways to protect against overwriting old data with an other(new) struct in memory slot already occupied?
If you want to contribute to this project, here's an opportunity to do so.
Make pull requests to implement the versions of diamondCut
listed below or discuss new possible versions in the comments.
The implementations must be gas efficient.
Something that is currently missing from the Diamond Standard is a way to add/replace/remove functions and execute arbitrary code in the same transaction. This is needed for example to initialize/setup storage data after adding functions to a diamond.
It is possible to have different versions of the diamondCut
function that take different arguments and work in different ways. However all versions are used to add/replace/remove functions from diamonds.
But before adding more versions of diamondCut
to the Diamond Standard I want them implemented in this project. In addition, versions of diamondCut
can exist and be used that are not defined in the Diamond Standard. The ones defined in the Diamond Standard are for compatibility between tools.
Just because a version of diamondCut
doesn't exist in the Diamond Standard doesn't mean it can't exist and be used. I'd like this project to include a library of different versions of diamondCut
that are useful in different circumstances.
Implement the following functions in DiamondFacet.sol or other file. All these functions can be private/internal/public/external.
diamondCut(bytes[] memory _diamondCut, address _initializer, bytes memory _data)
This version of diamondCut
is used to execute arbitrary code after adding/replacing/removing functions and passing in _data
as an argument to the arbitrary code.
_initializer
is a contract with a function initialize(bytes memory _data)
that is called using delegatecall
. Obviously the parameter _data
is passed from diamondCut
to initialize
.
diamondCut(bytes memory _diamondCut)
First 20 bytes is the facet address and the rest of the bytes are 4-byte function selectors.
diamondCut(address _facet, bytes memory _data
)This function is used to allow a new facet to add its functions to a diamond and execute arbitrary code, passing _data
as an argument.
_facet
should execute the initialize(bytes memory _data)
using delegatecode
. For example: _facet.delegatecall(abi.encodeWithSignature("initialize(bytes)", _data)
.
From within initialize(bytes memory _data)
a facet should add its function selectors.
I am interested in suggestions and adding other versions of diamondCut
to the project. Feel free to discuss ideas in the comments or in the Diamond Standard Discord: https://discord.gg/kQewPw2
CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 1 slot(s) too deep inside the stack.
selectorTofacet from EIP function is not present in the reference implementation. Is it renamed to FacetAddressAndSelectorPosition?
https://eips.ethereum.org/EIPS/eip-2535#facets-can-be-reusable-and-composable
For true interoperability of facets written and deployed by different authors, it would be useful if selectors could be aliased. For example, both a whitelist and a role management contract might have a function with the signature add(address)
.
As this would likely be a rare occurrence, the best implementation might be an AliasFacet
contract, whose fallback function handles the routing of selector aliases to the target facets. The Diamond
fallback would therefore remain gas-efficient.
Hi, Nick. i was going through the README and observed that the README is not showing new users how they can clone the diamond-3 repo and deploy it. Having clear steps on how to clone the repo, run tests and deploy the contracts to testnet and mainnet could help new blockchain developers explore this product.
If this can be included in the README, this would be nice. we don't assume that everyone coming to this repo is an experienced developer. Thanks
Hi @mudgen ,
I have added few facets to my diamond successfully. Truffle Testing runs smooth.
But as I added this facet, here comes a problem:
I want to put all events into one Facet. The Facet code is like this:
F_events.sol:
pragma solidity ^0.6.12;
// SPDX-License-Identifier: MIT
import "./DiamondStorageContract.sol";
contract F_events is DiamondStorageContract{
event simpleEvent(string eventNotice);
}
The testing Code is this:
...
it('should add F_events functions', async () => {
const selectors = getSelectors(f_events)
addresses.push(f_events.address)
result = await diamondFacet.methods.diamondCut([f_events.address + selectors]).send({ from: web3.eth.defaultAccount, gas: 1000000 })
result = await diamondLoupeFacet.methods.facetFunctionSelectors(addresses[7]).call()
assert.equal(result, '0x' + selectors)
})
I've already understood that in order to interact with the contract I need to provide the ABI of the "useful" facet. But how can I get it when I only have the address of the contract?
Currently the diamondCut
implementation copies calldata to memory. What if we didn't do this and just used the calldata? I check to see if this is possible and if we get a gas savings.
TypeError: Cannot add property 3, object is not extensible
cause by line 120
addresses.push(test1Facet.address)
fix by doing line 68 (array deep copy)
addresses = [...await diamondloupefacet.methods.facetaddresses().call()]
It is conceivable that the author of a Diamond contract might wish to implement a custom fallback function, but the current design does not allow this.
A possible implementation would allow a fallback address
to be set rather than a function
. This address would contain code that itself implements a fallback function.
Maybe the implementation of the fallback would be simpler if I used: address.delegatecall(msg.data)
instead of assembly. I would need to test to see if it uses more gas.
Hi there!
I just briefly checked the code and realized that in order to keep all facets current diamond version uses a dynamically allocated array and simple mapping for getting O(1) per getFacet() query. What if we could simply store everything in a tree in order to get O(log n) for getFacet() query without having a need to allocate an array and mapping(it looks kind of crutch in a code)?
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.