v0.4.0
An extensible framework of registrars, factories and product smart contracts for the Ethereum platform.
WARNING: This code base and the contract framework it enables is experimental and has not yet been independently audited.
On Ethereum, immutible code in the form of smart contracts poses potential hazards when that code doesn't work as intended. It is by nature difficult to stop, correct and upgrade unless explicitly written to do so. Strategies to mitagate these hazards advise against writing complex contracts in the first place and instead encourage simplicity and atomicity of logic. While there is utilitarian scope for contracts of isolated simple function, it would seem an unlikely way to fully realise the potential of Ethereum as a distributed organisation and applications technology.
This framework seeks to provide a modular foundation for complex organisational and functional structures to flourish on the Ethereum platform.
The Sandal Straps framework seeks to provide a modular kernel to manage a scalable and extensible body of interacting contracts, in order to develop complex upgradeable organisations from simpler component contracts.
There are four primary categorys of contracts in the framework:
- SandalStraps kernel which holds and manages a registrar called
metaregistrar
in which factories, registrars and administerative contracts are registered. - Registrars hold Name->Index and Index->Address mappings to registered contract.
- Factories are contracts containing bytecode of a product contract for deployment.
- Product contracts could be anything providing a required functionality and
are created from a registered factory and registered in a
registrar
of the same name as the factory. They may be independent from the framework or be an interactive component unto it. - Ancillary contracts are any contracts that support the framework or organization by providing managment controls such as fee values, permissioning or bytes mappings for contract resource lookup.
A SandalStraps organization can be considered to be the sum total of contract
functionally and data registered in its metaRegistrar
. In this way
administrative and functional components can be added or updgraded by
registering the necessary contracts and their factories. If an organization's
SandalStraps kernel itself requires upgrading then ownership of its
metaRegistrar
can simply be transferred to the new kernal.
SandalStraps compliant contracts MUST implement a common constructor and
minimal API derived from the RegBase
contract. This ensures that a contract
has the minimum set of properties and methods in order to be managed by
registrar
and UI components:
<ContractName>(address _creator, bytes32 _regName, address _owner)
- The compliant constructoraddress owner
- An address used for owner permissioned actions upon the contractbytes32 regName
- A name by which a contract is read by aRegistrar
contractbytes32 resource
- A datum which can be interpreted to discover additional resources pertaining to the contractchangeOwner(address _addr)
- A function by which contract ownership transfer is initited
An initialization function may be required where a contract would normally initialize state during construction, e.g.,
_init(<args>)
Where a contract cannot otherwise determine initization status, a dedicated boolean state variable should initialized to true and deleted upon successful initialization.
bool __initFuse = true;
Additional interfaces that are used in the framework consist of the Value
API and Withdrawable
ether
payments API which implement:
function value() public constant returns (uint)
function withdrawAll() public returns (bool)
SandalStraps Registrars are triple key lookup tables for contract registration.
Internally, Registrars store lookup data by Name->Index
and Index->Address
mappings with the regName
being returned from the contract being looked up.
This gives the lookup keys of regName
,index
and address
.
Registrations can be added and removed by the Registrar
owner
and the
registered contract's owner
.
A Factory
extends the SandalStraps compliant RegBase
API and contains the
compiled byte code of a product contract. This allows self-contained
deployment of a contract by a simple call to the function
createNew(bytes32 _regName, address _owner)
together with an ether value if
the factory specifies a product price.
Factory
also implements the Value
and Withdrawable
API's. The owner may
set the value
state which is interpreted as a price in ether that must be paid
in order to create a product contract.
Typically a product is created and registered into an associated SandalStraps
organisation by a call to newProduct()
. Products can be created with a direct
call to the factory but will not be advantaged by registration into a
SandalStraps organization.
Adding a factory to a SandalStraps instance will register it into the factories
registrar and also create a registrar of the factory's regName
if it didn't
already exist. When a product is created through newProduct()
, it is
entered into the registrar of its factory's name.
SandalStraps
provides the central interface and core management of a
SandalStraps
organization. Its primary purpose is to manage registrars,
reserved names, provision product creation and collect and transfer fees.
Proxying functionality allows it to arbitrarily own and call contracts and
thereby hold ownership of and/or value in value holding contracts such as
ERC20 tokens. Only the owner is permissioned to used the callAsContract()
function.
A number of administrative names are reserved to which only the owner can register contracts of those names. While the owner can reserve any name, the following names are explicity reserved in code:
metaregistrar
- Being for the root level registrarfactories
- being for the registrar in which factory contracts are registeredregistrar
- being for the registrar when product registrars are registeredsswallet
- being for an optional wallet contract to which funds are sent upon callingwithdrawAll()
. If this name is not registered inmetaregistrar
, funds will be sent to the SandalStrapscreator
which is typically the SandalStraps factory.sscommision
being for an optionalValue
contract which provides a divisor to calculate a commission payable to the SandalStraps contract on top of a factory price when a product contract is created. A typical value might be in the range of 20~1000 which give percentages of 5% to 0.1%.ssfactoryfee
being for an optionalValue
contract which provides a fee required to be paid when registering a factory. The owner is not charged the fee and may set it impossibly high to prevent 3rd parties registering factories.
SandalStraps compliant contracts which handle ether MUST implement at least
the minimal Withdrawable
API
consisting of:
function withdrawAll()
event Deposit(address _from, uint _value)
event Withdrawal(address _by, address _to, _value)
If a contract handles ether for multiple addresses, it SHOULD implement:
function withdrawAllFor(address[] _addrs)
The SandalStraps
contract and compliant factories may collect optional fees
and commissions.
An owner of a factory contract can set its value
by calling
changeValue(uint _value)
.
This value is interpreted as a price in ether which must be paid upon calling
createNew()
.
In addition to a possible Factory
fee, fees can be charged by a SandalStraps
instance upon registering a new Factory
through addFactory()
and creating
a new product by calling newProduct()
.
In order for a SandalStraps
instance to collect fees, one or both Value
contracts with the regName
of sscommision
and ssfactoryfee
need to be
registered in metaregistrar
The fees collected by the SandalStraps instance can be withdrawn to its
creator
or a contract called sswallet
if one is registered in
metaregistrar
.
Fees collected by a Factory
can be withdrawn to the factory owner.
Any contract can be written as SandalStraps
compliant by importing
Factory.sol
which will in turn import RegBase
as in the following
boilerplate RegBase
and Factory
examples.
import "https://github.com/o0ragman0o/SandalStraps/contracts/Factory.sol";
pragma solidity ^0.4.17;
contract SS_Foo is RegBase
{
bytes32 public constant VERSION = "SS_Foo v0.0.1";
// Structs, State variables, etc
function SS_Foo(address _creator, bytes32 _regName, address _owner)
RegBase(_creator, _regName, _owner)
{
// contract specific construction
}
...
}
contract FooFactory is Factory
{
bytes32 constant public regName = "Foos";
bytes32 constant public VERSION = "Foo_Factory v0.0.1";
function BaktFactory(address _creator, bytes32 _regName, address _owner)
Factory(_creator, regName, _owner)
{
// note that the constant `regName` is passed to the super rather
// than parameter `_regname`
_regName; // to quiet compiler warning
}
function createNew(bytes32 _regName, address _owner)
payable
feePaid
returns (bool)
{
last = new Foo(owner, _regName, msg.sender);
Created(msg.sender, _regName, last);
return true;
}
}
The Factory
can then be deployed to the blockchain and its address registered
to a SandalStraps
kernel instance by calling the function
registerFactory(address _factoryAddress)
the add factory fee if one is
specified by ssfactoryfee
.
Adding a Factory
in this way will also create an associated Registrar
of
the regName
of the Factory
. All product contracts of that factory which
are created by calling:
function newFromFactory(bytes32 _factoryRegName, bytes32 _newContractRegName)
will be registered in that registrar.