Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Stable의 USDT0 동작

이더리움에서 계약을 포팅하는 경우, 배포하기 전에 이 페이지를 읽으십시오. Stable의 USDT0는 기본 가스 토큰이자 동일한 잔액의 ERC-20 토큰입니다. 이로 인해 이더리움에서 가정되는 네 가지 동작이 깨집니다. 계약의 기본 잔액은 계약 호출 없이 변경될 수 있고, EXTCODEHASH는 0과 빈 해시 사이를 오갈 수 있으며, 0주소 전송은 되돌려지고, 단일 논리적 전송은 부분 잔액 조정으로 인해 여러 개의 Transfer 이벤트를 발생시킬 수 있습니다.

이 페이지에서는 각 사례를 살펴보고 안전한 계약 패턴을 제시합니다. 한 섹션만 읽는다면 마이그레이션 체크리스트를 읽으십시오. 이더리움 계약을 여기에 포팅하는 요약입니다.

이중 역할 개요

Stable의 USDT0는 기본 가스 토큰이자 ERC-20 토큰입니다. 이 이중 역할 모델은 잔액 동작, 계약 설계 및 이벤트 처리에 영향을 미칩니다. 아래 섹션에서는 이중 역할이 예상되는 동작을 변경하는 모든 경우를 살펴봅니다.

USDT0가 이런 방식으로 작동하는 이유에 대한 배경은 가스로서의 USDT0를 참조하십시오. 실제 전송을 통해 동작을 경험하려면 첫 USDT0 전송을 참조하십시오.

잔액 조정

USDT0는 기본 자산으로 18진수를 사용하고 ERC-20 토큰으로 6진수를 사용합니다. 기본 전송과 ERC-20 전송은 동일한 기본 잔액에서 작동하지만, 12자리 정밀도 차이는 전송에 서브 정수 정밀도가 포함될 때 시스템이 소수 금액을 조정해야 함을 의미합니다.

이전
  0.000001 USDT0 (ERC-20) + 0.000000000000000000 USDT0 (내부)
  // address(account).balance = 0.000001000000000000
  // USDT0.balanceOf(account) = 0.000001

다른 계정으로 0.0000001 USDT0 전송 시

이후
  0.000000 USDT0 (ERC-20) + 0.000000900000000000 USDT0 (내부)
  // address(account).balance = 0.000000900000000000
  // USDT0.balanceOf(account) = 0.000000

이로 인해 address(account).balanceUSDT0.balanceOf(account)가 최대 0.000001 USDT0까지 차이가 날 수 있습니다.

이벤트 처리

각 조정 전송은 추가 Transfer 이벤트를 발생시킵니다. 단일 논리적 USDT0 전송은 발신자와 수신자의 소수 잔액이 어떻게 영향을 받는지에 따라 최대 두 개의 추가 Transfer 이벤트를 생성할 수 있습니다.

  • 발신자 조정: 발신자의 소수 잔액이 불충분하면 0.000001 USDT0가 발신자에서 예비 주소로 이동합니다. 이로 인해 추가 Transfer 이벤트가 발생합니다.
  • 수신자 조정: 수신자의 소수 잔액이 오버플로되면 0.000001 USDT0가 예비 주소에서 수신자로 이동합니다. 이로 인해 추가 Transfer 이벤트가 발생합니다.
  • 모든 조정: 두 조건이 동일한 전송에서 발생하면 예비가 우회됩니다. 발신자는 주 전송의 일부로 0.000001 USDT0를 수신자에게 직접 전송합니다. 추가 이벤트는 발생하지 않습니다.

이러한 보조 이벤트는 예비 주소 0x5113954bbC0eD721F1C68671EBa3d91e9e9bF7b5를 포함합니다. Transfer 이벤트를 다시 재생하여 USDT0 잔액을 추적하는 인덱서 및 오프체인 서비스는 이 주소로의 전송 및 이 주소로부터의 전송을 필터링하거나 고려해야 합니다.

계약 설계 요구 사항

기본 잔액 변경 가능성

이더리움에서 계약의 기본 잔액은 일반적으로 계약 실행의 결과로만 변경됩니다. Stable에서는 계약의 기본 USDT0 잔액이 transferFrompermit을 포함한 ERC-20 허용 기반 작업으로 인해 변경될 수도 있습니다. 이러한 작업은 계약 코드를 호출하지 않고도 계약의 기본 잔액을 줄일 수 있습니다.

결과적으로 다음 가정은 Stable에서 유효하지 않습니다.

계약의 기본 잔액은 계약이 호출되어야만 줄어들 수 있습니다.

기본 잔액 미러링 금지

이더리움에서는 예금을 내부 변수로 추적하는 것이 일반적입니다. Stable에서는 ERC-20 transferFrom이 외부적으로 기본 잔액을 소진할 수 있으므로 이는 안전하지 않습니다.

// Stable에서 안전하지 않음
uint256 public deposited;
 
function deposit() external payable {
    deposited += msg.value;
}

전송 직전에 항상 실제 잔액 확인

모든 기본 가치 전송은 전송 직전에 address(this).balance를 사용하여 지급 능력을 확인해야 하며, 내부 회계 변수를 사용해서는 안 됩니다.

// 안전함
function withdraw() external {
    uint256 amount = credit[msg.sender];
    credit[msg.sender] = 0;
    require(address(this).balance >= amount, "잔액 부족");
    payable(msg.sender).call{value: amount}("");
}

상태 진행은 잔액 독립적이어야 합니다.

진행, 이정표 또는 완료 조건에 의존하는 프로토콜 논리는 카운터 또는 에포크와 같은 비잔액 상태 변수를 사용하여 명시적으로 추적해야 합니다. 기본 잔액은 지불 시점의 지급 능력 확인에만 사용해야 합니다.

0주소 전송 금지

Stable에서는 address(0)로의 기본 및 ERC-20 전송은 모두 되돌려집니다.

// Stable에서 되돌려짐
payable(address(0)).call{value: amount}("")
USDT0.transfer(address(0), amount);

기본 USDT0를 전송하는 계약 논리는 수신자를 검증하고 전송 호출 전에 address(0)를 명시적으로 거부해야 합니다.

// 안전함
require(recipient != address(0), "0 주소 수신자");
payable(recipient).call{value: amount}("");

계약이 0주소 전송을 소각 메커니즘으로 사용하는 경우 재설계해야 합니다. 되돌릴 수 없는 손실 의미론이 필요한 경우 명시적인 싱크 계약을 사용하십시오.

EXTCODEHASH 동작

이더리움에서 EXTCODEHASH opcode는 다음을 반환합니다.

  • 0 해시 (0x0000...): 주소가 사용된 적이 없는 경우 (nonce=0, balance=0, 코드 없음).
  • 빈 해시 (0xc5d2…a470, 빈 코드의 Keccak-256 해시): 주소가 존재하지만 코드가 없는 경우.

이더리움에서 주소가 0 해시에서 빈 해시로 전환되면 다시 0 해시로 돌아갈 수 없습니다. Stable에서는 USDT0가 permit() 기반 승인을 지원하므로 주소는 트랜잭션을 보내지 않고 승인을 생성할 수 있습니다. transferFrom()과 결합하면 nonce 증가 없이 기본 잔액 변경이 가능하며, EXTCODEHASH가 0 해시와 빈 해시 사이를 오갈 수 있습니다.

// Stable에서 안전하지 않음
function isUnusedAddress(address addr) public view returns (bool) {
    bytes32 codeHash;
    assembly {
        codeHash := extcodehash(addr)
    }
    return codeHash == bytes32(0);
}

대신 명시적 추적을 사용하십시오.

// 안전함
contract SafeAddressTracker {
    mapping(address => bool) public hasBeenUsed;
 
    function markAsUsed(address addr) internal {
        hasBeenUsed[addr] = true;
    }
 
    function isUnused(address addr) public view returns (bool) {
        return !hasBeenUsed[addr];
    }
}

테스트 요구 사항

Stable 배포를 위한 테스트 스위트에는 다음이 포함되어야 합니다.

  • 허용 기반 소진 시나리오 (approve + transferFrom)
  • 실제 기본 잔액을 사용한 지급 능력 강제 적용
  • EXTCODEHASH에 의존하지 않는 주소 사용 논리
  • 0주소 전송에 대한 명시적 오류 사례

마이그레이션 체크리스트

이더리움에서 Stable로 계약을 포팅할 때:

  • 내부 기본 잔액 미러 제거
  • 모든 지급 능력 확인을 address(this).balance로 교체
  • address(0)로의 모든 기본 또는 ERC-20 전송 제거
  • 모든 USDT0 승인 감사
  • permit 및 허용 기반 흐름을 포함하는 테스트 추가
  • 오프체인 인덱서가 부분 잔액 조정으로 인한 보조 Transfer 이벤트를 처리하는지 확인

주요 시사점

Stable에서 올바른 계약 설계를 위해서는 다음이 필요합니다.

  • USDT0를 이중 역할 자산으로 취급
  • 실제 잔액에 대한 지급 능력 강제 적용
  • 허용 기반 소진 경로 방지
  • 이더리움 특정 잔액 및 주소 가정에 대한 의존성 제거

오프체인 서비스 및 인덱서는 다음을 수행해야 합니다.

  • 부분 잔액 조정으로 인한 보조 Transfer 이벤트를 고려
  • 이벤트 기반 잔액 재구성이 아닌 직접 잔액 쿼리 사용

다음 권장 사항

  • 가스로서의 USDT0: USDT0가 기본 자산이자 ERC-20 토큰으로 작동하는 이유를 이해합니다.
  • 첫 USDT0 전송: 기본 및 ERC-20 경로를 통해 테스트넷에서 USDT0 전송을 제출합니다.
  • 이더리움 비교: 이더리움에서 포팅할 때의 모든 동작 차이점을 검토합니다.