ERC-1155는 하나의 스마트 컨트랙트에서 대체 가능 토큰(ERC-20)과 대체 불가 토큰(ERC-721/NFT)을 동시에 관리할 수 있는 멀티 토큰 표준이다. 2019년 Enjin이 제안했으며, 게임 아이템처럼 다양한 유형의 자산을 하나의 컨트랙트로 효율적으로 처리할 때 사용된다.
핵심 특성
| 특성 | 설명 |
|---|
| 멀티 토큰 | 하나의 컨트랙트에서 수천 종의 토큰 관리 |
| 배치 전송 | 한 번의 트랜잭션으로 여러 토큰 동시 전송 |
| 대체 가능 + 불가 | Fungible과 Non-Fungible 모두 지원 |
| 가스 효율 | ERC-20/721 대비 최대 90% 가스 절감 |
| 수신 훅 | 스마트 컨트랙트 수신자에게 토큰 처리 로직 강제 |
| 운영자 승인 | 특정 금액이 아닌 운영자(주소) 단위 전체 승인 |
ERC-20 / ERC-721 비교
| 항목 | ERC-20 | ERC-721 | ERC-1155 |
|---|
| 토큰 유형 | 대체 가능 | 대체 불가 | 혼합 |
| 컨트랙트 | 토큰당 1개 | 컬렉션당 1개 | 프로젝트당 1개 |
| 배치 전송 | ❌ | ❌ | ✅ |
| 수신 훅 | ❌ | ✅ (onERC721Received) | ✅ (onERC1155Received) |
| 개별 승인 | ✅ (금액 단위) | ✅ (토큰 단위) | ✅ (운영자 단위) |
| 가스 효율 | 보통 | 낮음 | 높음 |
작동 방식
각 토큰은 id로 구분되며, id에 따라 대체 가능/불가를 다르게 설정할 수 있다.
id = 1 → "Gold Coin" (공급량 1,000,000) — 대체 가능 (Fungible)
id = 2 → "Legendary Sword #001" (공급량 1) — 대체 불가 (NFT)
id = 3 → "Silver Key" (공급량 50,000) — 대체 가능 (Semi-fungible)
핵심: ERC-1155에는 일반 transfer()가 없다. 항상 safeTransferFrom() / safeBatchTransferFrom()만 사용한다. 발신자 본인도 from 인자를 명시해야 한다.
전체 인터페이스
solidity
interface IERC1155 {
// ── 조회 ─────────────────────────────────────────────
// 특정 account가 보유한 id 토큰 수량
function balanceOf(address account, uint256 id)
external view returns (uint256);
// 여러 account-id 쌍의 잔액 일괄 조회
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external view returns (uint256[] memory);
// operator가 owner의 모든 토큰 사용 권한 여부
function isApprovedForAll(address owner, address operator)
external view returns (bool);
// 토큰 메타데이터 URI ({id} 자리표시자 포함)
function uri(uint256 id) external view returns (string memory);
// ── 전송 ─────────────────────────────────────────────
// 단일 토큰 안전 전송
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;
// ── 승인 ─────────────────────────────────────────────
// operator에게 모든 토큰 사용 권한 부여/회수
function setApprovalForAll(address operator, bool approved) external;
}
이벤트
solidity
// 단일 토큰 전송 시 발생
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
);
// 토큰 URI 변경 시 발생
event URI(string value, uint256 indexed id);
민팅(발행)은 from = address(0), 소각은 to = address(0)으로 TransferSingle/Batch 이벤트를 발생시킨다.
수신 훅 (Receiver Hook)
스마트 컨트랙트 주소로 토큰을 전송할 때, 수신 컨트랙트는 반드시 수신 훅을 구현해야 한다. 그렇지 않으면 트랜잭션이 revert되어 토큰이 잠기는 것을 방지한다.
solidity
interface IERC1155Receiver {
// 단일 토큰 수신 훅
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
// 반드시 bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))
// == 0xf23a6e61 반환
// 배치 토큰 수신 훅
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
// 반드시 bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))
// == 0xbc197c81 반환
}
EIP-165의 supportsInterface()를 통해 수신자가 훅을 구현했는지 먼저 확인한다.
안전 전송 규칙
safeTransferFrom / safeBatchTransferFrom 호출은 다음 조건 중 하나라도 해당하면 revert된다.
| 조건 | 설명 |
|---|
| 권한 없음 | msg.sender가 토큰 소유자도 아니고 승인된 운영자도 아닌 경우 |
| 0 주소 전송 | to == address(0) (소각은 별도 함수로 처리) |
| 배열 길이 불일치 | ids.length != amounts.length |
| 잔액 부족 | 보유 수량 < 전송 수량 |
| 훅 실패 | 수신 컨트랙트가 올바른 bytes4 값을 반환하지 않음 |
메타데이터 URI
uri(id) 함수로 각 토큰의 메타데이터 URL을 반환한다. URL에 {id} 자리표시자를 포함하면 클라이언트가 토큰 ID로 치환해 조회한다.
uri(id) 반환값 예시:
"https://api.example.com/metadata/{id}.json"
클라이언트가 id=2를 조회할 때:
"https://api.example.com/metadata/0000000000000000000000000000000000000000000000000000000000000002.json"
↑ id를 64자리 16진수 0-패딩 형태로 치환
메타데이터 JSON 구조:
json
{
"name": "Legendary Sword",
"description": "A rare weapon",
"image": "ipfs://Qm.../sword.png",
"decimals": 0,
"properties": {
"attack": 150,
"rarity": "legendary"
}
}
구현 예시 (OpenZeppelin)
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
contract GameItems is ERC1155 {
uint256 public constant GOLD = 0; // 대체 가능 (화폐)
uint256 public constant SILVER = 1; // 대체 가능 (화폐)
uint256 public constant SWORD = 2; // 반대체 가능 (장비)
uint256 public constant SHIELD = 3; // 반대체 가능 (장비)
uint256 public constant CROWN = 4; // 대체 불가 (희귀 아이템)
constructor() ERC1155("https://game.example/api/item/{id}.json") {
_mint(msg.sender, GOLD, 10**18, ""); // 100경 GOLD 발행
_mint(msg.sender, SILVER, 10**27, "");
_mint(msg.sender, SWORD, 1000, "");
_mint(msg.sender, SHIELD, 1000, "");
_mint(msg.sender, CROWN, 1, ""); // NFT
}
}
// 배치 민팅 (가스 절감)
// _mintBatch(msg.sender, [GOLD, SWORD, CROWN], [10**18, 1000, 1], "");
주요 활용
| 분야 | 사례 |
|---|
| 블록체인 게임 | Enjin, Axie Infinity, Gods Unchained — 다양한 아이템을 하나의 컨트랙트로 |
| NFT 마켓플레이스 | OpenSea Storefront 컨트랙트 — 크리에이터 스토어 |
| DeFi LP 토큰 | 여러 풀의 지분 토큰 통합 관리 |
| 에디션 발행 | 동일 아트의 한정판 여러 개 (공급량 N의 Semi-fungible) |
관련 개념
- •NFT — ERC-1155가 지원하는 대체 불가 자산
- •ERC-721 — 개별 NFT 전용 표준
- •ERC-20 — 대체 가능 토큰 표준
- •스마트 컨트랙트 — ERC-1155 구현 기반
참고문헌
- 1.Ethereum Foundation. (2024). ERC-1155 토큰 표준. ethereum.org.
- 2.EIP-1155
- 3.Enjin ERC-1155
- 4.OpenZeppelin ERC1155 문서