무지개곰
article thumbnail
Published 2023. 5. 18. 14:59
[Solidity] ERC-1155란? BlockChain/solidity
반응형

ERC-1155는 이더리움 블록체인에서 다중 자산을 관리하는 표준입니다. 이 표준은 이더리움 기반의 스마트 컨트랙트를 사용하여 개발자가 게임 아이템, 디지털 자산, 토큰화된 자산 등을 효율적으로 관리할 수 있게 해 줍니다.

ERC-1155 표준

ERC_1155는 게임 개발자뿐만 아니라 다양한 분야에서 자산을 토큰화하고 거래할 수 있는 유연한 토큰 관의 표준입니다. 이러한 기능을 구현하기 위하여 아래의 함수가 반드시 포함되어야 합니다.

balanceOf(address _owner, uint256 _id)
balanceOfBatch(address[] _owners, uint256[] _ids)
setApprovalForAll(address _operator, bool _approved)
isApprovedForAll(address _owner, address _operator)
safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes _data)
safeBatchTransferFrom(address _from, address _to, uint256[] _ids, uint256[] _amounts, bytes _data)

balanceOf : _onwer가 보유한 _id 자산의 잔액을 반환

balanceOfBatch : address배열로 받은 _owners에 포함된 주소에 대하여 각 주소가 보유한 _ids의 잔액을 조회하고 배열에 담아 반환

setApprovalForAll : _operator에 대한 _onwer의 모든 자산에 대한 인출 권한을 부여하거나 취소합니다.

isApprovaedForAll : _operator가 _onwer의 자산에 대한 인출 권한을 가지고 있는지 여부를 반환

safeTransferFrom : _from에서 _to로 _amount만큼의 _id에 해당하는 자산을 전송

safeBatchTransferFrom : _from에서 _to로 _ids에 해당하는 자산들을 _amounts에 맞게 전송합니다.

 

또한 위의 함수 이외에도 사용되는 이벤트는 아래와 같습니다.

TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _amount)
TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _amounts)
ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved)
URI(string _value, uint256 indexed _id)

한 가지 자산을 전송할 때는 TransferSingle, 여러 가지 자산을 전송할 때는 TransferBatch, 권한을 승인하는 경우 ApprovalForAll, 자산의 URI가 업데이트되는 경우 URI 이벤트가 발생됩니다.

 

위의 내용을 통하여 interface를 작성한다면 아래와 같습니다.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IERC1155 is IERC165 {
   
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
    event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);
    event URI(string value, uint256 indexed id);

    function balanceOf(address account, uint256 id) external view returns (uint256);
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
    function setApprovalForAll(address operator, bool approved) external;
    function isApprovedForAll(address account, address operator) external view returns (bool);
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}

mint와 burn

ERC-20, ERC-721과 달리 ERC-1155는 mint와 burn을 제공하지 않습니다. mint와 burn 대신 safeTransferFrom 및 safeBatchTransferFrom과 같은 함수를 이용하여 자산을 생성하지 않고, 다른 주소로 전송하거나 이동시킬 수 있습니다.

 

*Openzeppelin에서 제공하는 ERC-1155의 경우 _mint, _mintBatch, _burn, _burnBatch 함수가 존재합니다.

 

_mint

    function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

_mintBatch

    function _mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

_burn

    function _burn(address from, uint256 id, uint256 amount) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

_burnBatch

    function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

mint, burn과 Batch의 차이는 한 가지 자산을 mint 혹은 burn 하는지 여러 가지 자산을 다루는지 차이로 배열로 정보를 받아 for문을 이용하는 차이 이외에 큰 차이는 없습니다.


_beforeTokenTransfer과 _aftreTokenTransfer이란?

mint와 burn함수를 보면 _beforeTokenTransfer과 _afterTokenTransfer이 있습니다. ERC 표준에 포함된 함수가 아닌 사용자 정의 로직을 실행하기 위한 훅(hook)으로 사용됩니다.

 

주요 사용 사례는 다음과 같습니다.

 

1. 전송 제약사항: `_beforeTokenTransfer` 함수를 사용하여 토큰 전송 시 발송자와 수신자의 주소, 토큰 ID 등에 대한 추가적인 제약사항을 검사할 수 있습니다. 예를 들어, 특정 주소에 대한 전송을 제한하거나 전송 조건을 체크할 수 있습니다.

 

2. 이벤트 발행: `_beforeTokenTransfer` 함수를 사용하여 토큰 전송 이벤트 전에 사용자에게 알림을 보내거나 이벤트를 발행할 수 있습니다. 예를 들어, 토큰 전송에 대한 로그를 생성하거나, 특정 이벤트가 발생했음을 통지할 수 있습니다.

 

3. 추가 작업 수행: `_beforeTokenTransfer` 함수를 사용하여 토큰 전송 이전에 추가적인 작업을 수행할 수 있습니다. 예를 들어, 수신자 컨트랙트에 대한 초기화 작업을 수행하거나, 특정 데이터를 업데이트할 수 있습니다.

 

`_beforeTokenTransfer` 함수는 ERC-1155 컨트랙트의 내부 함수로, 토큰 전송 이전에 호출되는 지점을 제공합니다. 이 함수를 커스터마이즈 하여 원하는 로직을 추가할 수 있습니다. 구체적인 동작은 개별적인 ERC-1155 구현에 따라 다르며, 사용자 정의 함수로서 이벤트를 통해 투명하게 작동하도록 설계됩니다.

반응형

'BlockChain > solidity' 카테고리의 다른 글

[Solidity] ERC-2771이란? (ERC-2771Context란?)  (0) 2023.07.02
[Solidity] EIP-712란?  (0) 2023.07.02
[Solidity] ERC20 Permit이란?  (0) 2023.06.17
[Solidity] ERC-721이란?  (0) 2023.05.18
[Solidity] ERC20이란? (ERC20 토큰 발행)  (0) 2023.05.14
profile

무지개곰

@무지개곰

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!