Midnight의 FungibleToken 컨트랙트 소개
Introduction to the FungibleToken contract on Midnight
Midnight은 탈중앙화 애플리케이션에 프라이버시를 제공하기 위해 설계된 프라이버시 우선 블록체인입니다. 영지식 증명, 프로그래밍 가능한 데이터 보호, 그리고 개발자 친화적 도구인 Compact(TypeScript 기반 DSL, 도메인 특화 언어)을 통해 이를 실현합니다.
OpenZeppelin은 Ethereum 생태계에서 실전 검증된 스마트 컨트랙트 라이브러리로 유명하며, 온체인에서 수조 달러 규모의 가치를 보호해 왔습니다. 최근 OpenZeppelin은 Midnight과 파트너십을 맺고 ERC-20과 같은 친숙한 표준을 프라이버시 보존 버전으로 적용하여 Compact 생태계에 유사한 도구를 제공하고 있습니다.
Ethereum 세계에서 ERC-20 표준은 balanceOf, transfer, approve 등의 공개 원장 함수를 가진 대체 가능 토큰을 정의합니다. 트랜잭션 데이터를 투명하게 노출하며 내장된 프라이버시가 없습니다. Midnight의 FungibleToken 컨트랙트는 여기서 영감을 얻었지만, Midnight의 영지식, 선택적 공개 프레임워크 안에서 동작합니다.
대체 가능 토큰(Fungible Token)은 블록체인 생태계의 핵심 요소로, 전통 화폐처럼 서로 교환 가능한 디지털 자산입니다. 거래 촉진, DeFi 프로토콜 운영, 디지털 커뮤니티 내 소유권 표현, 게임 내 경제 구동 등 폭넓게 활용됩니다.
각각 고유한 NFT와 달리, 대체 가능 토큰은 같은 종류끼리 동일한 가치를 지니므로 분할과 교환이 필요한 사용 사례에 적합합니다. 이런 폭넓은 채택은 유동적이고 상호 연결된 디지털 경제에서 대체 가능 토큰이 얼마나 중요한지를 보여줍니다.
이 글에서는 컨트랙트의 핵심 기능을 다룹니다. 원장 상태 변수 관리, 민팅/소각/전송 등 주요 진입점과 서킷, 그리고 Utils 및 Initializable 모듈의 안전 및 유틸리티 함수를 살펴봅니다. 이 구성 요소들의 관계를 이해하면, FungibleToken 컨트랙트가 대체 가능성, 사용성, 프라이버시를 어떻게 균형 있게 달성하는지 파악할 수 있습니다. Midnight에서 프라이버시 보존 DeFi, 신원 확인, 토큰화 자산을 구현하기 위한 필수 빌딩 블록입니다.
Features of the FungibleToken Contract
Midnight의 FungibleToken 컨트랙트는 원장 상태 변수를 활용하여 잔액, 허용량, 총 공급량, 이름, 심볼, 소수점을 추적합니다. 기능은 Mint, Burn, Transfer, Approve, TransferFrom, Initialize 등의 "서킷"(진입점)을 통해 노출되며, 모두 특정 영지식 검증 전환을 강제하고 토큰 상태의 무결성을 유지합니다.
Ledger State Variables
Compact에서 컨트랙트는 토큰 잔액과 허용량을 저장하는 구조화된 상태를 정의합니다. ERC-20과 유사합니다. _balances 맵은 사용자의 토큰 잔액을 추적하며 전송이 발생할 때 업데이트됩니다. _allowances 맵은 다른 사용자를 대신하여 토큰을 사용할 수 있도록 특정 사용자에게 부여된 권한을 추적합니다:
export ledger _balances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>;
export ledger _allowances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>>;
이 값들은 컨트랙트의 원장에 저장되며, 컨트랙트로 전송된 트랜잭션을 통해 업데이트됩니다.
컨트랙트 배포 시 설정되는 다른 값들도 있습니다:
export ledger _totalSupply: Uint<128>;
export sealed ledger _name: Opaque<"string">;
export sealed ledger _symbol: Opaque<"string">;
export sealed ledger _decimals: Uint<8>;
이 값들은 컨트랙트가 관리하는 토큰에 대한 다양한 정보를 제공합니다. 총 공급량, 이름, 심볼, 소수점(표시용) 등입니다.
Entry Points and Circuits
Compact에서 진입점은 서킷(Solidity 함수와 유사)으로 정의되며, 각각 영지식 검증 전환을 모델링합니다. 서킷 진입점과 서킷의 차이점은 진입점은 트랜잭션을 통해 호출할 수 있지만, 진입점이 아닌 서킷은 내부용이라는 것입니다. 핵심 서킷은 다음과 같습니다:
-
Mint/Burn(새 토큰을 민팅하거나 기존 토큰을 소각). -
Transfer: 주소 간 토큰 이동. -
Approve,TransferFrom: 표준 ERC-20 스타일 위임 메커니즘. -
Initialize: Initializable 모듈을 통한 컨트랙트 설정.
각 서킷은 필요한 제약 조건을 강제합니다. 예를 들어 충분한 잔액 확인, 허용량 차감 관리, 총 공급량 보존 등입니다.
컨트랙트 수명 주기의 다음 단계에서는 컨트랙트의 원장에 저장된 다양한 메타데이터가 안전하게 초기화됩니다.
Initialization and Metadata**
다음 서킷들은 대체 가능 토큰 메타데이터와 사용자 잔액을 위한 필수 설정 및 조회 로직을 정의하며, 올바른 초기화를 강제합니다.
-
initialize(name_, symbol_, decimals_)일회성 설정입니다.Initializable_initialize()를 호출한 뒤, (공개된) 이름, 심볼, 소수점을 저장합니다. 다른 모든 공개 서킷은 먼저 컨트랙트가 초기화되었는지 확인합니다. -
name()/symbol()/decimals()/totalSupply()초기화 여부를 먼저 확인한 뒤 봉인된(읽기 전용) 원장 값을 반환하는 간단한 게터입니다. -
balanceOf(account)키가 없는 경우 컨트랙트 실패를 방지하기 위해0을 반환하는 안전한 맵 조회입니다.
The Transfer Family
FungibleToken 컨트랙트의 전송 서킷은 토큰 이동을 관리합니다. 주요 서킷은 다음과 같습니다: 안전한 사용자 주도 전송을 위한 transfer, 내부 토큰 이동을 위한 _unsafeTransfer, 관리자 전송을 위한 _transfer, 저수준 토큰 이동을 위한 _unsafeUncheckedTransfer, 그리고 모든 토큰 작업의 중앙 회계 함수인 _update.
안전한 변형과 안전하지 않은 변형으로 나뉘는 이유는 현재 컨트랙트 주소로의 전송이 허용되지 않기 때문입니다(컨트랙트 간 상호작용이 지원될 때까지).
"안전한" 서킷은 이 정책을 강제하고, "안전하지 않은" 서킷은 이를 우회할 수 있으며, 주석에 위험하다고 명시적으로 표시되어 있습니다.
-
transfer(to, value)->Boolean안전한 사용자 주도 전송:to가ContractAddress이면 거 부합니다. 내부적으로는 검사 후 안전하지 않은 변형으로 전달합니다. -
_unsafeTransfer(to, value)->Boolean소유자가 호출자(left(ownPublicKey()))입니다. 검사 없는 내부 이동 함수를 사용하여 값을 이동한 뒤true를 반환합니다. -
_transfer(from, to, value)->[]관리자/확장 훅으로, 임의의from에서 토큰을 이동합니다(반드시 호출자일 필요 없음). "컨트랙트를to로 사용할 수 없음" 규칙은 여전히 강제되며, 내부적으로 동일한 이동 함수를 사용합니다. -
_unsafeUncheckedTransfer(from, to, value)->[]저수준 이동 함수로, 양쪽이 영/소각 주소가 아닌지 확인한 뒤 실제 회계를_update에 위임합니다. -
_update(from, to, value)->[]모든 민팅/소각/전송 경로에서 사용되는 중앙 회계 함수입니다. 내부 서킷으로, 트랜잭션을 통해 호출할 수 없습니다.-
from이 영 주소이면 민팅 서킷이 호출되어uint128오버플로우가 없는지 확인하고_totalSupply를 증가시킵니다. -
그렇지 않으면
from잔액에서 차감합니다(잔액 부족 시 되돌림). -
to가 영 주소이면 소각 서킷이 호출되어_totalSupply를 감소시킵니다. -
그렇지 않으면
to잔액에 추가합니다. 이 단일 함수가 모든 가치 이동에 대한 불변성을 보장합니다.
-
"전송 계열" 서킷은 안전한 토큰 이동을 보장하며, "안전한" 변형은 컨트랙트 주소로의 전송을 차단하고 "안전하지 않은" 변형은 저수준 제어를 제공합니다. 다음으로 허용량이 어떻게 작동하여 위임된 토큰 사용을 가능하게 하는지 살펴보겠습니다.
Allowances (approve / spend / transferFrom)
이 섹션에서는 FungibleToken 컨트랙트의 허용량(allowance) 메커니즘을 다룹니다. 사용자가 다른 주소에 토큰 지출 권한을 위임할 수 있는 서킷들로, 소유자의 개인 키를 직접 노출하지 않고도 대리 전송이 가능합니다.
-
allowance(owner, spender)중첩된_allowances맵을 읽으며, 키가 없을 때0을 반환합니다(되돌림 없음). -
approve(spender, value)->Boolean소유자가 호출자입니다._approve(owner, spender, value)로 전달하고true를 반환합니다. -
transferFrom(from, to, value)->Boolean안전한 위임 전송: "컨트랙트 수신자 불가" 규칙을 강제한 뒤,_unsafeTransferFrom에 위임합니다. -
_unsafeTransferFrom(from, to, value)->Boolean지출자가 호출자입니다. 먼저_spendAllowance(from, spender, value)를 통해 허용량을 사용한 뒤,_unsafeUncheckedTransfer로 값을 이동합니다.true를 반환합니다. -
_approve(owner, spender, value)->[]소유 자와 지출자가 모두 유효한지 확인하고, 필요하면 맵에 소유자 항목을 생성한 뒤 허용량을 기록합니다. (OZ의 ERC-20 패턴인 공개approve()-> 내부_approve()구조를 따릅니다.) -
_spendAllowance(owner, spender, value)->[]"무한"이 아닌 한 허용량에서 차감합니다. 구현에서 **MAX_UINT128**을 무한으로 취급합니다:currentAllowance == MAX이면 차감하지 않고, 그렇지 않으면currentAllowance >= value를 확인한 뒤currentAllowance - value를 기록합니다. 이는 앱이 MAX를 한 번 설정하면 추가 승인 없이 사용할 수 있는 "무마찰 승인"을 지원하기 때문에 중요합니다.
지금까지 토큰 지출을 다른 주소에 위임하는 허용량 메커니즘을 살펴보았습니다. 다음으로 토큰의 생성(민팅)과 소멸(소각) 방법을 알아보겠습니다.
Minting and Burning
FungibleToken 컨트랙트가 토큰을 생성하고 소멸하는 방법입니다. _mint와 _burn 함수가 무엇을 하고 주요 회계 시스템과 어떻게 연결되는지 살펴보겠습니다.
-
_mint(account, value)(안전한) ->[]컨트랙트 주소로의 민팅을 금지합니다(동일한 컨트랙트 간 제한)._unsafeMint로 전달합니다. -
_unsafeMint(account, value)->[]수신자 주소를 검증한 뒤,_update(burnAddress(), account, value)를 호출합니다. 즉, 민팅은 소각/영 주소로 부터의 전송으로 모델링됩니다. -
_burn(account, value)->[]발신자 주소를 검증한 뒤,_update(account, burnAddress(), value)를 호출합니다. 즉, 소각은 소각/영 주소로의 전송입니다. 참고: "영/소각" 주소의 실제 개념은 Utils 모듈에서 표준화되어 있습니다.Utils_isKeyOrAddressZero및Utils_isContractAddress같은 헬퍼도 확인할 수 있습니다.
민팅과 소각도 _update를 통해 라우팅되므로, 총 공급량은 정확히 한 곳에서만 조정되며, 모든 흐름에 동일한 안전 검사가 적용됩니다(민팅 시 uint128 오버플로우 검사 포함).
민팅과 소각 서킷은 _update 함수를 사용함으로써 총 공급량 조정이 항상 일관되고 모든 토큰 흐름에 동일한 안전 검사가 적용되도록 보장합니다.
이제 Utils와 Initializable 모듈이 제공하는 추가 안전 및 유틸리티 기능을 살펴보겠습니다.
Safety and Utility Glue (from Utils and Initializable)
Utils와 Initializable 모듈이 제공하는 핵심 보호 장치와 유틸리티 함수를 살펴봅니다. 컨트랙트의 무결성을 유지하고 안전한 운영을 보장하는 필수 구성 요소입니다.
-
초기화 가드:
Initializable_initialize와Initializable_assertInitialized함수는 초기화 가드 역할을 합니다. 컨트랙트 상태가 단 한 번만 올바르게 설정되고, 초기화가 완료된 경우에만 이후 작업이 진행되도록 보장합니다. 상태를 읽거나 수정하는 모든 서킷은assert함수를 호출하여 초기화 무결성을 검증합니다. -
주소 헬퍼:
-
Utils_isContractAddress(either): 사용자 키와 컨트랙트 주소를 구분합니다. -
Utils_isKeyOrAddressZero(either):_update,_unsafeUncheckedTransfer등에서 사용되는 영/소각 주소를 감지합니다. 이들은 임시 "컨트랙트 수신자 불가" 정책과 영 주소 검사를 지원합니다.
-
Utils와 Initializable 모듈은 컨트랙트의 올바른 설정과 안전한 운영을 뒷받침합니다. 다음으로 FungibleToken 컨트랙트의 전체 구성 요소가 어떻게 맞물려 동작하는지 살펴보겠습니다.
How the Pieces Fit Together
FungibleToken 컨트랙트의 모든 구성 요소가 어떻게 연결되는지 정리합니다. 직접 전송이든, 위임 전송이든, 민팅이든, 소각이든, 결국 몇 가지 핵심 함수를 거쳐 최종적으로 _update 함수에서 회계 처리됩니다.
-
사용자 전송:
transfer-> (안전 검사) ->_unsafeTransfer->_unsafeUncheckedTransfer->_update(잔액/공급량) -
위임 전송:
transferFrom-> (안전 검사) ->_unsafeTransferFrom->_spendAllowance->_unsafeUncheckedTransfer->_update -
민팅/소각:
_mint/_unsafeMint또는_burn->_update(한쪽에 영/소각 주소)
사용자 전송, 민팅, 소각 등 모든 토큰 작업이 중앙 _update 함수를 통해 일관된 회계를 유지하는 구조를 살펴보았습니다. 마지막으로 Midnight FungibleToken 컨트랙트의 핵심 사항을 정리합니다.
Conclusion
Midnight의 FungibleToken Compact 컨트랙트는 ERC-20 표준을 프라이버시 관점에서 재설계한 것입니다. 잔액, 전송, 승인 등 익숙한 토큰 인터페이스는 그대로 유지하면서, Compact의 ZK 검증 서킷으로 인코딩하여 비공개이면서도 검증 가능한 실행을 달성합니다. 컨트랙트의 상태와 로직은 설계 단계부터 보호되며, 원시 데이터가 아닌 증명만 블록체인에 기록됩니다.
ERC-20 표준은 디지털 자산의 생성과 관리를 위한 공통 프레임워크를 제공하여 상호운용성을 높이고 탈중앙화 애플리케이션의 성장을 가속했습니다. Midnight의 ERC-20 기반 토큰은 이 검증된 표준을 유지하면서 ZK 프라이버시를 결합하여, 기능성과 기밀성 모두를 필요로 하는 개발자와 사용자에게 친숙하면서도 한 단계 발전된 경험을 제공합니다.
모든 토큰 이동과 잔액이 완전히 투명한 Ethereum의 ERC-20과 달리, Midnight은 선택적 공개를 지원합니다. 사용자와 애플리케이션이 무엇을 공개할지 직접 결정합니다. FungibleToken 컨트랙트는 대체 가능성, 사용성, 프라이버시의 균형을 달성하여, Midnight 기반 프 라이버시 보존 DeFi, 신원 확인, 토큰화 자산의 필수 빌딩 블록 역할을 합니다.
Links
코드, 컨트랙트, 문서를 직접 살펴보세요.
- FungibleToken contract: FungibleToken.compact on Github
- OpenZeppelin documentation: docs.openzeppelin.com
- ERC20 standard: ethereum.org
- Midnight docs: Midnight Developer Documentation
- Do you have any questions?: Midnight Forum