前回に引き続き、AlisCrowdsale.solを読み進めます。importしているライブラリの解読までが終わっていたので、いよいよAlis独自のオーバーライドしている部分を読んでいきます。
https://github.com/AlisProject/ico-contracts/blob/master/contracts/AlisCrowdsale.sol
まず、本体のAlisCrowdsaleですが、Crowdsale、CappedCrowdsale、RefundableCrowdsale、WhitelistedCrowdsaleがくっついています。WhitelistedCrowdsaleだけまだ前回記事で説明していないので見て見ます。
https://github.com/AlisProject/ico-contracts/blob/master/contracts/WhitelistedCrowdsale.sol
簡単に説明すると、whiteListというmappingに特権ウォレットアドレスを設定している処理です。あとで使うのでしょう。
それでは、独自実装部分をそれぞれ見ていきます。
これは、元々はMintableTokenを発行していますが、独自のAlisTokenを発行するように変更されています。
function createTokenContract() internal returns (MintableToken) {
return new AlisToken();
}
CappedCrowdsaleのvalidPurchaseをオーバーライドしています。元々のロジックは、イーサリアムのキャップを設定されていましたが、ここではAlistTokenの発行上限も確認しているようです。super.validPurchase()も読んでいますので、イーサリアムの上限とAlisトークンの上限を同時にチェックしています。
function validPurchase() internal constant returns (bool) {
bool withinTokenCap = token.totalSupply().add(msg.value.mul(getRate())) <= tokenCap;
return super.validPurchase() && withinTokenCap;
}
こちらもAlisTokenのキャップに達したかどうかを確認していますが、validPurchaseと異なるのが最後のreturnがor条件になっている部分です。ですので、イーサリアムの上限に達したか、Alisトークンの上限に達したかいずれかの場合にクラウドセールは終了します。
function hasEnded() public constant returns (bool) {
bool tokenCapReached = token.totalSupply() >= tokenCap;
return super.hasEnded() || tokenCapReached;
}
次は、finalization()です。クラウドセールを終わらせる処理ですが、3行ほどコメントが書いてあります。
tokenCapからtoken.totalSupplyを引いているので、実際に発行したトークンと設定されているトークン発行リミットの差分、つまり残り(remaining)ということになります。この残りをmintしているので、端数分を発行してしまってキリよくする、という感じでしょうか(もっと深遠な理由があるのかもしれません、ご存知の方いたらご指摘ください)。
その後、トークンの所有権をAlisFundが持っているwalletに移し、ICOのゴールに到達したかどうかを確認します。到達していればそのまま終了、していなければRefundを行う、という形になっています。
// - To store remaining ALIS tokens.
// - To minting unfinished because of our consensus algorithm.
// - https://alisproject.github.io/whitepaper/whitepaper_v1.01.pdf
function finalization() internal {
uint256 remaining = tokenCap.sub(token.totalSupply());
if (remaining > 0) {
token.mint(wallet, remaining);
}
// change AlisToken owner to AlisFund.
token.transferOwnership(wallet);
// From RefundableCrowdsale#finalization
if (goalReached()) {
vault.close();
} else {
vault.enableRefunds();
}
}
トークンの購入部分。プリセール用の分岐が書かれています。
checkLimitはWhitelistedCrowdsale.solに書かれている関数で、どうやらプリセールスのためのアカウントリスト(whitelist)のために購入上限を設定していたようです。
その後、getRate(後述)を使ってイーサリアムをAlisトークンに変換し、その分だけトークンをmintしています。
function buyTokens(address beneficiary) payable {
require(!paused);
require(beneficiary != 0x0);
require(validPurchase());
require(saleAccepting());
uint256 weiAmount = msg.value;
// for presale
if ( isPresale() ) {
checkLimit(weiAmount);
}
// calculate token amount to be created
uint256 tokens = weiAmount.mul(getRate());
// update state
weiRaised = weiRaised.add(weiAmount);
token.mint(beneficiary, tokens);
TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
forwardFunds();
}
getRateはその名の通り、イーサリアムとトークンの変換レートです。
ただのif分ですが、時間に合わせて変換レートを変更しています。この値の変換レートはwhitepaperに書いてある通りです。
function getRate() constant returns (uint256) {
uint256 currentRate = rate;
// We decided using `now` alias of `block.timestamp` instead `block.number`
// Because of same reason:
// - https://github.com/OpenZeppelin/zeppelin-solidity/issues/350
if (isPresale()) {
// before 2017/09/01 02:00 UTC
currentRate = RATE_PRE_SALE;
} else if (now <= icoStartTime.add(1 weeks)) {
// before 2017/09/08 02:00 UTC
currentRate = RATE_WEEK_1;
} else if (now <= icoStartTime.add(2 weeks)) {
// before 2017/09/15 02:00 UTC
currentRate = RATE_WEEK_2;
} else if (now <= icoStartTime.add(3 weeks)) {
// before 2017/09/21 02:00 UTC
currentRate = RATE_WEEK_3;
}
return currentRate;
}
以下二つは条件判定。
function saleAccepting() internal constant returns (bool) {
return !isPresale() || isWhiteListMember(msg.sender);
}
function isPresale() internal constant returns (bool) {
return now <= icoStartTime;
}
ということで、AlisCrowdsaleの独自で実装していう部分を見ていきました。基本的にはopenzeppelin-solidityで定義されている関数をオーバーライドしている感じですね。
次は、AlisFund(マルチシグ)を見ていきます。