library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a + b; assert(c >= a); return c; } } contract ERC20Basic { function totalSupply() public view returns (uint256); function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } contract BasicToken is ERC20Basic { using SafeMath for uint256; mapping(address => uint256) balances; uint256 totalSupply_; /** * @dev total number of tokens in existence */ function totalSupply() public view returns (uint256) { return totalSupply_; } /** * @dev transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint256 _value) public returns (bool) { require(_to != address(0)); require(_value <= balances[msg.sender]); balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); emit Transfer(msg.sender, _to, _value); return true; } /** * @dev Gets the balance of the specified address. * @param _owner The address to query the the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address _owner) public view returns (uint256) { return balances[_owner]; } } contract ERC20 is ERC20Basic { function allowance(address owner, address spender) public view returns (uint256); function transferFrom(address from, address to, uint256 value) public returns (bool); function approve(address spender, uint256 value) public returns (bool); event Approval( address indexed owner, address indexed spender, uint256 value ); } contract StandardToken is ERC20, BasicToken { mapping (address => mapping (address => uint256)) internal allowed; /** * @dev Transfer tokens from one address to another * @param _from address The address which you want to send tokens from * @param _to address The address which you want to transfer to * @param _value uint256 the amount of tokens to be transferred */ function transferFrom( address _from, address _to, uint256 _value ) public returns (bool) { require(_to != address(0)); require(_value <= balances[_from]); require(_value <= allowed[_from][msg.sender]); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); emit Transfer(_from, _to, _value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint256 _value) public returns (bool) { allowed[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param _owner address The address which owns the funds. * @param _spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance( address _owner, address _spender ) public view returns (uint256) { return allowed[_owner][_spender]; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * * approve should be called when allowed[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param _spender The address which will spend the funds. * @param _addedValue The amount of tokens to increase the allowance by. */ function increaseApproval( address _spender, uint _addedValue ) public returns (bool) { allowed[msg.sender][_spender] = ( allowed[msg.sender][_spender].add(_addedValue)); emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * * approve should be called when allowed[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param _spender The address which will spend the funds. * @param _subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseApproval( address _spender, uint _subtractedValue ) public returns (bool) { uint oldValue = allowed[msg.sender][_spender]; if (_subtractedValue > oldValue) { allowed[msg.sender][_spender] = 0; } else { allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); } emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } } contract ERC20TokenMock is StandardToken { constructor() public { totalSupply_ = 1000000; balances[msg.sender] = totalSupply_; } } contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } contract TwoKeyTypes { enum CampaignType { NonFungible, Fungible, Counter, None } } contract TwoKeyWhitelisted is Ownable { mapping(address => bool) public whitelist; constructor() Ownable() public { } function isWhitelisted(address _beneficiary) public view returns(bool) { return(whitelist[_beneficiary]); } /** * @dev Adds single address to whitelist. * @param _beneficiary Address to be added to the whitelist */ function addToWhitelist(address _beneficiary) public onlyOwner { whitelist[_beneficiary] = true; } /** * @dev Adds list of addresses to whitelist. Not overloaded due to limitations with truffle testing. * @param _beneficiaries Addresses to be added to the whitelist */ function addManyToWhitelist(address[] _beneficiaries) public onlyOwner { for (uint256 i = 0; i < _beneficiaries.length; i++) { whitelist[_beneficiaries[i]] = true; } } /** * @dev Removes single address from whitelist. * @param _beneficiary Address to be removed to the whitelist */ function removeFromWhitelist(address _beneficiary) public onlyOwner { whitelist[_beneficiary] = false; } } contract TwoKeyEventSource is TwoKeyTypes { event Created(address indexed _campaign, address indexed _owner); event Joined(address indexed _campaign, address indexed _from, address indexed _to); event Escrow(address indexed _campaign, address indexed _escrow, address indexed _sender, uint256 _tokenID, address _childContractID, uint256 _indexOrAmount, CampaignType _type); event Rewarded(address indexed _campaign, address indexed _to, uint256 _amount); event Fulfilled(address indexed _campaign, address indexed _converter, uint256 indexed _tokenID, address _childContractID, uint256 _indexOrAmount, CampaignType _type); event Cancelled(address indexed _escrow, address indexed _converter, uint256 indexed _tokenID, address _childContractID, uint256 _indexOrAmount, CampaignType _type); function created(address _campaign, address _owner) public { emit Created(_campaign, _owner); } function joined(address _campaign, address _from, address _to) public { emit Joined(_campaign, _from, _to); } function escrow(address _campaign, address _escrow, address _sender, uint256 _tokenID, address _childContractID, uint256 _indexOrAmount, CampaignType _type) public { emit Escrow(_campaign, _escrow, _sender, _tokenID, _childContractID, _indexOrAmount, _type); } function rewarded(address _campaign, address _to, uint256 _amount) public { emit Rewarded(_campaign, _to, _amount); } function fulfilled(address _campaign, address _converter, uint256 _tokenID, address _childContractID, uint256 _indexOrAmount, CampaignType _type) public { emit Fulfilled(_campaign, _converter, _tokenID, _childContractID, _indexOrAmount, _type); } function cancelled(address _escrow, address _converter, uint256 _tokenID, address _childContractID, uint256 _indexOrAmount, CampaignType _type) public { emit Cancelled(_escrow, _converter, _tokenID, _childContractID, _indexOrAmount, _type); } } contract ComposableAssetFactory is Ownable, TwoKeyTypes { event Expired(address indexed _contract); using SafeMath for uint256; uint256 private startTime; uint256 private duration; // now is less than duration after start time - so we are still live modifier isOngoing() { require(startTime + duration > now); _; } // now is more than duration after start time - so we are dead modifier isClosed() { require(startTime + duration <= now); _; } /* The contract acts as a store. The children data structure is the catalogue of the store. Each asset is identified by a uint256 tokenID that acts as a SKU (shop keeping unit) This SKU is set by the owner and is unique only with this particular contract mapping(uint256 => ...) maps a tokenID to the asset Such a tokenID identifies one of: 1. ERC20 : which is represented by an entry mapping(address => uint256) which maps the ERC20 contract to the amount tokens we have 2. ERC721: which is represented by an entry mapping(address => uint256) where the address is a hash of the concatenation of the ERC721 contract address and the unique token within that contract, and the uint256 value is 1 or 0 */ // TODO (udi) I think it she mapping(uint256 => address) and not mapping(uint256 => mapping(address => uint256)) // the uint256 is SKU and the address is of ERC20 or ERC721 there is just one address per SKU so we dont need more than one. // You can the balanceOf method (both in ERC20 and ERC721) instead of using the last uint256 or counting how many entries you have // in mapping(address => uint256). From reading the interface of ERC721 it looks like it is not very important to // remeber which NFT you used. If you really want you can do a mapping from SKU to a struct. the first item in the // struct is the address and the second item is a map of all the NFT the campaign owns mapping(uint256 => bool) // but this is not elegant because this mapping is only needed for ERC721 and not for ERC20 so you should really put // it in a subclass. Anyway I dont think we need ETC721 and even if we support ERC721 we dont need to keep track of which tokenID (NFT) // is used. // mapping(uint256 => mapping(address => uint256)) children; // for each asset in store, we hold its capaign type struct Item { uint256 tokenID; address childContractID; CampaignType campaignType; } // list of items in store Item[] inventory; constructor(uint256 _start, uint256 _duration) Ownable() public { startTime = _start; duration = _duration; } // add asset to inventory list function addToInventory(uint256 _tokenID, address _childContract, CampaignType _type) internal { // _tokenID: shop keeping unit, bool found; for(uint256 i = 0; i < inventory.length; i++) { if (inventory[i].tokenID == _tokenID) { found = true; break; } } if (!found) { inventory.push(Item(_tokenID, _childContract, _type)); } } // remove asset from inventory list function removeFromInventory(uint256 _tokenID) internal { for(uint256 i = 0; i < inventory.length; i++) { if (inventory[i].tokenID == _tokenID) { uint256 currentLength = inventory.length; inventory[i] = inventory[currentLength - 1]; delete inventory[currentLength - 1]; inventory.length--; break; } } } // get type of item in inventory function getType(uint256 _tokenID) internal view returns (CampaignType) { for(uint256 i = 0; i < inventory.length; i++) { if (inventory[i].tokenID == _tokenID) { return inventory[i].campaignType; } } return CampaignType.None; } // add erc20 asset amount to the store, which adds an amount of that erc20 to our catalogue function addFungibleChild(uint256 _tokenID, address _childContract, uint256 _amount) public returns (bool) { require( _childContract.call( bytes4(keccak256("transferFrom(address,address,uint256)")), msg.sender, address(this), _amount ) ); // set as child children[_tokenID][_childContract] += _amount; addToInventory(_tokenID, _childContract, CampaignType.Fungible); return true; } // add erc721 asset to the store, which adds a particular unique item from that erc721 to our catalogue function addNonFungibleChild(uint256 _tokenID, address _childContract, uint256 _index) public returns (bool) { require( _childContract.call( bytes4(keccak256("transferFrom(address,address,uint256)")), msg.sender, _index ) ); address childToken = address( keccak256(abi.encodePacked(_childContract, _index)) ); // set as child children[_tokenID][childToken] = 1; addToInventory(_tokenID, _childContract, CampaignType.NonFungible); return true; } // transfer an amount of erc20 from our catalogue to someone function transferFungibleChild( address _to, uint256 _tokenID, address _childContract, uint256 _amount) onlyOwner public returns (bool) { require(children[_tokenID][_childContract] >= _amount); require( _childContract.call( bytes4(keccak256(abi.encodePacked("transfer(address,uint256)"))), _to, _amount ) ); children[_tokenID][_childContract] -= _amount; return true; } // transfer a unique item from a erc721 in our catalogue to someone function transferNonFungibleChild( address _to, uint256 _tokenID, address _childContract, uint256 _childTokenID) public onlyOwner returns (bool) { address childToken = address( keccak256(abi.encodePacked(_childContract, _childTokenID)) ); require(children[_tokenID][childToken] == 1); require( _childContract.call( bytes4(keccak256(abi.encodePacked("transfer(address,uint256)"))), _to, _childTokenID ) ); children[_tokenID][childToken] = 0; return true; } function expire(address _to) onlyOwner isClosed public { for(uint256 i = 0; i < inventory.length; i++) { Item memory item = inventory[i]; if (item.campaignType == CampaignType.Fungible && children[item.tokenID][item.childContractID] == 1) { transferFungibleChild(_to, item.tokenID, item.childContractID, children[item.tokenID][item.childContractID]); } else if (item.campaignType == CampaignType.NonFungible && children[item.tokenID][item.childContractID] > 0) { transferNonFungibleChild(_to, item.tokenID, item.childContractID, children[item.tokenID][item.childContractID]); } } selfdestruct(this); emit Expired(this); } // kill the contract, transfering everyting to the owner // Since the ERC721 and ERC20 store ownership by owner of this contract // that ownership need not be transferred function kill() public onlyOwner { selfdestruct(owner); } } contract TwoKeyEscrow is ComposableAssetFactory { using SafeMath for uint256; // emit through it events for backend to listen to TwoKeyEventSource eventSource; address internal contractor; address internal moderator; address internal buyer; /* * whitelist of converters. * to actually conclude the escorw positively, * the converter is to be approved first through the moderator */ TwoKeyWhitelisted whitelistConverter; // is the converter eligible for participation in conversion modifier isWhiteListedConverter() { require(whitelistConverter.isWhitelisted(buyer)); _; } modifier onlyContractorOrModerator() { require(msg.sender == contractor || msg.sender == moderator); _; } constructor( TwoKeyEventSource _eventSource, address _contractor, address _moderator, address _buyer, uint256 _start, uint256 _duration, TwoKeyWhitelisted _whitelistConverter ) ComposableAssetFactory(_start, _duration) public { eventSource = _eventSource; contractor = _contractor; moderator = _moderator; buyer = _buyer; whitelistConverter = _whitelistConverter; } /** * transferNonFungibleChildTwoKeyToken * @param _tokenID sku of asset * @param _childContract erc721 representing the asset class * @param _childTokenID unique index of asset * * transfer the asset to the buyer, */ function transferNonFungibleChildTwoKeyToken( uint256 _tokenID, address _childContract, uint256 _childTokenID) isWhiteListedConverter onlyContractorOrModerator public { require(super.transferNonFungibleChild(buyer, _tokenID, _childContract, _childTokenID)); } /** * transferFungibleChildTwoKeyToken * @param _tokenID sku of asset * @param _childContract erc20 representing the asset class * @param _amount amount of asset bought * * transfer the asset to the buyer, */ function transferFungibleChildTwoKeyToken( uint256 _tokenID, address _childContract, uint256 _amount) isWhiteListedConverter onlyContractorOrModerator public { require(super.transferFungibleChild(buyer, _tokenID, _childContract, _amount)); } /** * cancelNonFungibleChildTwoKey * cancels the purchase buy transfering the assets back to the campaign * and refunding the buyer * @param _tokenID sku of asset * @param _childContract erc721 representing the asset class * @param _childTokenID unique index of asset * */ function cancelNonFungibleChildTwoKey( uint256 _tokenID, address _childContract, uint256 _childTokenID) onlyContractorOrModerator public { super.transferNonFungibleChild(owner, _tokenID, _childContract, _childTokenID); eventSource.cancelled(address(this), buyer, _tokenID, _childContract, _childTokenID, CampaignType.NonFungible); } /** * cancelFungibleChildTwoKey * cancels the purchase buy transfering the assets back to the campaign * and refunding the buyer * @param _tokenID sku of asset * @param _childContract erc20 representing the asset class * @param _amount amount of asset bought * */ function cancelFungibleChildTwoKey( uint256 _tokenID, address _childContract, uint256 _amount) onlyContractorOrModerator public { super.transferFungibleChild(owner, _tokenID, _childContract, _amount); eventSource.cancelled(address(this), buyer, _tokenID, _childContract, _amount, CampaignType.Fungible); } }
0.4.24