Skip to content

Instantly share code, notes, and snippets.

@panukettu
Last active June 6, 2022 17:27
Show Gist options
  • Save panukettu/cef39bd5f1b6d127b4b929663e43fd06 to your computer and use it in GitHub Desktop.
Save panukettu/cef39bd5f1b6d127b4b929663e43fd06 to your computer and use it in GitHub Desktop.
Diamond with free function storage
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {s} from './Storage.sol';
contract Logic {
function setFoo(string memory newFoo) external {
s()._setFoo(newFoo);
}
function foo() external view returns (string memory) {
return s()._getFoo();
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {s} from './Storage.sol';
contract Logic2 {
function setFoo2(string memory newFoo) external {
s()._setFoo(newFoo);
}
function foo2() external view returns (string memory) {
return s()._getFoo();
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {s} from './Storage.sol';
import {Logic} from './Logic.sol';
import {Logic2} from './Logic2.sol';
/**
* @title Proxy
* @custom:dev-run-script tests/proxy.test.js
*/
contract Proxy {
constructor() payable {
Logic logic = new Logic();
Logic2 logic2 = new Logic2();
bytes4[] memory sigs = new bytes4[](2);
sigs[0] = logic.setFoo.selector;
sigs[1] = logic.foo.selector;
s().addRoutes(address(logic), sigs);
bytes4[] memory sigs2 = new bytes4[](2);
sigs2[0] = logic2.setFoo2.selector;
sigs2[1] = logic2.foo2.selector;
s().addRoutes(address(logic2), sigs2);
}
fallback() external payable {
// get facet from function selectors
address facet = s().target();
require(facet != address(0), '0-addr');
// Execute external function from facet using delegatecall and return any value.
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())
}
}
}
}
// Right click on the script name and hit "Run" to execute
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Proxy", function () {
it("should work", async function () {
const Proxy = await ethers.getContractFactory("Proxy");
const proxy = await Proxy.deploy();
await proxy.deployed();
const Logic = await ethers.getContractAt('Logic', proxy.address);
const Logic2 = await ethers.getContractAt('Logic2', proxy.address);
await Logic.setFoo('FooSetFromLogic');
let fooFrom1 = await Logic.foo();
let fooFrom2 = await Logic2.foo2();
expect(fooFrom1).to.equal('FooSetFromLogic');
expect(fooFrom1).to.equal(fooFrom2);
await Logic2.setFoo2('FooSetFromLogic2');
fooFrom1 = await Logic.foo();
fooFrom2 = await Logic2.foo2();
expect(fooFrom1).to.equal('FooSetFromLogic2');
console.log('done');
expect(fooFrom1).to.equal(fooFrom2);
});
});
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
struct Route {
address destination;
bytes4 selector;
}
struct State {
string foo;
mapping(bytes4 => Route) routes;
}
using {_setFoo, _getFoo, addRoutes, target} for State global;
function _getFoo(State storage self) view returns (string memory) {
return self.foo;
}
function target(State storage self) view returns (address) {
return self.routes[msg.sig].destination;
}
function addRoutes(State storage self, address destination, bytes4[] memory sigs) {
for(uint i; i < sigs.length; i++) {
self.routes[sigs[i]] = Route(destination, sigs[i]);
}
}
function _setFoo(State storage self, string memory _newFoo) {
self.foo = _newFoo;
}
function s() pure returns (State storage state) {
bytes32 position = keccak256("diamond.storage.file");
assembly {
state.slot := position
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment