Contract Name:
BMCAssetProxy
Contract Source Code:
File 1 of 1 : BMCAssetProxy
pragma solidity ^0.4.11;
contract BMCPlatform {
mapping(bytes32 => address) public proxies;
function name(bytes32 _symbol) returns(string);
function setProxy(address _address, bytes32 _symbol) returns(uint errorCode);
function isOwner(address _owner, bytes32 _symbol) returns(bool);
function totalSupply(bytes32 _symbol) returns(uint);
function balanceOf(address _holder, bytes32 _symbol) returns(uint);
function allowance(address _from, address _spender, bytes32 _symbol) returns(uint);
function baseUnit(bytes32 _symbol) returns(uint8);
function proxyTransferWithReference(address _to, uint _value, bytes32 _symbol, string _reference, address _sender) returns(uint errorCode);
function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) returns(uint errorCode);
function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) returns(uint errorCode);
function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) returns(uint errorCode);
function reissueAsset(bytes32 _symbol, uint _value) returns(uint errorCode);
function revokeAsset(bytes32 _symbol, uint _value) returns(uint errorCode);
function isReissuable(bytes32 _symbol) returns(bool);
function changeOwnership(bytes32 _symbol, address _newOwner) returns(uint errorCode);
}
contract BMCAsset {
function __transferWithReference(address _to, uint _value, string _reference, address _sender) returns(bool);
function __transferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
function __approve(address _spender, uint _value, address _sender) returns(bool);
function __process(bytes _data, address _sender) payable {
throw;
}
}
contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed from, address indexed spender, uint256 value);
string public symbol;
function totalSupply() constant returns (uint256 supply);
function balanceOf(address _owner) constant returns (uint256 balance);
function transfer(address _to, uint256 _value) returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
function approve(address _spender, uint256 _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint256 remaining);
}
/**
* @title BMC Asset Proxy.
*
* Proxy implements ERC20 interface and acts as a gateway to a single platform asset.
* Proxy adds symbol and caller(sender) when forwarding requests to platform.
* Every request that is made by caller first sent to the specific asset implementation
* contract, which then calls back to be forwarded onto platform.
*
* Calls flow: Caller ->
* Proxy.func(...) ->
* Asset.__func(..., Caller.address) ->
* Proxy.__func(..., Caller.address) ->
* Platform.proxyFunc(..., symbol, Caller.address)
*
* Asset implementation contract is mutable, but each user have an option to stick with
* old implementation, through explicit decision made in timely manner, if he doesn't agree
* with new rules.
* Each user have a possibility to upgrade to latest asset contract implementation, without the
* possibility to rollback.
*
* Note: all the non constant functions return false instead of throwing in case if state change
* didn't happen yet.
*/
contract BMCAssetProxy is ERC20 {
// Supports BMCPlatform ability to return error codes from methods
uint constant OK = 1;
// Assigned platform, immutable.
BMCPlatform public bmcPlatform;
// Assigned symbol, immutable.
bytes32 public smbl;
// Assigned name, immutable.
string public name;
string public symbol;
/**
* Sets platform address, assigns symbol and name.
*
* Can be set only once.
*
* @param _bmcPlatform platform contract address.
* @param _symbol assigned symbol.
* @param _name assigned name.
*
* @return success.
*/
function init(BMCPlatform _bmcPlatform, string _symbol, string _name) returns(bool) {
if (address(bmcPlatform) != 0x0) {
return false;
}
bmcPlatform = _bmcPlatform;
symbol = _symbol;
smbl = stringToBytes32(_symbol);
name = _name;
return true;
}
function stringToBytes32(string memory source) returns (bytes32 result) {
assembly {
result := mload(add(source, 32))
}
}
/**
* Only platform is allowed to call.
*/
modifier onlyBMCPlatform() {
if (msg.sender == address(bmcPlatform)) {
_;
}
}
/**
* Only current asset owner is allowed to call.
*/
modifier onlyAssetOwner() {
if (bmcPlatform.isOwner(msg.sender, smbl)) {
_;
}
}
/**
* Returns asset implementation contract for current caller.
*
* @return asset implementation contract.
*/
function _getAsset() internal returns(BMCAsset) {
return BMCAsset(getVersionFor(msg.sender));
}
/**
* Returns asset total supply.
*
* @return asset total supply.
*/
function totalSupply() constant returns(uint) {
return bmcPlatform.totalSupply(smbl);
}
/**
* Returns asset balance for a particular holder.
*
* @param _owner holder address.
*
* @return holder balance.
*/
function balanceOf(address _owner) constant returns(uint) {
return bmcPlatform.balanceOf(_owner, smbl);
}
/**
* Returns asset allowance from one holder to another.
*
* @param _from holder that allowed spending.
* @param _spender holder that is allowed to spend.
*
* @return holder to spender allowance.
*/
function allowance(address _from, address _spender) constant returns(uint) {
return bmcPlatform.allowance(_from, _spender, smbl);
}
/**
* Returns asset decimals.
*
* @return asset decimals.
*/
function decimals() constant returns(uint8) {
return bmcPlatform.baseUnit(smbl);
}
/**
* Transfers asset balance from the caller to specified receiver.
*
* @param _to holder address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transfer(address _to, uint _value) returns(bool) {
if (_to != 0x0) {
return _transferWithReference(_to, _value, "");
}
else {
return false;
}
}
/**
* Transfers asset balance from the caller to specified receiver adding specified comment.
*
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a platform's Transfer event.
*
* @return success.
*/
function transferWithReference(address _to, uint _value, string _reference) returns(bool) {
if (_to != 0x0) {
return _transferWithReference(_to, _value, _reference);
}
else {
return false;
}
}
/**
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @return success.
*/
function _transferWithReference(address _to, uint _value, string _reference) internal returns(bool) {
return _getAsset().__transferWithReference(_to, _value, _reference, msg.sender);
}
/**
* Performs transfer call on the platform by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a platform's Transfer event.
* @param _sender initial caller.
*
* @return success.
*/
function __transferWithReference(address _to, uint _value, string _reference, address _sender) onlyAccess(_sender) returns(bool) {
return bmcPlatform.proxyTransferWithReference(_to, _value, smbl, _reference, _sender) == OK;
}
/**
* Prforms allowance transfer of asset balance between holders.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transferFrom(address _from, address _to, uint _value) returns(bool) {
if (_to != 0x0) {
return _getAsset().__transferFromWithReference(_from, _to, _value, "", msg.sender);
}
else {
return false;
}
}
/**
* Performs allowance transfer call on the platform by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a platform's Transfer event.
* @param _sender initial caller.
*
* @return success.
*/
function __transferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) onlyAccess(_sender) returns(bool) {
return bmcPlatform.proxyTransferFromWithReference(_from, _to, _value, smbl, _reference, _sender) == OK;
}
/**
* Sets asset spending allowance for a specified spender.
*
* @param _spender holder address to set allowance to.
* @param _value amount to allow.
*
* @return success.
*/
function approve(address _spender, uint _value) returns(bool) {
if (_spender != 0x0) {
return _getAsset().__approve(_spender, _value, msg.sender);
}
else {
return false;
}
}
/**
* Performs allowance setting call on the platform by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _spender holder address to set allowance to.
* @param _value amount to allow.
* @param _sender initial caller.
*
* @return success.
*/
function __approve(address _spender, uint _value, address _sender) onlyAccess(_sender) returns(bool) {
return bmcPlatform.proxyApprove(_spender, _value, smbl, _sender) == OK;
}
/**
* Emits ERC20 Transfer event on this contract.
*
* Can only be, and, called by assigned platform when asset transfer happens.
*/
function emitTransfer(address _from, address _to, uint _value) onlyBMCPlatform() {
Transfer(_from, _to, _value);
}
/**
* Emits ERC20 Approval event on this contract.
*
* Can only be, and, called by assigned platform when asset allowance set happens.
*/
function emitApprove(address _from, address _spender, uint _value) onlyBMCPlatform() {
Approval(_from, _spender, _value);
}
/**
* Resolves asset implementation contract for the caller and forwards there transaction data,
* along with the value. This allows for proxy interface growth.
*/
function () payable {
_getAsset().__process.value(msg.value)(msg.data, msg.sender);
}
/**
* Indicates an upgrade freeze-time start, and the next asset implementation contract.
*/
event UpgradeProposal(address newVersion);
// Current asset implementation contract address.
address latestVersion;
// Proposed next asset implementation contract address.
address pendingVersion;
// Upgrade freeze-time start.
uint pendingVersionTimestamp;
// Timespan for users to review the new implementation and make decision.
uint constant UPGRADE_FREEZE_TIME = 3 days;
// Asset implementation contract address that user decided to stick with.
// 0x0 means that user uses latest version.
mapping(address => address) userOptOutVersion;
/**
* Only asset implementation contract assigned to sender is allowed to call.
*/
modifier onlyAccess(address _sender) {
if (getVersionFor(_sender) == msg.sender) {
_;
}
}
/**
* Returns asset implementation contract address assigned to sender.
*
* @param _sender sender address.
*
* @return asset implementation contract address.
*/
function getVersionFor(address _sender) constant returns(address) {
return userOptOutVersion[_sender] == 0 ? latestVersion : userOptOutVersion[_sender];
}
/**
* Returns current asset implementation contract address.
*
* @return asset implementation contract address.
*/
function getLatestVersion() constant returns(address) {
return latestVersion;
}
/**
* Returns proposed next asset implementation contract address.
*
* @return asset implementation contract address.
*/
function getPendingVersion() constant returns(address) {
return pendingVersion;
}
/**
* Returns upgrade freeze-time start.
*
* @return freeze-time start.
*/
function getPendingVersionTimestamp() constant returns(uint) {
return pendingVersionTimestamp;
}
/**
* Propose next asset implementation contract address.
*
* Can only be called by current asset owner.
*
* Note: freeze-time should not be applied for the initial setup.
*
* @param _newVersion asset implementation contract address.
*
* @return success.
*/
function proposeUpgrade(address _newVersion) onlyAssetOwner() returns(bool) {
// Should not already be in the upgrading process.
if (pendingVersion != 0x0) {
return false;
}
// New version address should be other than 0x0.
if (_newVersion == 0x0) {
return false;
}
// Don't apply freeze-time for the initial setup.
if (latestVersion == 0x0) {
latestVersion = _newVersion;
return true;
}
pendingVersion = _newVersion;
pendingVersionTimestamp = now;
UpgradeProposal(_newVersion);
return true;
}
/**
* Cancel the pending upgrade process.
*
* Can only be called by current asset owner.
*
* @return success.
*/
function purgeUpgrade() onlyAssetOwner() returns(bool) {
if (pendingVersion == 0x0) {
return false;
}
delete pendingVersion;
delete pendingVersionTimestamp;
return true;
}
/**
* Finalize an upgrade process setting new asset implementation contract address.
*
* Can only be called after an upgrade freeze-time.
*
* @return success.
*/
function commitUpgrade() returns(bool) {
if (pendingVersion == 0x0) {
return false;
}
if (pendingVersionTimestamp + UPGRADE_FREEZE_TIME > now) {
return false;
}
latestVersion = pendingVersion;
delete pendingVersion;
delete pendingVersionTimestamp;
return true;
}
/**
* Disagree with proposed upgrade, and stick with current asset implementation
* until further explicit agreement to upgrade.
*
* @return success.
*/
function optOut() returns(bool) {
if (userOptOutVersion[msg.sender] != 0x0) {
return false;
}
userOptOutVersion[msg.sender] = latestVersion;
return true;
}
/**
* Implicitly agree to upgrade to current and future asset implementation upgrades,
* until further explicit disagreement.
*
* @return success.
*/
function optIn() returns(bool) {
delete userOptOutVersion[msg.sender];
return true;
}
}