ERC-223 토큰 표준
소개
ERC-223이란 무엇인가요?
ERC-223은 ERC-20 표준과 유사한 대체 가능 토큰을 위한 표준입니다. 주요 차이점은 ERC-223이 토큰 API뿐만 아니라 발신자에서 수신자로 토큰을 전송하는 로직도 정의한다는 것입니다. 이 표준은 수신자 측에서 토큰 전송을 처리할 수 있도록 하는 통신 모델을 도입합니다.
ERC-20과의 차이점
ERC-223은 ERC-20의 일부 한계를 해결하고 토큰 컨트랙트와 토큰을 수신할 수 있는 컨트랙트 간의 새로운 상호작용 방식을 도입합니다. ERC-20에서는 불가능하지만 ERC-223에서는 가능한 몇 가지 기능이 있습니다.
- 수신자 측의 토큰 전송 처리: 수신자는 ERC-223 토큰이 예치되고 있음을 감지할 수 있습니다.
- 잘못 전송된 토큰 거부: 사용자가 토큰을 수신하도록 설계되지 않은 컨트랙트로 ERC-223 토큰을 전송하는 경우, 해당 컨트랙트는 트랜잭션을 거부하여 토큰 손실을 방지할 수 있습니다.
- 전송 시 메타데이터 포함: ERC-223 토큰은 메타데이터를 포함할 수 있어 토큰 트랜잭션에 임의의 정보를 첨부할 수 있습니다.
전제 조건
본문
ERC-223은 스마트 컨트랙트 내에서 토큰을 위한 API를 구현하는 토큰 표준입니다. 또한 ERC-223 토큰을 수신하도록 설계된 컨트랙트를 위한 API도 선언합니다. ERC-223 수신자 API를 지원하지 않는 컨트랙트는 ERC-223 토큰을 받을 수 없으므로 사용자 오류를 방지합니다.
스마트 컨트랙트가 다음 메서드와 이벤트를 구현하면 ERC-223 호환 토큰 컨트랙트라고 부를 수 있습니다. 배포가 완료되면 이더리움에서 생성된 토큰을 추적하는 역할을 담당하게 됩니다.
컨트랙트가 반드시 이러한 함수만 가져야 하는 것은 아니며, 개발자는 다른 토큰 표준의 기능을 이 컨트랙트에 추가할 수 있습니다. 예를 들어, approve 및 transferFrom 함수는 ERC-223 표준의 일부가 아니지만 필요한 경우 구현할 수 있습니다.
EIP-223 (opens in a new tab) 발췌:
메서드
ERC-223 토큰은 다음 메서드를 구현해야 합니다.
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transfer(address _to, uint256 _value, bytes calldata _data) public returns (bool success)
ERC-223 토큰을 수신하도록 설계된 컨트랙트는 다음 메서드를 구현해야 합니다.
function tokenReceived(address _from, uint _value, bytes calldata _data)
ERC-223 토큰이 tokenReceived(..) 함수를 구현하지 않은 컨트랙트로 전송되는 경우, 전송은 실패해야 하며 발신자의 잔액에서 토큰이 이동해서는 안 됩니다.
이벤트
event Transfer(address indexed _from, address indexed _to, uint256 _value, bytes calldata _data)
예시
ERC-223 토큰의 API는 ERC-20과 유사하므로 UI 개발 관점에서는 차이가 없습니다. 유일한 예외는 ERC-223 토큰의 경우 approve 및 transferFrom 함수가 이 표준에서 선택 사항이므로 없을 수도 있다는 점입니다.
Solidity 예시
다음 예시는 기본적인 ERC-223 토큰 컨트랙트가 어떻게 작동하는지 보여줍니다.
pragma solidity ^0.8.19;
abstract contract IERC223Recipient {
function tokenReceived(address _from, uint _value, bytes memory _data) public virtual;
}
contract VeryBasicERC223Token {
event Transfer(address indexed from, address indexed to, uint value, bytes data);
string private _name;
string private _symbol;
uint8 private _decimals;
uint256 private _totalSupply;
mapping(address => uint256) private balances;
function name() public view returns (string memory) { return _name; }
function symbol() public view returns (string memory) {return _symbol; }
function decimals() public view returns (uint8) { return _decimals; }
function totalSupply() public view returns (uint256) { return _totalSupply; }
function balanceOf(address _owner) public view returns (uint256) { return balances[_owner]; }
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function transfer(address _to, uint _value, bytes calldata _data) public returns (bool success){
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
if(isContract(_to)) {
IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data);
}
emit Transfer(msg.sender, _to, _value, _data);
return true;
}
function transfer(address _to, uint _value) public returns (bool success){
bytes memory _empty = hex"00000000";
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
if(isContract(_to)) {
IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty);
}
emit Transfer(msg.sender, _to, _value, _empty);
return true;
}
}
이제 tokenA가 ERC-223 토큰이라고 가정하고, 다른 컨트랙트가 tokenA의 예치를 수락하도록 만들고자 합니다. 이 컨트랙트는 tokenA만 수락하고 다른 모든 토큰은 거부해야 합니다. 컨트랙트가 tokenA를 수신하면 Deposit() 이벤트를 발생시키고 내부 deposits 변수의 값을 증가시켜야 합니다.
코드는 다음과 같습니다.
contract RecipientContract is IERC223Recipient {
event Deposit(address whoSentTheTokens);
uint256 deposits = 0;
address tokenA; // 우리가 수락하고자 하는 유일한 토큰입니다.
function tokenReceived(address _from, uint _value, bytes memory _data) public override
{
// 이 함수 내에서는 다음을 이해하는 것이 중요합니다.
// msg.sender는 수신 중인 토큰의 주소입니다,
// 대부분의 경우 토큰 컨트랙트는 이더를 소유하거나 전송하지 않으므로 msg.value는 항상 0입니다,
// _from은 토큰 전송의 발신자입니다,
// _value는 예치된 토큰의 양입니다.
require(msg.sender == tokenA);
deposits += _value;
emit Deposit(_from);
}
}
자주 묻는 질문
컨트랙트에 tokenB를 전송하면 어떻게 되나요?
트랜잭션이 실패하고 토큰 전송이 이루어지지 않습니다. 토큰은 발신자의 주소로 반환됩니다.
이 컨트랙트에 어떻게 예치할 수 있나요?
RecipientContract의 주소를 지정하여 ERC-223 토큰의 transfer(address,uint256) 또는 transfer(address,uint256,bytes) 함수를 호출합니다.
이 컨트랙트로 ERC-20 토큰을 전송하면 어떻게 되나요?
ERC-20 토큰이 RecipientContract로 전송되면 토큰은 전송되지만, 전송이 인식되지 않습니다(Deposit() 이벤트가 발생하지 않으며 예치금 값도 변경되지 않습니다). 원치 않는 ERC-20 예치는 필터링하거나 방지할 수 없습니다.
토큰 예치가 완료된 후 특정 함수를 실행하려면 어떻게 해야 하나요?
이를 수행하는 방법에는 여러 가지가 있습니다. 이 예시에서는 ERC-223 전송을 이더 전송과 동일하게 만드는 방법을 따르겠습니다.
contract RecipientContract is IERC223Recipient {
event Foo();
event Bar(uint256 someNumber);
address tokenA; // 우리가 수락하고자 하는 유일한 토큰입니다.
function tokenReceived(address _from, uint _value, bytes memory _data) public override
{
require(msg.sender == tokenA);
address(this).call(_data); // 들어오는 트랜잭션을 처리하고 후속 함수 호출을 수행합니다.
}
function foo() public
{
emit Foo();
}
function bar(uint256 _someNumber) public
{
emit Bar(_someNumber);
}
}
RecipientContract가 ERC-223 토큰을 수신하면, 이더 트랜잭션이 함수 호출을 트랜잭션 data로 인코딩하는 것과 동일하게 토큰 트랜잭션의 _data 매개변수로 인코딩된 함수를 실행합니다. 자세한 내용은 데이터 필드를 읽어보세요.
위의 예시에서 ERC-223 토큰은 transfer(address,uin256,bytes calldata _data) 함수를 사용하여 RecipientContract의 주소로 전송되어야 합니다. 데이터 매개변수가 0xc2985578(foo() 함수의 서명)인 경우, 토큰 예치를 받은 후 foo() 함수가 호출되고 Foo() 이벤트가 발생합니다.
매개변수는 토큰 전송의 data에도 인코딩될 수 있습니다. 예를 들어 _someNumber에 대해 12345 값으로 bar() 함수를 호출할 수 있습니다. 이 경우 data는 0x0423a13200000000000000000000000000000000000000000000000000000000000004d2여야 하며, 여기서 0x0423a132는 bar(uint256) 함수의 서명이고 00000000000000000000000000000000000000000000000000000000000004d2는 uint256 형식의 12345입니다.
한계
ERC-223은 ERC-20 표준에서 발견된 여러 문제를 해결하지만, 자체적인 한계가 없는 것은 아닙니다.
- 채택 및 호환성: ERC-223은 아직 널리 채택되지 않았으며, 이로 인해 기존 도구 및 플랫폼과의 호환성이 제한될 수 있습니다.
- 하위 호환성: ERC-223은 ERC-20과 하위 호환되지 않으므로, 기존 ERC-20 컨트랙트 및 도구는 수정 없이 ERC-223 토큰과 함께 작동하지 않습니다.
- 가스 비용: ERC-223 전송의 추가적인 검사 및 기능으로 인해 ERC-20 트랜잭션에 비해 가스 비용이 더 높아질 수 있습니다.