Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/EllipseMarketMakerLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ contract EllipseMarketMakerLib is TokenOwnable, IEllipseMarketMaker {
}

/// @dev returns true iff the contract is open for public trade.
function isOpenForPublic() public onlyOwner returns (bool) {
function isOpenForPublic() public constant returns (bool) {
return (openForPublic && operational);
}

Expand Down
117 changes: 47 additions & 70 deletions contracts/IssueanceFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ import './Standard223Receiver.sol';

/// @title Colu Issueance factoy with CLN for CC tokens.
/// @author Rotem Lev.
contract IssueanceFactory is CurrencyFactory{
contract IssueanceFactory is Standard223Receiver, TokenHolder {
using SafeMath for uint256;

uint256 public precision;
CurrencyFactory public currencyFactory;

// address of the deployed CLN contract (ERC20 Token)
address public clnAddress;
// address of the deployed elipse market maker contract
address public mmLibAddress;

struct IssueanceStruct {
uint256 hardcap;
Expand All @@ -27,6 +33,7 @@ contract IssueanceFactory is CurrencyFactory{
uint256 endTime;
uint256 targetPrice;
uint256 clnRaised;
address owner;
}

uint256 public totalCLNcustodian;
Expand All @@ -36,11 +43,19 @@ contract IssueanceFactory is CurrencyFactory{
// total supply of CLN
uint256 public CLNTotalSupply;

address[] public tokens;

event CLNRaised(address indexed token, address indexed participant, uint256 amount);
event CLNRefunded(address indexed token, address indexed participant, uint256 amount);

event SaleFinalized(address indexed token, uint256 clnRaised);

// modifier to check if called by issuer of the token
modifier tokenIssuerOnly(address token, address owner) {
require(issueMap[token].owner == owner);
_;
}

// sale has begun based on time and status
modifier saleOpen(address _token) {
require((now >= issueMap[_token].startTime && issueMap[_token].endTime >= now));
Expand All @@ -65,15 +80,17 @@ contract IssueanceFactory is CurrencyFactory{
}
// checks if the instance of market maker contract is open for public
modifier marketClosed(address _token) {
require(!MarketMaker(currencyMap[_token].mmAddress).isOpenForPublic());
require(!MarketMaker(currencyFactory.getMarketMakerAddressFromToken(_token)).isOpenForPublic());
_;
}
/// @dev constructor
/// @param _mmLib address for the deployed elipse market maker contract
/// @param _clnAddress address for the deployed CLN ERC20 token
function IssueanceFactory(address _mmLib, address _clnAddress) public CurrencyFactory(_mmLib, _clnAddress) {
CLNTotalSupply = ERC20(_clnAddress).totalSupply();
precision = IEllipseMarketMaker(_mmLib).PRECISION();
/// @param _currencyFactory address for the deployed CLN ERC20 token
function IssueanceFactory(address _currencyFactory) public {
currencyFactory = CurrencyFactory(_currencyFactory);
clnAddress = currencyFactory.clnAddress();
CLNTotalSupply = ERC20(clnAddress).totalSupply();
mmLibAddress = currencyFactory.mmLibAddress();
precision = IEllipseMarketMaker(mmLibAddress).PRECISION();
}

function createIssueance( uint256 _startTime,
Expand All @@ -90,9 +107,9 @@ contract IssueanceFactory is CurrencyFactory{
uint256 R2 = IEllipseMarketMaker(mmLibAddress).calcReserve(_reserveAmount, CLNTotalSupply, _totalSupply);
uint256 targetPrice = IEllipseMarketMaker(mmLibAddress).getPrice(_reserveAmount, R2, CLNTotalSupply, _totalSupply);
require(isValidIssueance(_hardcap, targetPrice, _totalSupply, R2));
address tokenAddress = super.createCurrency( _name, _symbol, _decimals, _totalSupply);
address tokenAddress = currencyFactory.createCurrency( _name, _symbol, _decimals, _totalSupply);
addToMap(tokenAddress, _startTime, _startTime + _durationTime, _hardcap, _reserveAmount, targetPrice);

tokens.push(tokenAddress);
return tokenAddress;
}

Expand All @@ -114,7 +131,8 @@ contract IssueanceFactory is CurrencyFactory{
startTime: _startTime,
endTime: _endTime,
clnRaised: 0,
targetPrice: _targetPrice});
targetPrice: _targetPrice,
owner: msg.sender});
}

/// @dev particiapte in the CLN based issuance
Expand All @@ -126,15 +144,14 @@ contract IssueanceFactory is CurrencyFactory{
returns (uint256 releaseAmount) {
require(_clnAmount > 0);

address marketMakerAddress = getMarketMakerAddressFromToken(_token);
// how much do we need to actually send to mm of the incomming amount
// and how much of the amount can participate
uint256 transferToReserveAmount;
uint256 participationAmount;
(transferToReserveAmount, participationAmount) = getParticipationAmounts(_clnAmount, _token);
// send what we need to the market maker for reserve
require(ERC20(clnAddress).transferFrom(msg.sender, this, participationAmount));
approveAndChange(clnAddress, _token, transferToReserveAmount, marketMakerAddress);
require(approveAndChange(clnAddress, _token, transferToReserveAmount) > 0);
// pay back to participant with the participated amount * price
releaseAmount = participationAmount.mul(issueMap[_token].targetPrice).div(precision);
issueMap[_token].clnRaised = issueMap[_token].clnRaised.add(participationAmount);
Expand All @@ -157,9 +174,8 @@ contract IssueanceFactory is CurrencyFactory{
uint256 transferToReserveAmount;
uint256 participationAmount;
(transferToReserveAmount, participationAmount) = getParticipationAmounts(tkn.value, _token);
address marketMakerAddress = getMarketMakerAddressFromToken(_token);

approveAndChange(clnAddress, _token, transferToReserveAmount, marketMakerAddress);
require(approveAndChange(clnAddress, _token, transferToReserveAmount) > 0);
// transfer only what we need
releaseAmount = participationAmount.mul(issueMap[_token].targetPrice).div(precision);
issueMap[_token].clnRaised = issueMap[_token].clnRaised.add(participationAmount);
Expand All @@ -181,12 +197,11 @@ contract IssueanceFactory is CurrencyFactory{
marketClosed(_token)
returns (bool) {
// move all CC and CLN that were raised and not in the reserves to the issuer
address marketMakerAddress = getMarketMakerAddressFromToken(_token);
uint256 clnAmount = issueMap[_token].clnRaised.sub(issueMap[_token].reserve);
totalCLNcustodian = totalCLNcustodian.sub(clnAmount);
uint256 ccAmount = ERC20(_token).balanceOf(this);
// open Market Maker for public trade.
require(MarketMaker(marketMakerAddress).openForPublicTrade());
require(currencyFactory.openMarket(_token));

require(ERC20(_token).transfer(msg.sender, ccAmount));
require(ERC20(clnAddress).transfer(msg.sender, clnAmount));
Expand All @@ -204,11 +219,9 @@ contract IssueanceFactory is CurrencyFactory{
marketClosed(_token)
returns (bool) {
//if we have CC time to thorw it to the Market Maker
address marketMakerAddress = getMarketMakerAddressFromToken(_token);
require(ERC20(_token).transferFrom(msg.sender, this, _ccAmount));
uint256 factoryCCAmount = ERC20(_token).balanceOf(this);
require(ERC20(_token).approve(marketMakerAddress, factoryCCAmount));
require(MarketMaker(marketMakerAddress).change(_token, factoryCCAmount, clnAddress) > 0);
require(approveAndChange(_token, clnAddress, factoryCCAmount) > 0);

uint256 returnAmount = _ccAmount.mul(precision).div(issueMap[_token].targetPrice);
issueMap[_token].clnRaised = issueMap[_token].clnRaised.sub(returnAmount);
Expand All @@ -227,10 +240,8 @@ contract IssueanceFactory is CurrencyFactory{
marketClosed(msg.sender)
returns (bool) {
//if we have CC time to thorw it to the Market Maker
address marketMakerAddress = getMarketMakerAddressFromToken(msg.sender);
uint256 factoryCCAmount = ERC20(msg.sender).balanceOf(this);
require(ERC20(msg.sender).approve(marketMakerAddress, factoryCCAmount));
require(MarketMaker(marketMakerAddress).change(msg.sender, factoryCCAmount, clnAddress) > 0);
require(approveAndChange(msg.sender, clnAddress, factoryCCAmount) > 0);

uint256 returnAmount = tkn.value.mul(precision).div(issueMap[msg.sender].targetPrice);
issueMap[msg.sender].clnRaised = issueMap[msg.sender].clnRaised.sub(returnAmount);
Expand All @@ -241,41 +252,6 @@ contract IssueanceFactory is CurrencyFactory{

}

/// @dev normal send cln to the market maker contract, sender must approve() before calling method. can only be called by owner
/// @dev sending CLN will return CC from the reserve to the sender.
function insertCLNtoMarketMaker(address, uint256) public returns (uint256) {
require(false);
return 0;
}

/// @dev ERC223 transferAndCall, send cln to the market maker contract can only be called by owner (see MarketMaker)
/// @dev sending CLN will return CC from the reserve to the sender.
function insertCLNtoMarketMaker(address) public tokenPayable returns (uint256) {
require(false);
return 0;
}

/// @dev normal send cc to the market maker contract, sender must approve() before calling method. can only be called by owner
/// @dev sending CC will return CLN from the reserve to the sender.
function extractCLNfromMarketMaker(address, uint256) public returns (uint256) {
require(false);
return 0;
}

/// @dev ERC223 transferAndCall, send cc to the market maker contract can only be called by owner (see MarketMaker)
/// @dev sending CC will return CLN from the reserve to the sender.
function extractCLNfromMarketMaker() public tokenPayable returns (uint256) {
require(false);
return 0;
}

/// @dev opens the Market Maker to recvice transactions from all sources.
/// @dev Request to transfer ownership of Market Maker contract to Owner instead of factory.
function openMarket(address) public returns (bool) {
require(false);
return false;
}

/// @dev checks if the parameters that were sent to the create are valid for a promised price and buyback
/// @param _hardcap uint256 CLN hardcap for issuance
/// @param _price uint256 computed through the market maker using the supplies and reserves
Expand All @@ -289,26 +265,21 @@ contract IssueanceFactory is CurrencyFactory{
return (_S2 > _R2 && _S2.sub(_R2).mul(precision) >= _hardcap.mul(_price));
}


/// @dev helper function to fetch market maker contract address deploed with the CC
/// @param _token address token address for this issuance (same as CC adress)
function getMarketMakerAddressFromToken(address _token) public constant returns (address) {
return currencyMap[_token].mmAddress;
}

/// @dev helper function to approve tokens for market maker and then chane tokens
/// @param _token address deployed ERC20 token address to spend
/// @param _token2 address deployed ERC20 token address to buy
/// @param _amount uint256 amount of _token to spend
/// @param _marketMakerAddress address for the deploed market maker with this CC
function approveAndChange(address _token,
address _token2,
uint256 _amount,
address _marketMakerAddress) private
uint256 _amount) private
returns (uint256) {
if(_amount > 0) {
require(ERC20(_token).approve(_marketMakerAddress, _amount));
return MarketMaker(_marketMakerAddress).change(_token, _amount, _token2);
require(ERC20(_token).approve(currencyFactory, _amount));
if (_token == clnAddress) {
return currencyFactory.insertCLNtoMarketMaker(_token2, _amount);
} else {
return currencyFactory.extractCLNfromMarketMaker(_token, _amount);
}
}
return 0;
}
Expand Down Expand Up @@ -394,9 +365,15 @@ contract IssueanceFactory is CurrencyFactory{
}

if (issueMap[_tokenAddress].hardcap > 0) {
require(MarketMaker(currencyMap[_tokenAddress].mmAddress).isOpenForPublic());
require(MarketMaker(currencyFactory.getMarketMakerAddressFromToken(_tokenAddress)).isOpenForPublic());
}

return ERC20(_tokenAddress).transfer(owner, _amount);
}

/// @dev implementation for standard 223 reciver.
/// @param _token address of the token used with transferAndCall.
function supportsToken(address _token) public constant returns (bool) {
return (clnAddress == _token || issueMap[_token].hardcap > 0);
}
}
2 changes: 1 addition & 1 deletion contracts/MarketMaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ contract MarketMaker is ERC223Receiver {
function change(address _toToken, uint _minReturn) public returns (uint _returnAmount);
function quote(address _fromToken, uint _amount, address _toToken) public constant returns (uint _returnAmount);
function openForPublicTrade() public returns (bool success);
function isOpenForPublic() public returns (bool success);
function isOpenForPublic() public constant returns (bool success);

event Change(address indexed fromToken, uint inAmount, address indexed toToken, uint returnAmount, address indexed account);
}
22 changes: 14 additions & 8 deletions test/IssueanceFactory.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const expectRevert = require('./helpers/expectRevert');
const eventHelper = require('./helpers/eventHelper');
const coder = require('web3-eth-abi');
const expect = require('chai').expect;
const BigNumber = require('bignumber.js');
Expand All @@ -10,6 +11,7 @@ const EllipseMarketMaker = artifacts.require('EllipseMarketMaker');
const IssueanceFactory = artifacts.require('IssueanceFactory');
const ColuLocalCurrency = artifacts.require('ColuLocalCurrency');
const EllipseMarketMakerLib = artifacts.require('EllipseMarketMakerLib');
const CurrencyFactory = artifacts.require('CurrencyFactory');

const TOKEN_DECIMALS = 10 ** 18;

Expand Down Expand Up @@ -71,6 +73,7 @@ contract('IssueanceFactory', (accounts) => {

let amount = 50 * TOKEN_DECIMALS;
let tokenAddress;
let currencyFactory;

before(async () => {
mmlib = await EllipseMarketMakerLib.new();
Expand All @@ -80,14 +83,15 @@ contract('IssueanceFactory', (accounts) => {
cln = await ColuLocalNetwork.new(CLN_MAX_TOKENS);
await cln.makeTokensTransferable();
await cln.transfer(accounts[1], THOUSAND_CLN * 1000);
currencyFactory = await CurrencyFactory.new(mmlib.address, cln.address);
// await cln.transfer(accounts[2], THOUSAND_CLN * 1000);
// await cln.transfer(accounts[3], THOUSAND_CLN * 1000);
});


describe ('Issue through CLN', async () => {
beforeEach(async () => {
Factory = await IssueanceFactory.new(mmlib.address, cln.address, {from: accounts[0]} )
Factory = await IssueanceFactory.new(currencyFactory.address, {from: accounts[0]} )
});

it('should not be able to create without name', async () => {
Expand All @@ -114,9 +118,10 @@ contract('IssueanceFactory', (accounts) => {

it('should be able to create with correct parameters', async () => {
now = await (web3.eth.getBlock(web3.eth.blockNumber)).timestamp;
let result = await Factory.createIssueance(now + 10, 1000000, THOUSAND_CLN, THOUSAND_CLN / 2, 'Some Name', 'SON', 18, CC_MAX_TOKENS, {from: accounts[0]});
assert.lengthOf(result.logs, 1);
let event = result.logs[0];
await Factory.createIssueance(now + 10, 1000000, THOUSAND_CLN, THOUSAND_CLN / 2, 'Some Name', 'SON', 18, CC_MAX_TOKENS, {from: accounts[0]});
let logs = await eventHelper.assertEvent(currencyFactory, {event: 'TokenCreated'});
assert.lengthOf(logs, 1);
let event = logs[0];
assert.equal(event.event, 'TokenCreated');
tokenAddress = event.args.token
assert(expect(tokenAddress).to.be.a('String'));
Expand All @@ -129,12 +134,13 @@ contract('IssueanceFactory', (accounts) => {
let now
beforeEach(async () => {
now = await (web3.eth.getBlock(web3.eth.blockNumber)).timestamp;
Factory = await IssueanceFactory.new(mmlib.address, cln.address, {from: accounts[0]} );
Factory = await IssueanceFactory.new(currencyFactory.address, {from: accounts[0]} );
let clnAddress = await Factory.clnAddress();
assert.equal(clnAddress ,cln.address);
let result = await Factory.createIssueance(now + 10, 1000000, THOUSAND_CLN, THOUSAND_CLN / 2, 'Some Name', 'SON', 18, CC_MAX_TOKENS, {from: accounts[0]});
assert.lengthOf(result.logs, 1);
let event = result.logs[0];
await Factory.createIssueance(now + 10, 1000000, THOUSAND_CLN, THOUSAND_CLN / 2, 'Some Name', 'SON', 18, CC_MAX_TOKENS, {from: accounts[0]});
let logs = await eventHelper.assertEvent(currencyFactory, {event: 'TokenCreated'});
assert.lengthOf(logs, 1);
let event = logs[0];
assert.equal(event.event, 'TokenCreated');
tokenAddress = event.args.token
assert(expect(tokenAddress).to.be.a('String'));
Expand Down
19 changes: 19 additions & 0 deletions test/helpers/eventHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
let _ = require("lodash");

module.exports = {
assertEvent: function(contract, filter) {
return new Promise((resolve, reject) => {
let event = contract[filter.event]();
event.watch();
event.get((error, logs) => {
let log = _.filter(logs, filter);
if (log) {
resolve(log);
} else {
throw Error("Failed to find filtered event for " + filter.event);
}
});
event.stopWatching();
});
}
}