๊ฐ๋์ฉ ์ฝ์ธ์ ๊ฐ๋ฐํด์ค ์ ์๋๋ ์์ฒญ์ ๋ฐ๋๋ฐ์, ์ฝ์ธ ๊ฐ๋ฐ์ ์ค์ํ ๊ฒ์ ๊ฐ๋ฐ ๊ทธ ์์ฒด๋ณด๋ค๋ "์ธ์์ ์ด๋ค ๋ถ๋ถ์ ์ด๋ป๊ฒ ๋ณํ์ํฌ ๊ฒ์ด๋ค" ๋ผ๋ ๊ทธ ์ฝ์ธ๋ง์ ์ธ๊ณ๊ด์ธ ๊ฒ ๊ฐ์ต๋๋ค. ์ ๊ฐ์ธ์ ์ธ ์๊ฐ์ผ๋ก๋, ์ฝ์ธ์ ์กด์ฌ๋ฅผ ํตํด ํน์ ์ฐ์ ๊ตฐ์ ๊ตฌ์ฑํ๋ ์ฌ๋๋ค๊ณผ ํ์ฌ๋ค์ ์ฐธ์ฌ๊ฐ ๋ ๋ ค๋๊ณ ๊ทธ๋ก ์ธํด ๋น ๋ฅธ ์๊ฐ ๋ด์ ๊ธฐ์กด์ ์ฐ์ ๊ตฌ์กฐ๊ฐ ์๋กญ๊ฒ ์ฌํธ์ฑ๋ ์ ์๋๋ก ์ค๊ณํ๋ ๊ฒ์ด ์ค์ํ ๊ฒ ๊ฐ์ต๋๋ค. ๋ค์ ๋งํฌ๋ฅผ ํ๋ฒ ์ฝ์ด๋ณด์๋ฉด ๋์์ด ๋์ค ๊ฒ ๊ฐ์ต๋๋ค.
๋ํ ์ด๋๋ฆฌ์ ERC20 ํ ํฐ์ ๊ฒฝ์ฐ์๋ ์ด๋ฌํ ์ธ๊ณ๊ด๊ณผ ๊ทธ์ ๋ฐ๋ฅธ ์ฌ์ ํ๋์ ์ด๊ธฐ์ ์๋ฐํ๊ฒ ์ค๊ณํ๋ ๊ฒ์ด ์์ฃผ ์์ฃผ ์ค์ํฉ๋๋ค. ์๋ํ๋ฉด ํ ํฐ์ ํ๋ฒ ๋ฐํํ๊ณ ๋๋ฉด, ๊ทธ ์์ค๊ฐ ์ด๋๋ฆฌ์ ๋ธ๋ก์ฒด์ธ์ ์๊ตฌ์ ์ผ๋ก ์ฌ๋ผ๊ฐ๋ฉด์ ์ดํ ์์ค๋ฅผ ์ ๋ฐ์ดํธ ํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ธฐ์กด ์์ค๋ฅผ ๊ฐ์ ํ ์ ์๋ ์ ์ผํ ๋ฐฉ๋ฒ์, ์๋ก์ด ์ฃผ์๋ก ์์ค๋ฅผ ๋ฑ๋กํ ํ ์์ ์ ์ฌ์ฉํ๋ ํ ํฐ์ ์๋กญ๊ฒ ๋ง๋ ํ ํฐ์ผ๋ก ์ด์ ์์ผ์ฃผ๋ ๊ฒ์ ๋๋ค. ํ์ง๋ง ์ด๋ฐ ์์ ์ ํ ํฐ์ ์ ๋ขฐ๋๋ฅผ ๋จ์ดํธ๋ฆด ์ ์์ผ๋ฏ๋ก ๊ฐ๊ธ์ ํ๋ฉด ์ ๋๊ฒ ์ฃ ?
๋ฐ๋ผ์ ๋ค์์ ๊ธฐ์ตํ์ธ์.
์ฝ์ธ ๊ฐ๋ฐ ์์ฒด๋ ์ฝ์ต๋๋ค. ์ด ๋ฌธ์๋ง ์ ์์งํ์ ๋ ๋๊ตฌ๋ ์ฝ์ธ์ ๊ฐ๋ฐํ์ค ์ ์์ต๋๋ค. ํ์ง๋ง ์ฝ์ธ์ ์ ๋ง๋ค๊ธฐ(2๋ฒ๊ณผ 3๋ฒ์ ๋์์ ๋ง์กฑ์ํค๊ธฐ)๋ ์ด๋ ค์ด ๊ฒ ๊ฐ์ต๋๋ค. ์์ฉํ์ํค๊ธฐ ์ํด์๋ ๊ฒฝํ ๋ง์ ์กฐ์ธ์๊ฐ ํ์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ฐธ๊ณ ๋ก, ์ ๊ฐ ๊ฐ๋ฐ์ ์ฐธ์ฌํ ์ฝ์ธ์ธ KStarCoin(์ดํ KSC) ์ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ ๋ชฉ์ ์ ๊ฐ์ง๊ณ ๋ง๋ค์์ต๋๋ค.
์ด๋ณด๋ค ๋ ์์ธํ ๋ด์ฉ์ด ๊ถ๊ธํ์ ๋ถ์ KSC ํํ์ด์ง ๋ฐ KSC ๋ฐฑ์ ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์. ๋ํ ์์ค๋ http://์ผ์ด์คํ์ฝ์ธ.์์ค.๋ณด๊ธฐ.cc ์์ ํ์ธํ์ค ์ ์์ต๋๋ค.
์๋ก ์ด ๊ธธ์๋ค์. ์ค๋์ ERC20 ๊ท์ฝ์ ๋ฐ๋ฅธ ํ ํฐ์ ์์ฑ์ํฌ ์๊ฐ์ ๋๋ค. ์ง๋ ์ฐ์ฌ๋ฅผ ๋ชป ๋ณด์ ๋ถ์ ๋ณด๊ณ ๋ค์ ๊ธ์ ํ์ธํ์ธ์.
https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.4.18;
import "./ERC20Basic.sol";
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
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);
}
์ง๋ ์ฐ์ฌ 1ํธ์์ ERC20Basic ์ฝํธ๋ํธ๋ ERC20 ๊ท์ฝ์์ '์ 3์ ์ก๊ธ ๊ธฐ๋ฅ' ์ ๋นผ๋๊ณ ์ ์ธํ๋ค๊ณ ๋ง์๋๋ฆฐ ์ ์์ต๋๋ค. ์ค๋ ์๊ฐํด๋๋ฆฌ๋ ERC20 ์ ๋๋์ด ERC20 ๊ท์ฝ์ ์จ์ ํ ๋ชจ๋ ์ ์ธํ๋ ๊ฐ์ ์ฝํธ๋ํธ ์
๋๋ค. ๊ฐ์ ์ฝํธ๋ํธ๊ฐ ๋ฌด์์ธ์ง ์ ๋ชจ๋ฅด๊ฒ ๋ค๋ฉด ์ฐ์ฌ 1ํธ์์ ํด๋น ๋ถ๋ถ์ ์ฐพ์์ ์ฝ์ด๋ณด์๊ธฐ ๋ฐ๋๋๋ค.
ERC20 ์ฝํธ๋ํธ๋ is ํค์๋๋ฅผ ํตํด ERC20Basic ์ ๋ชจ๋ ๊ฒ์ ์์ ๋ฐ๊ธฐ ๋๋ฌธ์ totalSupply, balanceOf, transfer ํจ์์ Transfer ์ด๋ฒคํธ ํจ์๋ ์ด๋ฏธ ๊ฐ์ง๊ณ ์๋ ์ํ์
๋๋ค. ์ ์์ค์์๋ ERC20 ๊ท์ฝ์ ๋๋จธ์ง ํจ์๋ค allowance, transferFrom, approve ๊ณผ ์ด๋ฒคํธ ํจ์ Approval ์ ์ถ๊ฐ๋ก ์ ์ธํ๊ณ ์๋ค์. ERC20 ๊ท์ฝ์ ๋ฐ๋ฅธ ๊ฐ ํจ์์ ์๋ฏธ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
| ํจ์๋ช | ๋ด์ฉ | ๋ฆฌํด๊ฐ |
|---|---|---|
| approve | spender ์๊ฒ value ๋งํผ์ ํ ํฐ์ ์ธ์ถํ ๊ถ๋ฆฌ๋ฅผ ๋ถ์ฌํ๋ค. ์ด ํจ์๋ฅผ ์ด์ฉํ ๋๋ ๋ฐ๋์ Approval ์ด๋ฒคํธ ํจ์๋ฅผ ํธ์ถํด์ผ ํ๋ค. | ์ฑ๊ณต / ์คํจ |
| allowance | owner ๊ฐ spender ์๊ฒ ์ธ์ถ์ ํ๋ฝํ ํ ํฐ์ ๊ฐ์๋ ๋ช๊ฐ์ธ๊ฐ? | ํ์ฉ๋ ํ ํฐ์ ๊ฐ์ |
| transferFrom | from ์ ๊ณ์ข์์ value ๊ฐ์ ํ ํฐ์ to ์๊ฒ ๋ณด๋ด๋ผ. ๋จ, ์ด ํจ์๋ approve ํจ์๋ฅผ ํตํด ์ธ์ถํ ๊ถ๋ฆฌ๋ฅผ ๋ฐ์ spender ๋ง ์คํํ ์ ์๋ค. | ์ฑ๊ณต / ์คํจ |
ERC20Basic ์ฝํธ๋ํธ์ ๋ง์ฐฌ๊ฐ์ง๋ก ERC20 ์ฝํธ๋ํธ๋ ERC20 ์ธํฐํ์ด์ค ๊ท์ฝ์ ๊ทธ๋๋ก ์ด ๊ฒ ๋ฐ์ ๋์ง ์์ต๋๋ค. ์ฌ๊ธฐ์๋ ๊ตณ์ด ์ดํดํ๋ ค๊ณ ํ์ง ์๊ณ ๋์ด๊ฐ์ ํ ์๋ StandardToken ์ฝํธ๋ํธ ๋ถ๋ถ์ ๋ณด์๋ฉด ๋ฉ๋๋ค.
์ฐธ๊ณ ๋ก solidity ์๋ ๊ฐ์ ์ฝํธ๋ํธ์ ์ญํ ์ด ๋น์ทํ๋ฉด์ ๋ ๋ช
์์ ์ธ ํํ์ธ interface ํค์๋๋ ์กด์ฌํฉ๋๋ค. ํ์ง๋ง interface ํค์๋๋ ๋ค๋ฅธ ์ธํฐํ์ด์ค๋ฅผ ์์ ๋ฐ์ง๋ฅผ ๋ชปํฉ๋๋ค. ์ด๊ฑด ๋๋ฌด๋ ํฐ ๋จ์ ์ด๋ผ ํ์ฌ๋ ๊ตณ์ด interface ํค์๋๋ฅผ ์ฌ์ฉํ ํ์๊ฐ ์ ํ ์์ต๋๋ค. (์ฐธ๊ณ : ๊ณต์ ๋ฌธ์์์ 'Some of these restrictions might be lifted in the future.' ๋ผ๊ณ ํํํ๊ธฐ ๋๋ฌธ์ ์ฐจํ ๊ฐ์ ๋ ๊ฒ ๊ฐ์).
pragma solidity ^0.4.18;
import "./BasicToken.sol";
import "./ERC20.sol";
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
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);
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;
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);
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);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
์ด๋ฒ ์์ค๋ ์กฐ๊ธ ๊ธฐ๋ค์.
์ผ๋จ StandardToken ์ฝํธ๋ํธ๋ ERC20 ์ฝํธ๋ํธ์ BasicToken ์ฝํธ๋ํธ๋ฅผ ์์๋ฐ๊ณ ์์ต๋๋ค. BasicToken ์์ totalSupply, balanceOf, transfer ํจ์๋ฅผ ์ด๋ฏธ ๊ตฌํํ๊ธฐ ๋๋ฌธ์ ํด๋น ๋ด์ฉ์ ๊ตฌํํ์ง ์์๋ ๋ฉ๋๋ค. ERC20 ์์ ์๋กญ๊ฒ ์ ์ธํ allowance, transferFrom, approveํจ์๋ ์์ง ์ ์ธ๋ง ๋ ์ํ์ด๋ฏ๋ก ์ด ๋ถ๋ถ์ ๊ตฌํํ๋ฉด ๋๊ฒ ๊ตฐ์.
์, ๊ทธ๋ผ ์ด์ ์์ค์ ๊ฐ ๋ถ๋ถ์ ๋ถ์ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
mapping (address => mapping (address => uint256)) internal allowed;
allowed ๋ณ์๋ approve ํจ์๋ฅผ ํตํด '๋๊ฐ', '๋๊ตฌ์๊ฒ', '์ผ๋ง์' ์ธ์ถ ๊ถํ์ ์ค์ง๋ฅผ ์ ์ฅํฉ๋๋ค. '๋๊ฐ', '๋๊ตฌ์๊ฒ' ๋ ๋ถ๋ถ์ map ์ key ๋ก ์ฌ์ฉํ๊ณ , '์ผ๋ง' ๋ฅผ value ๋ก ์ ์ฅํฉ๋๋ค. ์๋ฅผ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
allowed[๋๊ฐ][๋๊ตฌ์๊ฒ]= ์ผ๋ง;
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);
Transfer(_from, _to, _value);
return true;
}
require ์ธ ์ค์
_from ์ด ๋์ ๋นผ๋ ค๋ msg.sender ์๊ฒ ํ์ฉํ ๊ถํ ์ด๋ด์ธ์ง๋ฅผ ์ฒดํฌํฉ๋๋ค.์ผ๋ฐ์ ์ผ๋ก msg.sender ๋ ๊ฐ์ค๋น๋ฅผ ์๋ชจํ์ฌ ์ด ํจ์๋ฅผ ํธ์ถํ ๊ณ์ ์
๋๋ค. ์ฆ, _from ์๊ฒ ์ธ์ถ ๊ถํ์ ๋ฐ์ ๊ณ์ ์ด transferFrom ํจ์๋ฅผ ํธ์ถํด์ผ ๋์ํฉ๋๋ค.
_from ์ ๊ณ์ข์์ _value ๋งํผ์ ๋นผ๋ผ(์ธ์ถํ๋ผ)._to ์ ๊ณ์ข์ _value ๋งํผ ๋ํด๋ผ(์
๊ธํ๋ผ).msg.sender ์ ์ธ์ถ ๊ถํ์์ _value ๋งํผ์ ์ ํ๋ผ.Transfer ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํต๋๋ค.
indexed ๋ ๊ฐ์ ์ฐจํ ์ธ์ ๋ ๊ฒ์ ๊ฐ๋ฅํฉ๋๋ค.allowed ๋งต ๋ณ์๋ฅผ ์ ๋ณด๋ฉด _to ๊ฐ์ ์ ์ฅ์ด ๋์ด ์์ง ์์ต๋๋ค. ๋ค์ ๋งํด _from ๊ณ์ข์ ์ฃผ์ธ์ด approve ํจ์์์ spender ์๊ฒ ์ธ์ถ ๊ถํ์ ์ฃผ๋ฉด, ๊ทธ spender ๋ ์ด transferFrom ํจ์๋ฅผ ํธ์ถํ์ฌ _from ๊ณ์ข์์ ๋๊ตฌ์๊ฒ๋ผ๋ ๋์ ๋ณด๋ผ ์ ์์ต๋๋ค.
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
์์ค๋ ์์ฃผ ๊ฐ๋จํฉ๋๋ค.
allowed[msg.sender][_spender] = _value; ํจ์๋ฅผ ํธ์ถํ ๋ณธ์ธ(msg.sender)์ ๊ณ์ข์์ value ๋งํผ ์ธ์ถํด ๊ฐ ์ ์๋ ๊ถ๋ฆฌ๋ฅผ spender ์๊ฒ ๋ถ์ฌํ๋ค.Approval(msg.sender, _spender, _value); ERC20 ๊ท์ฝ์ ๋ฐ๋ผ Approval ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ๋ค. ์ด๋ฒคํธ ํจ์๋ฅผ ํตํด ์ธ๊ฐ์ง ๊ฐ(msg.sender, _spender, _value)๋ ์๊ตฌ์ ์ผ๋ก ๋ธ๋ก์ฒด์ธ์ ๊ธฐ๋ก๋๋ฉฐ indexed ๋ ๊ฐ์ ๊ฒ์๋ ์ ์๋ค.๊ทธ๋ฐ๋ฐ ์ด ๋ถ๋ถ์ ์ฃผ์์ด ์คํ๋ ค ์ค์ํฉ๋๋ค. approve ํจ์ ์์ ๋ค์๊ณผ ๊ฐ์ ์ฃผ์์ด ๋ถ์ด ์์์ต๋๋ค.
* 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
์ ๋ด์ฉ์ธ ์ฆ์จ, ์ด๋๋ฆฌ์์ ๊ฒฝ์ฐ์๋ ์ฑ๊ตด์ด ๋๊ธฐ ์ ๊น์ง๋ ์คํ์ด ๋์ง ์๊ธฐ ๋๋ฌธ์, ๊ฐํน ๋ฆ๊ฒ ์คํํ ์ฝ๋๊ฐ ๋จผ์ ๋์ํ๋ ๊ฒฝ์ฐ๊ฐ ์๊น๋๋ค. ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ๊ฐ ์๊ธธ ์ ์์ต๋๋ค.
์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด A ๋ B ์๊ฒ ์ธ์ถํ ๊ถ๋ฆฌ๋ฅผ N ์์ 0 ์ผ๋ก ๋จผ์ ๋ฐ๊พธ๊ณ , ๋ฌธ์ ์์ด ์ ์์ ์ผ๋ก ๋ณ๊ฒฝ๋ ๊ฒ์ ํ์ธํ ํ ๋ค์ M ์ผ๋ก ๋ณ๊ฒฝํ๋ผ๋ ๋ป์ ๋๋ค.
์ฑ๊ตด ์์์ ์ํด ์ฝ๋์ ์คํ ์์๊ฐ ๋ค๋ฐ๋ ์ ์๋ค๋ ๋ถ๋ถ์ ๊ฐ๊ณผํ๋ฉด, ์ด ์ธ์๋ ๋ค์ํ ๋ณด์์ ํ์ ์ด ์๊ธธ ์ ์์ต๋๋ค. ํญ์ ์ด ๋ถ๋ถ์ ์ผ๋์ ๋์๊ธฐ ๋ฐ๋๋๋ค.
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
_owner ๊ฐ _spender ์๊ฒ ์ผ๋ง๋งํผ์ ์ธ์ถ ๊ถํ์ ๋ถ์ฌํ๋์ง๋ฅผ return ํฉ๋๋ค. public ํจ์์ด๊ธฐ ๋๋ฌธ์ ๋๊ตฌ๋ ๊ณ์ข ์ฃผ์๋ง ์๋ฉด ํ์ธํ ์ ์์ต๋๋ค.
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
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);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
์ด ํจ์๋ค์ ERC20 ๊ท์ฝ์ ์๋ ํจ์๋ ์๋๋๋ค. ์ approve ํจ์์์ ๊ฐ์ ๋ฐ๊ฟ ๋ 0์ผ๋ก ๋ณ๊ฒฝํ ํ ๋ค์ ์ํ๋ ๊ฐ์ผ๋ก ๋ฐ๊พธ๋ ๊ฒ์ ๋ง์ด ๋ถํธํ ์ผ์ด๊ธฐ ๋๋ฌธ์ (์ฐธ๊ณ : 0 ์ผ๋ก ๋ณ๊ฒฝํ๋ ๋ช
๋ น์ด ์ฑ๊ตด๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ ธ๋ค๊ฐ ๋ค์ ์ํ๋ ๊ฐ์ผ๋ก ๋ณ๊ฒฝํ๋๋ก ์คํํด์ผ ํจ), ์ฐจ์ก๋งํผ์ ๋ํ๊ฑฐ๋ ๋นผ๋ ํจ์๋ฅผ ์ถ๊ฐ๋ก ์ ๊ณตํ๊ณ ์์ต๋๋ค. ์ด ํจ์๋ฅผ ์ด์ฉํ๋ฉด N+M ๋งํผ ์ธ์ถํด๊ฐ๋ ๊ณต๊ฒฉ์ ํผํ ์ ์์ต๋๋ค.
์์ค๊ฐ ์ด๋ ต์ง ์๊ณ , ํ์ค ๊ท์ฝ์ด ์๋๊ธฐ ๋๋ฌธ์ ์์ค๋ฅผ ๊ตณ์ด ์ค๋ช ๋๋ฆฌ์ง ์๊ฒ ์ต๋๋ค.
https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/examples/SimpleToken.sol
pragma solidity ^0.4.18;
import "../token/ERC20/StandardToken.sol";
/**
* @title SimpleToken
* @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
* Note they can later distribute these tokens as they wish using `transfer` and other
* `StandardToken` functions.
*/
contract SimpleToken is StandardToken {
string public constant name = "SimpleToken"; // solium-disable-line uppercase
string public constant symbol = "SIM"; // solium-disable-line uppercase
uint8 public constant decimals = 18; // solium-disable-line uppercase
uint256 public constant INITIAL_SUPPLY = 10000 * (10 ** uint256(decimals));
/**
* @dev Constructor that gives msg.sender all of existing tokens.
*/
function SimpleToken() public {
totalSupply_ = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
Transfer(0x0, msg.sender, INITIAL_SUPPLY);
}
}
์์ฃผ ๊ฐ๋จํ ์์ค์ด์ง๋ง ERC20 ํ์ค ๊ท์ฝ์ ๋ฐ๋ฅด๋ ํ ํฐ์ ์๋ฒฝํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค. ์์ค ์ค๋ช
์ ์ฐ์ฌ 2ํธ์์์ MyBasicToken ๊ณผ ๊ฑฐ์ ์ ์ฌํ๋ฏ๋ก ๊ตณ์ด ํ์ง ์๊ฒ ์ต๋๋ค.
๋์ ๋๋ง์ ์ปค์คํ ํ ํฐ์ ๋ง๋ค๊ธฐ ์ํ ๋ฐฉ๋ฒ์ ๊ฐ๋ฅด์ณ๋๋ฆฌ๊ฒ ์ต๋๋ค. ๋๋ฌด๋ ๋๋ฌด๋ ์ฝ์ต๋๋ค. ์ ์์ค์์ ๋ค์ ํ๊ธ ๋ถ๋ถ๋ง ์์ ํ์ฌ ์ฌ์ฉํ์๋ฉด ๋ฉ๋๋ค.
string public constant name = "ํ ํฐ_์ด๋ฆ";
string public constant symbol = "ํ ํฐ_์ฌ๋ณผ"; // solium-disable-line uppercase
uint8 public constant decimals = 18; // solium-disable-line uppercase
uint256 public constant INITIAL_SUPPLY = ํ ํฐ_๋ฐํ๋ * (10 ** uint256(decimals));
decimals ๊ฐ 18 ์ ๊ฐ๊ธ์ ๊ทธ๋ฅ ๋์๋๊ฒ ์ข์ต๋๋ค. ์ด ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ๋ง๋ค๋ ค๋ ํ ํฐ๊ณผ ether ์์ ์ํธ ๊ณ์ฐ์ด ๋ณต์กํด์ง ์ ์์ต๋๋ค.
zeppelin ์ ์์ค ๋์ ์ฐ๋ฆฌ๋ ๋ฌ๋ ์ธ ๋ถ๋ถ๋ง ๋ณ๊ฒฝํ๊ณ ์์ฃผ ์ ๋์ํ๋ ERC20 ํ ํฐ์ ๋ง๋ค ์ ์์์ต๋๋ค! ์ผํธ! ์๋ค์, ๋ ํ ํฐ ๋ง๋ค์์ด! ๋นจ๋ฆฌ ํฌ์ํด!
ํ์ง๋ง ์์ง ์ค์ ์ฌ์ฉํ ํ ํฐ์ผ๋ก๋ ๋ถ์กฑํ ์ ์ด ๋ง์ต๋๋ค. ๋ค์ ์๊ฐ์๋ zeppelin ์ ๋ค๋ฅธ ์์ค๋ค์ ์ด์ฉํ์ฌ ๋ช๊ฐ์ง ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋๋ก ํ๊ฒ ์ต๋๋ค.
TaeKim(@nida-io) ์ ํ๋ก์ ํธ๋ฅผ ๊ตฌ๊ฒฝํ์ธ์.