概要
ERC20トークンはethereum上で利用できるトークンの標準規格。これに準拠しているトークンは Ethereumウォレット、および同じ基準を使用する他のクライアントまたはコントラクトと互換性がある。今回は標準的なトークンの機能を実装したTokenERC20コントラクトとトークンの中央管理者を設定するowendコントラクトを作成し、それらを継承したより多くの機能を持つトークンを実装する。
owenedコントラクト
トークンの中央管理者を設定する。modifier onlyOwnerによって関数の実行可能者をownerに限定することができる。
ERC20Token.sol
contract owned {
address public owner;
//コンストラクタ、 ownerを設定
function owned() public {
owner = msg.sender;
}
//ownerのみ実行できる
modifier onlyOwner {
require(msg.sender == owner);
_;
}
// ownerの交代
function transferOwnership(address newOwner) onlyOwner public {
owner = newOwner;
}
}
TokenERC20コントラクト
ERC20Token.sol
contract TokenERC20 {
// トークンの名前、シンボル、供給量
string public name;
string public symbol;
//小数点以下何桁までか、18から変えないほうがいい
uint8 public decimals = 18;
uint256 public totalSupply;
//publicはブロックチェーン上の誰でもアクセスできることを示す
//アカウントのアドレスと残高
mapping (address => uint256) public balanceOf;
//アカウント毎にそれぞれのアカウントが自由に使用可能なトークン量を定める
mapping (address => mapping (address => uint256)) public allowance;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
//コンストラクタ
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) public {
totalSupply = initialSupply * 10 ** uint256(decimals);
balanceOf[msg.sender] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
//トークンの転送を行う、internalは内部関数を示しコントラクト内からしかアクセスできなくする
function _transfer(address _from, address _to, uint _value) internal {
//inputの検証
require(_to != 0x0);
require(balanceOf[_from] >= _value);
require(balanceOf[_to] + _value > balanceOf[_to]);
uint previousBalances = balanceOf[_from] + balanceOf[_to];
//トークンの移動
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
//transferが行われたことを知らせる
Transfer(_from, _to, _value);
// 転送が成功したかチェック
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
//トランザクションを送ったアカウントからトークンを転送
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
//トランザクションを送ったアカウントがほかのアカウントのトークンを転送
function transferFrom(address _from,adress _to,uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]);
allowance[_from][msg.sender] -= _value;
_transfer(_from,_to,_value);
}
//他のアカウントに許可するトークンの使用量を設定
function approve(address _spender,uint256 _value) public returns(bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
}
トークン例
//継承、owened,TokenERC20にアクセス可能になる
contract MyAdvancedToken is owned, TokenERC20 {
//売値買値
uint256 public sellPrice;
uint256 public buyPrice;
//アカウントがトランザクションを送るために最低限維持すべきether
uint minBalanceForAccounts;
//アカウントの凍結の有無
mapping (address => bool) public frozenAccount;
event FrozenFunds(address target, bool frozen);
//ownerのみ実行可能、1finney=0.001ether
function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
}
//コンストラクタ
function MyAdvancedToken(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) TokenERC20(initialSupply, tokenName, tokenSymbol) public {}
//トークンの転送を行う内部関数
function _transfer(address _from, address _to, uint _value) internal {
require (_to != 0x0);
require (balanceOf[_from] >= _value);
require (balanceOf[_to] + _value > balanceOf[_to]);
//アカウントが凍結されていないか確認
require(!frozenAccount[_from]);
require(!frozenAccount[_to]);
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
Transfer(_from, _to, _value);
}
function transfer(address _to, uint256 _value) {
if(msg.sender.balance < minBalanceForAccounts){
//minBalanceForAccountsに足りない分トークンを売る
sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
}
_transfer(msg.sender, _to, _value);
}
//ownerのみ実行可能、トークンの供給量の変更
function mintToken(address target, uint256 mintedAmount) onlyOwner public {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
Transfer(0, this, mintedAmount);
Transfer(this, target, mintedAmount);
}
//ownerのみ実行可能、アカウントの凍結
function freezeAccount(address target, bool freeze) onlyOwner public {
frozenAccount[target] = freeze;
FrozenFunds(target, freeze);
}
//ownerのみ実行可能、売値と買値を設定、Gasを消費するため余り頻繁には変えたくない
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
//トークンをetherで買う、payableを指定してetherを受け入れ可能にする
function buy() payable public {
uint amount = msg.value / buyPrice;
_transfer(this, msg.sender, amount);
}
//トークンを売る
function sell(uint256 amount) public {
//コントラクトが十分なetherを持っているか
require(this.balance >= amount * sellPrice);
_transfer(msg.sender, this, amount);
msg.sender.transfer(amount * sellPrice);
}
//マイニングの報酬に1トークンを与える
function giveBlockReward() {
balanceOf[block.coinbase] += 1;
}
uint currentChallenge = 1;
//立方根を正しく求められたアカウントに1トークン与える
function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) {
require(answerToCurrentReward**3 == currentChallenge);
balanceOf[msg.sender] += 1;
//次の問題を設定
currentChallenge = nextChallenge;
}
}