Skip to content

Instantly share code, notes, and snippets.

@jiexi
Last active October 25, 2021 15:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jiexi/27fb1c7e8241b78b4264f497e49efd2f to your computer and use it in GitHub Desktop.
Save jiexi/27fb1c7e8241b78b4264f497e49efd2f to your computer and use it in GitHub Desktop.
Eternal Storage by Decentralized DNS Example
The blog post that explains how these contracts work can be found at:
https://hackernoon.com/eternal-storage-building-dapps-for-the-future-stl3xre
pragma solidity >=0.5.14 <0.6.0;
contract Base {
EternalStorage internal es;
constructor(address _es) public {
es = EternalStorage(_es);
}
modifier onlyNetworkContracts() {
require(
hasRole("contract") == true,
"Sender is not a contract in the network"
);
_;
}
modifier onlyNetworkContract(string memory _contractName) {
address contractAddress = getContractAddress(_contractName);
require(contractAddress == msg.sender, "Incorrect or outdated contract access used");
_;
}
modifier onlyRole(string memory _role) {
require(
hasRole(_role) == true,
"Sender doesn't have matching role"
);
_;
}
function getContractAddress(string memory _contractName) public view returns(address) {
address contractAddress = es.getAddress(
keccak256(abi.encodePacked("contracts", _contractName, "address"))
);
require(address(contractAddress) != address(0x0), "Contract not found");
return contractAddress;
}
function hasRole(string memory _role) public view returns(bool) {
return es.getBool(
keccak256(abi.encodePacked("access.role", _role, msg.sender))
) ;
}
}
pragma solidity >=0.5.14 <0.6.0;
contract ContractAdmin is Base {
constructor(address _es) Base(_es) public {}
function addContract(
string memory _name,
address _contractAddress,
string memory _contractAbi
) public onlyRole("owner") {
require(_contractAddress != address(0x0), "Invalid contract address");
address existingContractName = es.getAddress(keccak256(abi.encodePacked("contracts", _name, "address")));
require(existingContractName == address(0x0), "Contract name is already in use");
bool existingContractAddress = es.getBool(keccak256(abi.encodePacked("access.role", "contract", _contractAddress)));
require(!existingContractAddress, "Contract address is already in use");
es.setAddress(keccak256(abi.encodePacked("contracts", _name, "address")), _contractAddress);
es.setString(keccak256(abi.encodePacked("contracts", _name, "abi")), _contractAbi);
es.setBool(keccak256(abi.encodePacked("access.role", "contract", _contractAddress)), true);
}
function upgradeContract(
string memory _name,
address _upgradedContractAddress,
string memory _upgradedContractAbi,
bool _forceEther
) public onlyRole("owner") {
require(_upgradedContractAddress != address(0x0), "Invalid contract address");
address oldContractAddress = es.getAddress(keccak256(abi.encodePacked("contracts", _name, "address")));
require(oldContractAddress != address(0x0), "Contract name does not exist");
require(oldContractAddress != _upgradedContractAddress, "Upgraded contract address must not be existing contract address");
if (!_forceEther) {
require(oldContractAddress.balance == 0, "Existing contract has an ether balance");
}
es.setAddress(keccak256(abi.encodePacked("contracts", _name, "address")), _upgradedContractAddress);
es.setString(keccak256(abi.encodePacked("contracts", _name, "abi")), _upgradedContractAbi);
es.setBool(keccak256(abi.encodePacked("access.role", "contract", _upgradedContractAddress)), true);
es.deleteBool(keccak256(abi.encodePacked("access.role", "contract", oldContractAddress)));
}
}
pragma solidity >=0.5.14 <0.6.0;
pragma experimental ABIEncoderV2;
contract DNS {
DomainsModel _domains;
constructor(address _es) public {
_domains = new DomainsModel(_es);
}
function claim(string memory name) public {
address domainOwner = _domains.getOwner(name);
if (domainOwner == address(0)) {
_domains.setOwner(name, msg.sender);
return;
}
require(domainOwner == msg.sender, "Domain has already been claimed");
}
function updateRecords(string memory name, DomainsModel.DomainRecord[] memory records) public {
claim(name);
_domains.setRecords(name, records);
}
function release(string memory name) public {
address domainOwner = _domains.getOwner(name);
require(domainOwner == msg.sender, "Sender does not own this domain");
_domains.setRecords(name, []);
_domains.deleteOwner(name);
}
function domains(string memory name) public view
returns (address owner_)
{
address domainOwner = _domains.getOwner(name);
owner_ = domainOwner;
}
function recordsForName(string memory name) public view
returns (DomainsModel.DomainRecord[] memory recordsForName_)
{
DomainsModel.DomainRecord[] memory records = _domains.getRecords(name);
recordsForName_ = records;
}
function domainNamesForOwner(address owner) public view
returns (string[] memory domainNamesForOwner_)
{
string[] memory domainNames = _domains.getDomainNamesForOwner(owner);
domainNamesForOwner_ = domainNames;
}
function domainNamesForSender() public view
returns (string[] memory domainNamesForSender_)
{
string[] memory domainNames = _domains.getDomainNamesForOwner(msg.sender);
domainNamesForSender_ = domainNames;
}
}
pragma solidity >=0.6.4 <0.7.0;
pragma experimental ABIEncoderV2;
contract DNS {
struct DomainRecord {
string _type; // "A", "CNAME", "MX", etc...
string value;
uint ttl;
}
struct Domain {
address owner;
DomainRecord[] records;
}
mapping(string => Domain) public domains;
function claim(string memory name) public {
address domainOwner = domains[name].owner;
if (domainOwner == address(0)) {
domains[name].owner = msg.sender;
return;
}
require(domainOwner == msg.sender, "Domain has already been claimed");
}
function updateRecords(string memory name, DomainRecord[] memory records) public {
claim(name);
delete domains[name].records;
uint size = records.length;
for (uint i = 0; i < size; i++) {
domains[name].records.push(records[i]);
}
}
function release(string memory name) public {
require(domains[name].owner == msg.sender, "Sender does not own this domain");
domains[name].owner = address(0);
}
function recordsForName(string memory name) public view
returns (DomainRecord[] memory recordsForName_)
{
recordsForName_ = domains[name].records;
}
}
pragma solidity >=0.6.4 <0.7.0;
pragma experimental ABIEncoderV2;
contract DNS {
struct DomainRecord {
string _type; // "A", "CNAME", "MX", etc...
string value;
uint ttl;
}
struct Domain {
address owner;
DomainRecord[] records;
}
mapping(string => Domain) public domains;
function claim(string memory name) public {
address domainOwner = domains[name].owner;
if (domainOwner == address(0)) {
domains[name].owner = msg.sender;
return;
}
require(domainOwner == msg.sender, "Domain has already been claimed");
}
function updateRecords(string memory name, DomainRecord[] memory records) public {
claim(name);
delete domains[name].records;
uint size = records.length;
for (uint i = 0; i < size; i++) {
domains[name].records.push(records[i]);
}
}
function release(string memory name) public {
require(domains[name].owner == msg.sender, "Sender does not own this domain");
delete domains[name].records; // now it clears records during release
domains[name].owner = address(0);
}
function recordsForName(string memory name) public view
returns (DomainRecord[] memory recordsForName_)
{
recordsForName_ = domains[name].records;
}
}
pragma solidity >=0.5.14 <0.6.0;
pragma experimental ABIEncoderV2;
contract DNS {
DomainsModel _domains;
constructor(address _es) public {
_domains = new DomainsModel(_es);
}
function claim(string memory name) public {
address domainOwner = _domains.getOwner(name);
if (domainOwner == address(0)) {
_domains.setOwner(name, msg.sender);
return;
}
require(domainOwner == msg.sender, "Domain has already been claimed");
}
function updateRecords(string memory name, DomainsModel.DomainRecord[] memory records) public {
claim(name);
_domains.setRecords(name, records);
}
function release(string memory name) public {
address domainOwner = _domains.getOwner(name);
require(domainOwner == msg.sender, "Sender does not own this domain");
_domains.setRecords(name, []);
_domains.deleteOwner(name);
}
function domains(string memory name) public view
returns (address owner_)
{
address domainOwner = _domains.getOwner(name);
owner_ = domainOwner;
}
function recordsForName(string memory name) public view
returns (DomainsModel.DomainRecord[] memory recordsForName_)
{
DomainsModel.DomainRecord[] memory records = _domains.getRecords(name);
recordsForName_ = records;
}
function domainNamesForOwner(address owner) public view
returns (string[] memory domainNamesForOwner_)
{
string[] memory domainNames = _domains.getDomainNamesForOwner(owner);
domainNamesForOwner_ = domainNames;
}
function domainNamesForSender() public view
returns (string[] memory domainNamesForSender_)
{
string[] memory domainNames = _domains.getDomainNamesForOwner(msg.sender);
domainNamesForSender_ = domainNames;
}
}
pragma solidity >=0.5.14 <0.6.0;
pragma experimental ABIEncoderV2;
contract DNS {
DomainsModel _domains;
constructor(address _es) public {
_domains = new DomainsModel(_es);
}
function claim(string memory name) public {
address domainOwner = _domains.getOwner(name);
if (domainOwner == address(0)) {
_domains.setOwner(name, msg.sender);
return;
}
require(domainOwner == msg.sender, "Domain has already been claimed");
}
function updateRecords(string memory name, DomainsModel.DomainRecord[] memory records) public {
claim(name);
_domains.setRecords(name, records);
}
function release(string memory name) public {
address domainOwner = _domains.getOwner(name);
require(domainOwner == msg.sender, "Sender does not own this domain");
_domains.setRecords(name, []);
_domains.deleteOwner(name);
}
function domains(string memory name) public view
returns (address owner_)
{
address domainOwner = _domains.getOwner(name);
owner_ = domainOwner;
}
function recordsForName(string memory name) public view
returns (DomainsModel.DomainRecord[] memory recordsForName_)
{
DomainsModel.DomainRecord[] memory records = _domains.getRecords(name);
recordsForName_ = records;
}
function domainNamesForOwner(address owner) public view
returns (string[] memory domainNamesForOwner_)
{
string[] memory domainNames = _domains.getDomainNamesForOwner(owner);
domainNamesForOwner_ = domainNames;
}
function domainNamesForSender() public view
returns (string[] memory domainNamesForSender_)
{
string[] memory domainNames = _domains.getDomainNamesForOwner(msg.sender);
domainNamesForSender_ = domainNames;
}
}
pragma solidity >=0.5.14 <0.6.0;
pragma experimental ABIEncoderV2;
contract DomainModel {
struct DomainRecord {
string recordType; // "A", "CNAME", "MX", etc...
string value;
uint ttl;
}
EternalStorage es;
StringListStorage sls;
constructor(address _es) public {
es = EternalStorage(_es);
sls = new StringListStorage(_es);
}
function getOwner(string memory name) public view
returns (address owner_)
{
address domainOwner = es.getAddress(keccak256(abi.encodePacked("domains", name, "owner")));
owner_ = domainOwner;
}
function setOwner(string memory name, address owner) public {
if (owner == address(0)) {
return;
}
es.setAddress(keccak256(abi.encodePacked("domains", name, "owner")), owner);
sls.pushListItem(keccak256(abi.encodePacked("owners", owner, "domains")), name);
}
function setRecords(string memory name, DomainRecord[] memory records) public {
uint length = records.length;
es.setUint(keccak256(abi.encodePacked("domains", name, "records", "length")), length);
for (uint i = 0; i < length; i++) {
es.setString(keccak256(abi.encodePacked("domains", name, "records", i, "type")), records[i].recordType);
es.setString(keccak256(abi.encodePacked("domains", name, "records", i, "value")), records[i].value);
es.setUint(keccak256(abi.encodePacked("domains", name, "records", i, "ttl")), records[i].ttl);
}
}
function deleteOwner(string memory name) public {
address domainOwner = es.getAddress(keccak256(abi.encodePacked("domains", name, "owner")));
if (domainOwner == address(0)) {
return;
}
es.setAddress(keccak256(abi.encodePacked("domains", name, "owner")), address(0));
int index = sls.getListIndexOf(keccak256(abi.encodePacked("owners", domainOwner, "domains")), name);
if (index != -1) {
sls.removeUnorderedListItem(keccak256(abi.encodePacked("owners", domainOwner, "domains")), uint(index));
}
}
function getRecords(string memory name) public view
returns (DomainRecord[] memory recordsForName_)
{
uint length = es.getUint(keccak256(abi.encodePacked("domains", name, "records", "length")));
DomainRecord[] memory records = new DomainRecord[](length);
for (uint i = 0; i < length; i++) {
records[i] = DomainRecord({
recordType: es.getString(keccak256(abi.encodePacked("domains", name, "records", i, "type"))),
value: es.getString(keccak256(abi.encodePacked("domains", name, "records", i, "value"))),
ttl: es.getUint(keccak256(abi.encodePacked("domains", name, "records", i, "ttl")))
});
}
recordsForName_ = records;
}
function getDomainNamesForOwner(address owner) public view
returns (string[] memory domainNamesForOwner_)
{
uint length = sls.getListLength(keccak256(abi.encodePacked("owners", owner, "domains")));
string[] memory domainNames = new string[](length);
for (uint i = 0; i < length; i++) {
domainNames[i] = sls.getListItem(keccak256(abi.encodePacked("owners", owner, "domains")), i);
}
domainNamesForOwner_ = domainNames;
}
}
pragma solidity >=0.5.14 <0.6.0;
pragma experimental ABIEncoderV2;
contract DomainModel is Base {
struct DomainRecord {
string recordType; // "A", "CNAME", "MX", etc...
string value;
uint ttl;
}
constructor(address _es) Base(_es) public {}
function getOwner(string memory name) public view
returns (address owner_)
{
address domainOwner = es.getAddress(keccak256(abi.encodePacked("domains", name, "owner")));
owner_ = domainOwner;
}
function setOwner(string memory name, address owner) onlyNetworkContract("DNS") public {
if (owner == address(0)) {
return;
}
StringListStorage sls = StringListStorage(getContractAddress("StringListStorage"));
es.setAddress(keccak256(abi.encodePacked("domains", name, "owner")), owner);
sls.pushListItem(keccak256(abi.encodePacked("owners", owner, "domains")), name);
}
function setRecords(string memory name, DomainRecord[] memory records) onlyNetworkContract("DNS") public {
uint length = records.length;
es.setUint(keccak256(abi.encodePacked("domains", name, "records", "length")), length);
for (uint i = 0; i < length; i++) {
es.setString(keccak256(abi.encodePacked("domains", name, "records", i, "type")), records[i].recordType);
es.setString(keccak256(abi.encodePacked("domains", name, "records", i, "value")), records[i].value);
es.setUint(keccak256(abi.encodePacked("domains", name, "records", i, "ttl")), records[i].ttl);
}
}
function deleteOwner(string memory name) onlyNetworkContract("DNS") public {
address domainOwner = es.getAddress(keccak256(abi.encodePacked("domains", name, "owner")));
if (domainOwner == address(0)) {
return;
}
es.setAddress(keccak256(abi.encodePacked("domains", name, "owner")), address(0));
StringListStorage sls = StringListStorage(getContractAddress("StringListStorage"));
int index = sls.getListIndexOf(keccak256(abi.encodePacked("owners", domainOwner, "domains")), name);
if (index != -1) {
sls.removeUnorderedListItem(keccak256(abi.encodePacked("owners", domainOwner, "domains")), uint(index));
}
}
function getRecords(string memory name) public view
returns (DomainRecord[] memory recordsForName_)
{
uint length = es.getUint(keccak256(abi.encodePacked("domains", name, "records", "length")));
DomainRecord[] memory records = new DomainRecord[](length);
for (uint i = 0; i < length; i++) {
records[i] = DomainRecord({
recordType: es.getString(keccak256(abi.encodePacked("domains", name, "records", i, "type"))),
value: es.getString(keccak256(abi.encodePacked("domains", name, "records", i, "value"))),
ttl: es.getUint(keccak256(abi.encodePacked("domains", name, "records", i, "ttl")))
});
}
recordsForName_ = records;
}
function getDomainNamesForOwner(address owner) public view
returns (string[] memory domainNamesForOwner_)
{
StringListStorage sls = StringListStorage(getContractAddress("StringListStorage"));
uint length = sls.getListLength(keccak256(abi.encodePacked("owners", owner, "domains")));
string[] memory domainNames = new string[](length);
for (uint i = 0; i < length; i++) {
domainNames[i] = sls.getListItem(keccak256(abi.encodePacked("owners", owner, "domains")), i);
}
domainNamesForOwner_ = domainNames;
}
}
pragma solidity >=0.5.14 <0.6.0;
contract EternalStorage {
mapping(bytes32 => uint256) private uIntStorage;
mapping(bytes32 => string) private stringStorage;
mapping(bytes32 => address) private addressStorage;
mapping(bytes32 => bytes) private bytesStorage;
mapping(bytes32 => bool) private boolStorage;
mapping(bytes32 => int256) private intStorage;
mapping(bytes32 => bytes32) private bytes32Storage;
modifier onlyNetworkContract() {
if (boolStorage[keccak256(abi.encodePacked("contract.storage.initialized"))] == true) {
require(boolStorage[keccak256(abi.encodePacked("access.role", "contract", msg.sender))] == true, "Sender is not a contract in the network");
}
_;
}
constructor() public {
boolStorage[keccak256(abi.encodePacked("access.role", "owner", msg.sender))] = true;
}
// Get
function getAddress(bytes32 _key) external view returns (address) {
return addressStorage[_key];
}
function getUint(bytes32 _key) external view returns (uint) {
return uIntStorage[_key];
}
function getString(bytes32 _key) external view returns (string memory) {
return stringStorage[_key];
}
function getBytes(bytes32 _key) external view returns (bytes memory) {
return bytesStorage[_key];
}
function getBool(bytes32 _key) external view returns (bool) {
return boolStorage[_key];
}
function getInt(bytes32 _key) external view returns (int) {
return intStorage[_key];
}
function getBytes32(bytes32 _key) external view returns (bytes32) {
return bytes32Storage[_key];
}
// Set
function setAddress(bytes32 _key, address _value) external onlyNetworkContract {
addressStorage[_key] = _value;
}
function setUint(bytes32 _key, uint _value) external onlyNetworkContract {
uIntStorage[_key] = _value;
}
function setString(bytes32 _key, string memory _value) public onlyNetworkContract {
stringStorage[_key] = _value;
}
function setBytes(bytes32 _key, bytes memory _value) public onlyNetworkContract {
bytesStorage[_key] = _value;
}
function setBool(bytes32 _key, bool _value) external onlyNetworkContract {
boolStorage[_key] = _value;
}
function setInt(bytes32 _key, int _value) external onlyNetworkContract {
intStorage[_key] = _value;
}
function setBytes32(bytes32 _key, bytes32 _value) external onlyNetworkContract {
bytes32Storage[_key] = _value;
}
// Delete
function deleteAddress(bytes32 _key) external onlyNetworkContract {
delete addressStorage[_key];
}
function deleteUint(bytes32 _key) external onlyNetworkContract {
delete uIntStorage[_key];
}
function deleteString(bytes32 _key) external onlyNetworkContract {
delete stringStorage[_key];
}
function deleteBytes(bytes32 _key) external onlyNetworkContract {
delete bytesStorage[_key];
}
function deleteBool(bytes32 _key) external onlyNetworkContract {
delete boolStorage[_key];
}
function deleteInt(bytes32 _key) external onlyNetworkContract {
delete intStorage[_key];
}
function deleteBytes32(bytes32 _key) external onlyNetworkContract {
delete bytes32Storage[_key];
}
}
pragma solidity >=0.5.14 <0.6.0;
contract Proxy {
address private delegate;
address private owner = msg.sender;
// allows us to update where the proxy contract points to
function upgradeDelegate(address newDelegateAddress) public {
require(msg.sender == owner, "Sender is not owner");
delegate = newDelegateAddress;
}
// fallback function
function() external payable {
assembly {
let _target := sload(0)
calldatacopy(0x0, 0x0, calldatasize)
let result := delegatecall(gas(), _target, 0x0, calldatasize, 0x0, 0)
returndatacopy(0x0, 0x0, returndatasize)
switch result case 0 {revert(0, 0)} default {return (0, returndatasize)}
}
}
}
pragma solidity >=0.5.14 <0.6.0;
contract StringListStorage {
EternalStorage es;
constructor(address _es) public {
es = EternalStorage(_es);
}
// Get
function getListLength(bytes32 _key) external view returns (uint) {
return es.getUint(keccak256(abi.encodePacked(_key, "length")));
}
function getListItem(bytes32 _key, uint _index) external view returns (string memory) {
return es.getString(keccak256(abi.encodePacked(_key, "item", _index)));
}
function getListIndexOf(bytes32 _key, string memory _value) public view returns (int) {
uint length = es.getUint(keccak256(abi.encodePacked(_key, "length")));
for (uint index = 0; index < length; ++index) {
string memory value = es.getString(keccak256(abi.encodePacked(_key, "item", index)));
if (keccak256(abi.encodePacked(value)) == keccak256(abi.encodePacked(_value))) {
return int(index);
}
}
return -1;
}
// Set
function setListItem(bytes32 _key, uint _index, string memory _value) public {
require(_index < es.getUint(keccak256(abi.encodePacked(_key, "length"))), "List index out of bounds");
es.setString(keccak256(abi.encodePacked(_key, "item", _index)), _value);
}
function pushListItem(bytes32 _key, string memory _value) public {
uint length = es.getUint(keccak256(abi.encodePacked(_key, "length")));
es.setString(keccak256(abi.encodePacked(_key, "item", length)), _value);
es.setUint(keccak256(abi.encodePacked(_key, "length")), length + 1);
}
function removeUnorderedListItem(bytes32 _key, uint _index) external {
require(_index < es.getUint(keccak256(abi.encodePacked(_key, "length"))), "List index out of bounds");
uint length = es.getUint(keccak256(abi.encodePacked(_key, "length")));
if (_index < length - 1) {
string memory lastItem = es.getString(keccak256(abi.encodePacked(_key, "item", length - 1)));
es.setString(keccak256(abi.encodePacked(_key, "item", _index)), lastItem);
}
es.setUint(keccak256(abi.encodePacked(_key, "length")), length - 1);
}
}
pragma solidity >=0.5.14 <0.6.0;
contract StringListStorage {
EternalStorage es;
constructor(address _es) public {
es = EternalStorage(_es);
}
// Get
function getListLength(bytes32 _key) external view returns (uint) {
return es.getUint(keccak256(abi.encodePacked(_key, "length")));
}
function getListItem(bytes32 _key, uint _index) external view returns (string memory) {
return es.getString(keccak256(abi.encodePacked(_key, "item", _index)));
}
function getListIndexOf(bytes32 _key, string memory _value) public view returns (int) {
uint length = es.getUint(keccak256(abi.encodePacked(_key, "length")));
for (uint index = 0; index < length; ++index) {
string memory value = es.getString(keccak256(abi.encodePacked(_key, "item", index)));
if (keccak256(abi.encodePacked(value)) == keccak256(abi.encodePacked(_value))) {
return int(index);
}
}
return -1;
}
// Set
function setListItem(bytes32 _key, uint _index, string memory _value) onlyNetworkContracts public {
require(_index < es.getUint(keccak256(abi.encodePacked(_key, "length"))), "List index out of bounds");
es.setString(keccak256(abi.encodePacked(_key, "item", _index)), _value);
}
function pushListItem(bytes32 _key, string memory _value) onlyNetworkContracts public {
uint length = es.getUint(keccak256(abi.encodePacked(_key, "length")));
es.setString(keccak256(abi.encodePacked(_key, "item", length)), _value);
es.setUint(keccak256(abi.encodePacked(_key, "length")), length + 1);
}
function removeUnorderedListItem(bytes32 _key, uint _index) onlyNetworkContracts external {
require(_index < es.getUint(keccak256(abi.encodePacked(_key, "length"))), "List index out of bounds");
uint length = es.getUint(keccak256(abi.encodePacked(_key, "length")));
if (_index < length - 1) {
string memory lastItem = es.getString(keccak256(abi.encodePacked(_key, "item", length - 1)));
es.setString(keccak256(abi.encodePacked(_key, "item", _index)), lastItem);
}
es.setUint(keccak256(abi.encodePacked(_key, "length")), length - 1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment