Cardstack usa un contrato base administratable.sol que proporciona la capacidad de agregar y eliminar administradores y súper administradores, así como modificadores para designar que las funciones solo pueden ser invocadas por administradores y superadministradores, o de forma más restrictiva, solo superadministradores. Además, se utilizan mapas iterables para que revisar introspectivamente las direcciones administrativas.
pragma solidity ^0.4.18;
import "zeppelin-solidity/contracts/ownership/Ownable.sol";
import "zeppelin-solidity/contracts/math/SafeMath.sol";
contract administratable is Ownable {
using SafeMath for uint256;
uint256 public totalAdminsMapping;
uint256 public totalSuperAdminsMapping;
mapping (uint256 => address) public adminsForIndex;
mapping (uint256 => address) public superAdminsForIndex;
mapping (address => bool) public admins;
mapping (address => bool) public superAdmins;
mapping (address => bool) processedAdmin;
mapping (address => bool) processedSuperAdmin;
event AddAdmin(address indexed admin);
event RemoveAdmin(address indexed admin);
event AddSuperAdmin(address indexed admin);
event RemoveSuperAdmin(address indexed admin);
modifier onlyAdmins {
if (msg.sender != owner && !superAdmins[msg.sender] && !admins[msg.sender]) revert();
_;
}
modifier onlySuperAdmins {
if (msg.sender != owner && !superAdmins[msg.sender]) revert();
_;
}
function addSuperAdmin(address admin) public onlyOwner {
superAdmins[admin] = true;
if (!processedSuperAdmin[admin]) {
processedSuperAdmin[admin] = true;
superAdminsForIndex[totalSuperAdminsMapping] = admin;
totalSuperAdminsMapping = totalSuperAdminsMapping.add(1);
}
AddSuperAdmin(admin);
}
function removeSuperAdmin(address admin) public onlyOwner {
superAdmins[admin] = false;
RemoveSuperAdmin(admin);
}
function addAdmin(address admin) public onlySuperAdmins {
admins[admin] = true;
if (!processedAdmin[admin]) {
processedAdmin[admin] = true;
adminsForIndex[totalAdminsMapping] = admin;
totalAdminsMapping = totalAdminsMapping.add(1);
}
AddAdmin(admin);
}
function removeAdmin(address admin) public onlySuperAdmins {
admins[admin] = false;
RemoveAdmin(admin);
}
}
A modo de referencia, aparece el Registry.sol completo que muestra cómo se agrega el almacenamiento, cómo se registran y cómo se actualizan los contratos:
pragma solidity ^0.4.18;
import "zeppelin-solidity/contracts/ownership/Ownable.sol";
import "zeppelin-solidity/contracts/math/SafeMath.sol";
import "./upgradeable.sol";
import "./ExternalStorage.sol";
import "./CstLedger.sol";
import "./administratable.sol";
import "./configurable.sol";
import "./storable.sol";
import "./freezable.sol";
import "./ERC20.sol";
contract Registry is Ownable, administratable, upgradeable {
using SafeMath for uint256;
bytes4 constant INTERFACE_META_ID = 0x01ffc9a7;
bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de;
uint256 public numContracts;
mapping(bytes32 => address) public storageForHash;
mapping(bytes32 => address) public contractForHash;
mapping(bytes32 => bytes32) public hashForNamehash;
mapping(bytes32 => bytes32) public namehashForHash;
mapping(uint256 => string) public contractNameForIndex;
event ContractRegistered(address indexed _contract, string _name, bytes32 namehash);
event ContractUpgraded(address indexed successor, address indexed predecessor, string name, bytes32 namehash);
event StorageAdded(address indexed storageAddress, string name);
event StorageRemoved(address indexed storageAddress, string name);
event AddrChanged(bytes32 indexed node, address a);
function() public {
revert();
}
function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
return interfaceId == ADDR_INTERFACE_ID ||
interfaceId == INTERFACE_META_ID;
}
function addr(bytes32 node) public view returns (address) {
return contractForHash[hashForNamehash[node]];
}
function getContractHash(string name) public view unlessUpgraded returns (bytes32) {
return keccak256(name);
}
function register(string name, address contractAddress, bytes32 namehash) public onlySuperAdmins unlessUpgraded returns (bool) {
bytes32 hash = keccak256(name);
require(bytes(name).length > 0);
require(contractAddress != 0x0);
require(contractForHash[hash] == 0x0);
require(hashForNamehash[namehash] == 0x0);
contractNameForIndex[numContracts] = name;
contractForHash[hash] = contractAddress;
if (namehash != 0x0) {
hashForNamehash[namehash] = hash;
namehashForHash[hash] = namehash;
}
numContracts = numContracts.add(1);
address storageAddress = storageForHash[storable(contractAddress).getStorageNameHash()];
address ledgerAddress = storageForHash[storable(contractAddress).getLedgerNameHash()];
if (storageAddress != 0x0) {
ExternalStorage(storageAddress).addAdmin(contractAddress);
}
if (ledgerAddress != 0x0) {
CstLedger(ledgerAddress).addAdmin(contractAddress);
}
configurable(contractAddress).configureFromStorage();
ContractRegistered(contractAddress, name, namehash);
if (namehash != 0x0) {
AddrChanged(namehash, contractAddress);
}
return true;
}
function upgradeContract(string name, address successor) public onlySuperAdmins unlessUpgraded returns (bytes32) {
bytes32 hash = keccak256(name);
require(successor != 0x0);
require(contractForHash[hash] != 0x0);
address predecessor = contractForHash[hash];
contractForHash[hash] = successor;
uint256 remainingContractBalance;
// we need https://github.com/ethereum/EIPs/issues/165
// to be able to see if a contract is ERC20 or not...
if (hash == keccak256("cst")) {
remainingContractBalance = ERC20(predecessor).balanceOf(predecessor);
}
upgradeable(predecessor).upgradeTo(successor,
remainingContractBalance);
upgradeable(successor).upgradedFrom(predecessor);
address successorStorageAddress = storageForHash[storable(successor).getStorageNameHash()];
address successorLedgerAddress = storageForHash[storable(successor).getLedgerNameHash()];
address predecessorStorageAddress = storageForHash[storable(predecessor).getStorageNameHash()];
address predecessorLedgerAddress = storageForHash[storable(predecessor).getLedgerNameHash()];
if (successorStorageAddress != 0x0) {
ExternalStorage(successorStorageAddress).addAdmin(successor);
}
if (predecessorStorageAddress != 0x0) {
ExternalStorage(predecessorStorageAddress).removeAdmin(predecessor);
}
if (successorLedgerAddress != 0x0) {
CstLedger(successorLedgerAddress).addAdmin(successor);
}
if (predecessorLedgerAddress != 0x0) {
CstLedger(predecessorLedgerAddress).removeAdmin(predecessor);
}
configurable(successor).configureFromStorage();
if (namehashForHash[hash] != 0x0) {
AddrChanged(namehashForHash[hash], successor);
}
ContractUpgraded(successor, predecessor, name, namehashForHash[hash]);
return hash;
}
function addStorage(string name, address storageAddress) public onlySuperAdmins unlessUpgraded {
bytes32 hash = keccak256(name);
storageForHash[hash] = storageAddress;
StorageAdded(storageAddress, name);
}
function getStorage(string name) public view unlessUpgraded returns (address) {
return storageForHash[keccak256(name)];
}
function removeStorage(string name) public onlySuperAdmins unlessUpgraded {
address storageAddress = storageForHash[keccak256(name)];
delete storageForHash[keccak256(name)];
StorageRemoved(storageAddress, name);
}
}
■ ProgramadorHassan Abdel-Rahman
Developer at Cardstack
■ Links de la empresahttps://cardstack.com/https://twitter.com/cardstackhttps://www.facebook.com/cardstackprojecthttps://medium.com/cardstack■ Chat Oficial de TelegramIngles: h
Ingles: https://t.me/cardstackEspañol:
Español: http://bit.ly/2FauvfP■ Criptoinversores LATAMYoutube:
http://bit.ly/2ndiNu7Twitter:
http://bit.ly/2nexRa0Linkedin:
http://bit.ly/2GimYgb