Decentralization

Solidity로 기본적인 토큰 판매 스마트 컨트랙트 작성

이영훈닷컴 2025. 3. 15. 19:54
728x90

오늘은 Solidity를 사용하여 토큰 판매를 위한 기본적인 스마트 컨트랙트를 작성하는 방법에 대해 배웠습니다. 이 컨트랙트는 사용자가 이더리움을 지불하고 토큰을 구매할 수 있도록 하고, 소유자는 자신이 받은 수익을 출금할 수 있는 기능을 포함합니다. 또한, 환율을 설정하고 추가 토큰을 발행하는 기능도 구현되어 있습니다.

pragma solidity >=0.4.22 <0.6.0; // Solidity 버전 설정 (0.4.22 이상, 0.6.0 미만)

contract SellToken { 

    /* 계좌별 잔액을 저장하는 매핑 (address -> 잔액) */
    mapping (address => uint256) public balanceOf;

    address payable public owner; // 스마트 컨트랙트 소유자 주소
    uint public rate; // 토큰 환율 (1 ETH 당 몇 개의 토큰을 받을 수 있는지)
    uint public totalSupply; // 총 토큰 공급량
    uint public income; // ETH로 받은 수익

    /* 초기 공급량을 설정하는 생성자 */
    constructor (uint256 initialSupply) public {
        owner = msg.sender; // 컨트랙트를 배포한 주소를 소유자로 설정
        balanceOf[owner] = initialSupply; // 소유자에게 초기 공급량을 할당
        rate = 1; // 기본 환율 설정 (1 ETH = 1 토큰)
        totalSupply = initialSupply; // 총 공급량 설정
        income = 0; // 초기 수익을 0으로 설정
    }

    /* 토큰을 전송하는 함수 */
    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] > _value); // 송금자가 충분한 잔액을 가지고 있는지 확인
        require(balanceOf[_to] + _value > balanceOf[_to]); // 오버플로우 방지
        balanceOf[msg.sender] -= _value; // 송금자의 잔액 감소
        balanceOf[_to] += _value; // 수신자의 잔액 증가
        return true; // 전송 성공
    }

    /* 내부적으로 토큰을 판매하는 함수 (외부에서 직접 호출 불가) */
    function sell(address _from, address _to, uint _value) private {
        require(balanceOf[_from] > _value); // 판매자가 충분한 토큰을 보유하고 있는지 확인
        require(balanceOf[_to] + _value > balanceOf[_to]); // 오버플로우 방지
        balanceOf[_from] -= _value; // 판매자의 토큰 감소
        balanceOf[_to] += _value; // 구매자의 토큰 증가
    }

    /* 사용자가 이더리움을 지불하고 토큰을 구매하는 함수 */
    function buy () public payable {
        uint pay = msg.value * rate / (10**18); // 지불한 이더리움을 토큰으로 환산
        sell(owner, msg.sender, pay); // 소유자로부터 구매자에게 토큰 전송
        income = income + pay; // 총 수익 증가
    }

    /* 컨트랙트의 수익을 소유자의 계좌로 출금하는 함수 */
    function withdrawal() public isOwner {
        uint out = income * (10**18); // 수익을 이더리움 단위로 변환
        owner.transfer(out); // 소유자에게 출금
        income = 0; // 수익 초기화
    }

    /* 토큰 환율을 변경하는 함수 (소유자만 호출 가능) */
    function setRate(uint _rate) public isOwner {
        rate = _rate;
    }

    /* 추가 발행: 소유자가 새로운 토큰을 발행하는 함수 */
    function additionalIssue(uint _amount) public isOwner {
        balanceOf[owner] += _amount; // 소유자의 잔액 증가
        totalSupply += _amount; // 총 공급량 증가
    }

    /* 소유자만 실행할 수 있도록 제한하는 수정자 */
    modifier isOwner() {
        require(msg.sender == owner); // 호출자가 소유자인지 확인
        _; // 조건이 충족되면 함수 실행
    }
}

스마트 컨트랙트 개요

이번에 작성한 스마트 컨트랙트는 SellToken이라는 이름을 가지고 있습니다. 이 컨트랙트는 다음과 같은 기능을 제공합니다:

  • 사용자는 이더리움을 지불하고 토큰을 구매할 수 있습니다.
  • 소유자는 토큰의 환율을 설정하고, 수익을 출금하며, 추가로 토큰을 발행할 수 있습니다.
  • 사용자는 다른 주소로 토큰을 전송할 수 있습니다.

코드 분석

1. 변수와 상태 정의

mapping (address => uint256) public balanceOf;
address payable public owner;
uint public rate;
uint public totalSupply;
uint public income;
  • balanceOf: 각 주소가 보유한 토큰의 잔액을 저장하는 매핑입니다.
  • owner: 스마트 컨트랙트의 소유자 주소입니다. 이 주소는 여러 기능(환율 설정, 수익 출금 등)을 관리합니다.
  • rate: 토큰과 이더리움 간의 환율을 정의합니다. 예를 들어, 1 ETH당 몇 개의 토큰을 받을 수 있는지 설정합니다.
  • totalSupply: 현재 발행된 총 토큰 수입니다.
  • income: 소유자가 받은 이더리움 수익을 추적하는 변수입니다.

2. 생성자 함수

constructor (uint256 initialSupply) public {
    owner = msg.sender;
    balanceOf[owner] = initialSupply;
    rate = 1;
    totalSupply = initialSupply;
    income = 0;
}
  • 생성자는 스마트 컨트랙트가 배포될 때 한 번만 실행됩니다. 여기서는 소유자에게 초기 토큰 수(initialSupply)를 할당하고, 환율을 기본값인 1로 설정합니다.
  • msg.sender는 이 스마트 컨트랙트를 배포한 주소로, 이 주소가 owner로 설정됩니다.

3. transfer 함수 (토큰 전송)

function transfer(address _to, uint256 _value) public returns (bool success) {
    require(balanceOf[msg.sender] > _value);
    require(balanceOf[_to] + _value > balanceOf[_to]);
    balanceOf[msg.sender] -= _value;
    balanceOf[_to] += _value;
    return true;
}
  • 사용자가 다른 주소로 토큰을 전송할 수 있도록 하는 함수입니다.
  • 전송하려는 주소(_to)와 전송하려는 금액(_value)을 확인하고, 유효한 경우 잔액을 업데이트합니다.
  • require를 사용하여 예외 상황을 처리하고, 전송이 성공적으로 완료되면 true를 반환합니다.

4. sell 함수 (토큰 판매)

function sell(address _from, address _to, uint _value) private {
    require(balanceOf[_from] > _value);
    require(balanceOf[_to] + _value > balanceOf[_to]);
    balanceOf[_from] -= _value;
    balanceOf[_to] += _value;
}
  • 이 함수는 토큰을 판매하는 기능을 처리하는 내부 함수입니다. 외부에서 호출할 수 없습니다.
  • 판매자는 충분한 잔액을 가지고 있어야 하며, 구매자는 해당 금액을 받을 수 있는 충분한 잔액을 가질 수 있어야 합니다.

5. buy 함수 (토큰 구매)

function buy () public payable {
    uint pay = msg.value * rate / (10**18);
    sell(owner, msg.sender, pay);
    income = income + pay;
}
  • 사용자가 이더리움을 보내면서 토큰을 구매하는 함수입니다.
  • msg.value는 전송된 이더리움의 양입니다. 이를 환율(rate)에 따라 토큰 수로 변환하고, 이를 소유자로부터 구매자에게 전송합니다.
  • 구매한 토큰 수는 income 변수에 추가되어 추후 출금할 수 있게 됩니다.

6. withdrawal 함수 (수익 출금)

function withdrawal() public isOwner {
    uint out = income * (10**18);
    owner.transfer(out);
    income = 0;
}
  • isOwner라는 수정자가 추가된 이 함수는 소유자만 호출할 수 있습니다.
  • 수익(income)을 이더리움으로 환산하여 소유자에게 전송하고, 이후 income을 0으로 리셋합니다.

7. setRate 함수 (환율 설정)

function setRate(uint _rate) public isOwner {
    rate = _rate;
}
  • setRate 함수는 환율을 설정하는 함수입니다. 이 함수는 소유자만 호출할 수 있습니다.
  • 환율을 설정하여 사용자가 보낼 이더리움에 대해 어떤 비율로 토큰을 받을지를 정합니다.

8. additionalIssue 함수 (토큰 추가 발행)

function additionalIssue(uint _amount) public isOwner {
    balanceOf[owner] += _amount;
    totalSupply += _amount;
}
  • 소유자는 필요에 따라 토큰을 추가로 발행할 수 있습니다.
  • 새로운 토큰이 소유자에게 할당되며, 총 공급량도 증가합니다.

9. isOwner 수정자

modifier isOwner() {
    require(msg.sender == owner);
    _;
}
  • isOwner는 소유자만 특정 기능을 호출할 수 있도록 제한하는 수정자입니다.
  • 함수 앞에 isOwner를 추가하면 해당 함수는 소유자만 호출할 수 있게 됩니다.

결론

이번에 작성한 SellToken 스마트 컨트랙트는 기본적인 토큰 판매 기능을 제공하는 컨트랙트입니다. 사용자는 이더리움을 지불하고 토큰을 구매할 수 있으며, 소유자는 환율을 설정하고, 수익을 출금하며, 추가 토큰을 발행할 수 있습니다.

이 코드를 통해 Solidity와 스마트 컨트랙트의 핵심 개념을 익히고, 간단한 토큰 판매 시스템을 구축하는 방법을 배웠습니다.

728x90