본문으로 건너뛰기
전체 글

nigo-protocol의 EVM 호환: 기존 컨트랙트와 개발 도구를 이어 쓰는 방법

· 약 8분
Bankware Global Engineering

새로운 원장을 선택할 때는 그 위에서 사용할 코드와 도구도 함께 생각하게 된다. 이미 Solidity로 작성한 컨트랙트가 있고, 앱은 익숙한 라이브러리로 잔액을 읽고 거래에 서명하고 있다면, 그 개발 자산을 이어 쓸 수 있다는 것은 큰 이점이다.

nigo-protocol은 자체 원장 구조에 EVM 실행 경로를 두어 이 연결을 제공한다. 실제 SDK 샘플 검증에서는 동일한 Token 바이트코드와 ABI를 유지한 채 ethers·viem·Web3j로 배포·조회·서명 전송·거래 결과 확인까지 수행했다. 필요한 변경은 업무 규칙의 재작성 없이 SDK 설정과 거래 옵션에서 처리했다.

EVM 함수 호출과 상태 변경이 컨트랙트 실행을 원장에 반영하는 구조를 설명했다면, 이번 글은 그 구조가 개발자에게 주는 이점을 살펴본다. 기존 코드와 도구를 이어 쓰는 일이 토큰 전송 앱에서 어떻게 나타나는지 따라가 보자.

원장이 달라져도 개발 경험을 이어 간다

EVM은 컨트랙트 코드를 실행하는 가상 머신이다. Solidity로 작성한 코드를 컴파일하면 EVM이 실행할 바이트코드가 만들어진다. 앱은 ABI라는 약속에 따라 함수 입력을 인코딩하고 반환값을 해석한다. 컨트랙트를 사용하는 앱인 dApp에서 이 둘은 업무 규칙과 호출 방식을 연결한다.

nigo-protocol의 EVM 경로에서는 이 개발 방식을 사용할 수 있다. 컨트랙트는 함수와 저장소로 업무를 표현하고, 앱은 ABI를 이용해 그 함수를 호출한다. 호출 결과를 nigo-protocol의 공통 상태 형식인 StateCell에 반영하는 일은 원장의 실행 계층이 맡는다. 개발자는 EVM 컨트랙트를 호출할 때 셀의 입력·출력 변경안을 직접 구성할 필요가 없다.

클라이언트 도구도 이 연결에 포함된다. SDK는 함수 호출과 거래 작성·서명을 돕는 라이브러리이고, RPC는 SDK가 노드에 조회하거나 거래를 보내는 요청 인터페이스다. 검증에 사용한 버전은 ethers 6.17.0, viem 2.55.19, Web3j 4.10.0이다. 세 SDK는 같은 Token의 ABI와 바이트코드를 사용했고, 각 SDK의 표준 서명 경로로 거래를 만들었다.

이것이 개발자에게 주는 이점은 구체적이다. 검증한 흐름에서 컨트랙트의 토큰 이전 규칙을 유지하면서, 함수 호출과 서명·결과 조회에 익숙한 라이브러리를 계속 사용할 수 있었다. 원장 내부의 상태 표현과 앱이 사용하는 인터페이스 사이를 EVM 호환 계층이 연결한다.

같은 토큰 코드로 전송을 끝까지 이어 간다

주소별 잔액을 저장하는 토큰 컨트랙트 T를 생각해 보자. 같은 코드를 nigo-protocol에 새로 배포하고 A에게 토큰 100, B에게 0이 있는 상태를 준비한다. A가 직접 서명해 transfer(B, 20)을 호출하면 A에게 80, B에게 20이 남는다. 이 수량은 흐름을 설명하기 위한 예제이며, 실제 검증의 전송량을 옮긴 것은 아니다.

여기서는 토큰의 별도 전송 수수료와 발행·소각이 없고, 전송 전후에 다른 거래가 잔액을 바꾸지 않는다고 가정한다. 호출에 첨부하는 기본 코인 수량과 프로토콜 수수료 금액은 0으로 두되 실행량 한도는 적용한다. 새 배포에서 출발하므로 다른 체인의 기존 잔액·이력 이관은 별도의 작업이다.

앱은 기존의 함수 호출·서명·조회 흐름을 따라간다.

같은 T 코드를 새 원장에 배포

현재 잔액 읽기: A=100, B=0

A가 transfer(B, 20)에 서명·전송

확정된 실행 결과와 이전 이벤트 확인

잔액 다시 읽기: A=80, B=20

먼저 앱은 ABI를 통해 T의 balanceOf(A)를 호출하고 토큰 잔액 100을 읽는다. 이 조회는 상태를 바꾸지 않고 함수를 실행하는 eth_call 요청으로 전달된다. 이어서 같은 ABI로 transfer(B, 20)의 입력을 만들고, A의 키로 서명한 거래를 노드에 보낸다.

거래가 확정되면 앱은 실행 결과 기록인 receipt에서 성공 여부와 T의 Transfer(A, B, 20) 이벤트를 확인한다. balanceOf를 다시 호출하면 A=80, B=20을 읽는다. 토큰 합계도 100 + 0 = 80 + 20으로 유지된다. 서명할 요청을 만드는 쪽과 그 결과를 읽는 쪽 모두 같은 컨트랙트 인터페이스를 사용한다.

실제 SDK 샘플에서도 배포, 컨트랙트 조회, 서명 전송, receipt와 이벤트 확인을 하나의 흐름으로 검증했다. 컨트랙트가 실행된다는 사실에 더해 기존 SDK로 요청을 만들고 결과를 해석하는 연결까지 확인한 것이다. 토큰 전송 화면을 구성할 때 필요한 코드와 도구를 재사용할 수 있는 근거가 여기에 있다.

계정 위임과 표준 암호 연산도 지원한다

EVM 앱이 활용하는 기능에는 토큰의 저장소를 읽고 쓰는 것 외에도 계정의 동작을 확장하거나 서명·증명을 검증하는 기능이 있다. nigo-protocol은 이런 컨트랙트를 구성하는 데 필요한 실행 기능도 지원한다.

set_code로 계정에 실행할 코드를 연결한다

set_code는 EIP-7702가 정의한 Type-4 거래다. 키로 서명하는 일반 계정이 승인 서명을 통해 실행할 코드의 주소를 지정하고, 그 계정의 주소와 저장소를 사용해 지정한 코드를 실행하는 방식이다. 계정의 주소를 유지하면서 코드로 동작을 확장할 수 있고, 설정한 위임은 이후 거래에도 유지된다. 새로운 유효한 승인으로 위임 대상을 바꾸거나 해제할 수도 있다.

nigo-protocol은 Type-4 거래의 수신·승인 검증·위임 설정과 위임된 코드 실행을 지원한다. 공식 실행 테스트를 실제 거래 처리 경로에 적용해 상태 변경과 가스, 실행 결과를 대조한 검증 기록도 있다. 개발자는 이 위임 기반 위에서 여러 토큰 전송을 묶는 계정 로직 같은 기능을 설계할 수 있다. 구체적인 지갑의 동작은 위임할 코드와 클라이언트에서 구현한다.

precompile로 기존 암호 연산 호출을 이어 쓴다

precompile은 서명 복구·해시·타원곡선 연산 등을 정해진 주소로 호출하는 EVM의 내장 실행 기능이다. 컨트랙트가 이를 사용하면 정해진 입력 형식으로 데이터를 전달하고 결과를 돌려받는다.

nigo-protocol은 Ethereum 실행 규칙의 버전인 Osaka를 기준으로 표준 precompile 목록을 모두 구현한다. 서명 복구인 ecrecover, SHA-256 같은 해시 연산, MODEXP와 BN254 연산에 더해 KZG point evaluation, BLS12-381 연산, P-256 서명 검증도 제공한다. 이 기능을 사용하는 컨트랙트는 기존 표준 주소와 입력 형식으로 암호 연산을 호출하는 코드를 이어 쓸 수 있다.

검증에서는 연산 결과뿐 아니라 가스 계산과 잘못된 입력의 처리도 확인한다. 공식 테스트 자료를 이용한 실행 계층 검증과 함께, 컨트랙트 내부 호출·최상위 거래·RPC 조회 경로에서 precompile이 동작하는지도 검사한다. 토큰 샘플의 SDK 검증에 더해, 서명과 암호학적 증명을 사용하는 앱을 위한 실행 기반도 갖춘 것이다.

업무 규칙을 유지하고 연동 설정을 맞춘다

원장을 옮기면 RPC 접속 주소, 체인 ID, 서명자와 배포 주소를 목적지에 맞게 설정한다. SDK가 수수료를 정하거나 결과를 다시 조회하는 방식에도 해당 원장의 정책을 반영한다. 검증한 샘플에서는 이런 조정으로 같은 컨트랙트의 업무 규칙을 유지할 수 있었다.

이 변경 수준을 LOW_CHANGE로 분류한다. 컨트랙트와 업무 로직을 유지하면서 SDK 초기화·거래 옵션 같은 연동 부분을 조정했다는 뜻이다. 접속 정보와 배포 설정만 바꾸는 Config-only와 구분해, 실제로 손댄 범위를 드러낸다. 검증에서 확보한 성과는 업무를 다시 구현할 부담을 줄이고 조정할 부분을 연동 설정에 모았다는 데 있다.

수수료 설정이 한 예다. 가스는 실행에 쓰는 자원을 계량하는 단위이며, nigo-protocol의 ZERO 정책은 수수료 금액을 0으로, FIXED 정책은 가스당 단가를 고정한다. 두 정책 모두 실행량 한도를 적용한다.

검증에 사용한 Type-2 거래에는 가스당 수수료 상한과 가스당 우선 수수료 상한을 명시했다. ZERO에서는 두 값 모두 0으로, 가스당 단가가 P인 FIXED에서는 각각 P와 0으로 설정했다. 이는 nigo-protocol의 수수료 정책에 맞춰 SDK가 작성할 거래 옵션을 지정하는 작업이다.

예제의 transfer(B, 20)과 잔액을 갱신하는 T의 코드는 그대로다. 목적지 원장의 수수료 정책을 거래 작성 부분에 반영하면서, 토큰 20을 이전하는 업무 규칙은 재사용한다. 이처럼 유지하는 코드와 조정하는 설정을 구분할 수 있다는 것이 이전 작업을 구체적으로 계획하게 해 준다.

재시작 뒤의 조회까지 연결을 확인했다

사용자가 전송 결과를 확인한 뒤 앱을 다시 열어도 거래 내역과 잔액을 읽을 수 있어야 한다. SDK 검증은 노드 재시작 후의 조회까지 포함했다.

ZERO와 FIXED 각각의 실행에서 새 파일 기반 DB를 사용해 샘플 거래를 처리한 다음, 노드를 종료하고 같은 DB로 다시 시작했다. 재시작 후에는 새 거래를 넣지 않고 기존 거래·receipt·블록·로그와 컨트랙트 상태를 다시 읽었다. 보존된 결과를 비교해 재시작 전후에 일치하는지 확인했다.

이는 SDK에서 시작한 요청의 결과가 저장되고, 같은 DB를 다시 열었을 때도 조회된다는 근거다. 검증한 범위는 같은 버전의 노드가 보존한 결과를 재조회하는 흐름이며, 과거 버전 DB를 새 형식으로 바꾸는 작업은 별도 검증 대상이다.

코드 재사용, 표준 서명과 요청, 저장 후 재조회가 이어지면 앱 개발자는 원장의 내부 저장 형식에 직접 접근하지 않고도 사용자에게 전송 결과를 보여줄 수 있다.

앱에 필요한 조회 범위를 맞춘다

토큰 전송 예제는 현재 잔액과 확정된 거래 결과를 사용한다. 현재 nigo-protocol의 eth_call은 합의로 확정된 상태를 읽으며, latest, safe, finalized를 지정하거나 태그를 생략하면 같은 현재 확정 상태를 사용한다. receipt와 이벤트 로그도 확정된 거래 결과를 기준으로 제공한다.

앱에 “지난달 말의 잔액” 화면을 추가한다면 조회 요구가 달라진다. 현재 RPC는 과거의 확정 블록과 이벤트 로그를 조회할 수 있지만, 임의의 과거 블록 번호를 지정해 그때의 상태에서 eth_call을 실행하는 기능은 제공하지 않는다. 이런 화면은 과거 상태 조회의 지원 범위와 이력 데이터 구성을 함께 검토해야 한다.

따라서 실제 이전에서는 앱이 사용하는 함수·거래 형식·조회 시점을 목록으로 정하고, 사용할 SDK 버전으로 그 흐름을 확인하면 된다. 앞서 확인한 세 SDK의 토큰 샘플은 그 출발 근거이며, 다른 컨트랙트의 기능이나 브라우저 지갑의 연결·승인 화면은 해당 앱의 요구에 맞춰 추가로 검증한다. set_code에 필요한 Type-4 거래 작성·서명은 검증에 사용한 ethers·viem에서 확인했다. Web3j 4.10.0은 이 작성·서명 기능을 제공하지 않으므로, 활용할 거래 형식에 맞춰 SDK를 선택한다.

기존 개발 자산 위에서 새 원장을 사용한다

nigo-protocol의 EVM 호환은 기존 개발 경험을 이어 갈 수 있는 접점을 만든다. 검증한 토큰 흐름에서는 같은 바이트코드와 ABI, 익숙한 SDK의 서명·조회 기능을 사용했고, 필요한 변경은 업무 로직을 유지한 채 연동 설정과 거래 옵션에서 처리했다.

컨트랙트와 호출 코드를 쌓아 온 개발자에게는 그 자산을 다시 활용할 수 있는 기반이다. StateCell을 사용하는 원장 내부의 설계와, Solidity·EVM 도구로 앱을 만드는 개발 경험을 함께 가져갈 수 있다. 여기에 계정 위임과 표준 precompile 지원이 더해져, 재사용할 수 있는 개발 방식은 단순한 토큰 전송을 넘어선다. 같은 코드와 도구로 앱을 만들고, 필요한 연동 설정을 맞춰 새 원장에 연결하는 것이 이 호환성이 제공하는 가치다.

Direct Cell, Native Program, EVM의 특징을 살펴보고 나면, 이들이 하나의 업무 안에서 어떻게 함께 쓰일 수 있는지가 궁금해진다. 서로 다른 실행 방식이 같은 자산과 업무 흐름으로 연결될 때, 하나의 원장을 공유하는 가치는 더욱 구체적으로 드러난다. EVM 컨트랙트에서 Native 자산을 다루려면 어떤 경로를 거치고, 각 영역의 권한과 검증 규칙은 어떻게 지켜질까? 조만간 후속 글에서 이러한 연계 구조와, 실행 도중 실패했을 때 관련 변경을 함께 되돌리는 방법을 살펴보려 한다.