신규: Helius가 Light Protocol을 인수했습니다
Ethereum에서 Solana로 마이그레이션하는 방법: 개발자 가이드
블로그/연구

Ethereum에서 Solana로 마이그레이션하는 방법: 개발자 가이드

Developer Experience EngineerX의 0xIchigoLinkedIn의 0xIchigoGitHub의 0xIchigo
읽는 데 41분

이 글에서는 무엇을 다루나요?

Ethereum은 최근 가장 중요한 혁신 중 하나입니다. 역사상 처음으로 여러 산업을 혁신할 잠재력을 지닌, 사회적 조정을 위한 탈중앙화 글로벌 플랫폼이 등장했습니다. 하지만 Ethereum의 런타임 환경인 Ethereum Virtual Machine(EVM)은 현재 상태로는 소비자용 애플리케이션에 적합하지 않습니다. 단일 스레드 기반의 가스 네트워크이며 수수료 변동성이 큽니다. 반면 Solana는 높은 처리량과 낮은 지연 시간을 제공하는 네트워크입니다. 낮고 예측 가능한 수수료와 병렬화된 인프라를 제공합니다. EVM의 한계를 직접 해결하고 기존 설계를 개선하므로, 확장성과 효율성을 갖춘 애플리케이션을 구축하려는 개발자에게 매력적인 선택지입니다.

이 글은 Solana에서 개발하려는 EVM 개발자를 위한 종합 마이그레이션 가이드입니다. Ethereum과 Solana의 합의 메커니즘, 트랜잭션 처리 방식, 스마트 컨트랙트 개발 언어 등 두 네트워크의 근본적인 차이를 다룹니다. 이어서 계정에 대해 더 일관되고 다면적인 접근 방식을 제시하는 Solana의 계정 모델을 살펴봅니다. Solana 개발 경험을 개선하는 Solidity 친화적 도구인 Solang과 Neon EVM도 소개합니다.

근본적인 차이

이 섹션에서는 글로벌 조정을 위한 탈중앙화 상태 머신을 목표로 하는 두 블록체인, Ethereum과 Solana의 핵심 차이를 살펴봅니다. 두 네트워크는 합의 메커니즘, 트랜잭션 처리 방식, 스마트 컨트랙트 개발 언어에서 큰 차이를 보입니다. 이러한 근본적인 차이를 깊이 이해하면 고성능 글로벌 상태 머신으로서 Solana가 Ethereum보다 어떤 차별화된 이점을 갖는지 알 수 있습니다.

합의 메커니즘

합의 메커니즘은 모든 블록체인 네트워크의 기반입니다. 트랜잭션을 검증하고 블록을 안전하고 효율적이며 탈중앙화된 방식으로 블록체인에 추가하는 방법을 결정합니다. Ethereum과 Solana는 모두 지분 증명(PoS) 네트워크입니다. PoS라는 공통 기반을 사용하지만, 확인 규칙이 다르기 때문에 합의에 도달하는 방식도 다릅니다.

Ethereum은 Casper the Friendly Finality Gadget(Casper-FFG)과 LMD-GHOST 포크 선택 알고리즘을 결합한 Gasper를 사용합니다. 이 조합이 Ethereum을 보호하는 합의 메커니즘을 구성합니다.

Casper는 블록의 확인 상태를 “확정”으로 높이는 PoS 기반 확정성 시스템입니다. 전체 스테이킹 물량의 3분의 2가 해당 블록의 포함에 찬성하고 그 위에 다른 블록이 구축되면, Ethereum에서 해당 블록은 확정된 것으로 간주됩니다. 확정성을 얻으려면 전체 스테이킹 물량의 3분의 2가 해당 블록이 정식 체인에 속한다는 데 동의해야 합니다. 따라서 공격자는 전체 스테이킹 물량의 3분의 2를 소유하거나 조작하고, 슬래싱을 통해 최소 3분의 1을 잃지 않는 한 대체 확정 체인을 만들 수 없습니다. Casper 덕분에 신규 참여자는 정식 체인과 확실하게 동기화할 수 있습니다. 

LMD-GHOST는 “Latest Message-Driven Greedy Heaviest Observed Sub-Tree”의 약자입니다. Ethereum에서 어떤 포크를 따를지 결정하는 알고리즘입니다. 네트워크 검증인으로부터 가장 많은 지지, 즉 가장 큰 “가중치”를 받은 포크를 선택합니다. 이것이 “greedy heaviest sub-tree”라는 이름의 유래입니다. 그런 다음 각 검증인의 가장 최근 메시지만 고려합니다. Ethereum에 새 블록이 제안될 때마다 검증인은 이 규칙을 사용해 해당 블록을 정식 체인에 포함할지 결정합니다.

반면 Solana는 Proof of History(PoH) 해시를 사용해 합의에 도달합니다. 이름 때문에 혼동하기 쉽지만 PoH는 합의 알고리즘이 아닙니다. 적대적 네트워크에서 시간을 증명하는 방법입니다. 더 구체적으로는 노드가 서로 통신하지 않고도 이벤트 순서에 합의할 수 있게 하는 암호학적 타임스탬프 함수입니다. 리더, 즉 원장에 항목을 추가하는 검증인은 블록에 타임스탬프를 적용해 이전 블록 이후 일정 시간이 지났음을 증명합니다. 

이 타임스탬프는 데이터가 특정 시점에 존재했음을 증명해 과거 기록을 만듭니다. 과거 기록은 순차적인 프리이미지 저항성 해시 함수를 사용해 생성되며, 각 해시는 이전 해시에 의존합니다. 검증 가능한 지연 함수(VDF)는 이러한 해시 생성에서 핵심 역할을 합니다. 각 해시가 이전 해시를 기반으로 만들어지고 그 이후 경과한 시간을 포함하도록 보장합니다. VDF를 통합하면 해싱 프로세스에 시간 차원이 추가되어 Solana에서 타임스탬프가 지정된 이벤트의 검증 가능한 시퀀스를 만들 수 있습니다.

Solana의 Proof of History 메커니즘이 암호학적 타임스탬프로 신뢰할 수 있는 이벤트 시퀀스를 만드는 방식을 살펴봤습니다. 이제 이것이 Solana의 합의 메커니즘인 Tower BFT와 어떻게 통합되는지 이해해야 합니다. Tower Byzantine Fault Tolerance(BFT)는 기존 비잔틴 장애 허용 합의 모델을 PoH에 맞게 최적화한 Solana의 변형입니다. Tower BFT는 PoH가 만든 과거 기록을 참조 프레임워크로 사용합니다. 이 프레임워크를 통해 검증인은 원장 상태에 효율적이고 정확하게 투표할 수 있습니다. Tower BFT에서는 검증인의 투표가 오랫동안 잠기며, 투표가 추가될수록 확정 수준이 높아집니다. PoH 덕분에 검증인은 서로 통신하지 않고도 원장 상태에 대해 더 빠르고 정확한 결정을 내릴 수 있습니다. PoH와 Tower BFT를 결합하면 합의 속도가 빨라지고 네트워크의 보안과 신뢰성이 향상됩니다. 그 결과 트랜잭션을 빠르게 확정하는 높은 처리량의 확장 가능한 블록체인 환경이 구현됩니다.

트랜잭션 처리 및 실행 환경

블록체인의 효율성과 확장성은 주로 트랜잭션 처리 역량과 실행 환경에 따라 결정됩니다. 이러한 요소는 트랜잭션 실행 속도와 네트워크 운영의 비용 효율성을 좌우합니다. 블록체인의 트랜잭션 처리 방식과 실행 환경의 세부 특성은 개발자 경험에 큰 영향을 미칩니다.

Ethereum의 런타임 환경은 Ethereum Virtual Machine(EVM)이라는 결정론적 단일 스레드 스택 머신으로 작동합니다. 수학 함수처럼 동작합니다. 즉, 입력이 주어지면 EVM은 결정론적 출력을 생성합니다. Ethereum에는 상태 전이 함수가 있다고 정의하면 이해하기 쉽습니다. f(S, T) = S’. 이전 상태(S)와 새로운 유효 트랜잭션 집합(T)이 주어지면 EVM은 새로운 유효 출력 상태(S’)를 생성합니다. 동일한 트랜잭션 집합이 주어지면 EVM은 언제나 같은 최종 상태에 도달합니다. 이는 네트워크 노드 간 일관성을 유지하는 데 매우 중요합니다.

EVM은 트랜잭션을 순차적으로 처리합니다. 순차 처리를 통해 각 트랜잭션은 해당 시점까지의 네트워크 상태를 정확히 반영하는 환경에서 실행됩니다. 순차 실행은 정밀한 상태 변경과 가스 비용 계산을 가능하게 합니다. 이러한 예측 가능성 덕분에 개발자와 사용자는 트랜잭션 결과와 비용을 명확히 파악할 수 있습니다.

Ethereum은 단일 스레드 실행 모델을 채택해 스마트 컨트랙트 실행 환경을 단순화합니다. 각 트랜잭션이 순차적으로 처리되므로 개발자는 상태 변경을 예측할 수 있습니다. 실행의 복잡성보다 스마트 컨트랙트 로직에 집중할 수 있어, 예측 가능한 상태 변경은 신규 개발자가 Ethereum 개발 환경에 더 쉽게 접근하도록 돕습니다. 하지만 이러한 단순성은 확장성을 어렵게 만듭니다. 순차 처리는 네트워크 처리량을 제한합니다. 수요가 많을 때 혼잡이 발생하고 트랜잭션 대기 시간이 길어지며 가스 수수료가 높아질 수 있습니다. 모든 트랜잭션이 가스를 소비하므로 개발자는 가스 효율성을 최적화해야 합니다. 비효율적인 코드가 일반 사용자에게 앱을 사실상 사용할 수 없게 만들 수 있는 혼잡 상황에 대응하고 전반적인 사용자 경험을 개선하려면 최적화가 필요합니다.

Solana 개발자는 Ethereum 개발자처럼 가스 사용량 최적화를 걱정할 필요가 없습니다. Solana는 Solana Virtual Machine(SVM)이라는 높은 처리량과 낮은 지연 시간의 상태 머신으로 설계되었습니다. SVM의 핵심 구성 요소는 Sealevel입니다. 트랜잭션을 병렬로 처리하는 런타임 엔진입니다. Solana에서 각 트랜잭션은 실행 시 상태의 어느 부분을 읽거나 쓸지 런타임에 알립니다. 런타임은 충돌하지 않는 트랜잭션과 동일한 상태를 읽는 트랜잭션을 병렬로 처리합니다. Sealevel은 트랜잭션 워크로드를 검증인 하드웨어의 여러 스레드에 분산해 스마트 컨트랙트 실행을 최적화합니다. 따라서 검증인이 한 코어에서 트랜잭션을 처리하는 동시에 다른 코어에서 또 다른 트랜잭션을 처리할 수 있습니다.

Solana의 기본적인 병렬성은 트랜잭션 비용을 크게 낮춥니다. Solana 트랜잭션에는 기본 수수료와 우선순위 수수료라는 두 가지 수수료가 있습니다. 기본 수수료는 서명당 5,000 lamport로 고정되며, 대부분의 트랜잭션에는 서명이 하나만 있습니다. 우선순위 수수료는 선택 사항이며 다른 트랜잭션보다 우선 처리되도록 할 수 있습니다. 우선순위 수수료가 높은 트랜잭션은 스케줄러에 의해 비결정론적으로 우선 처리됩니다. 트랜잭션 수수료는 흔히 $0.001 USD 미만이며, 투표를 제외한 평균 수수료는 0.000005~0.00007 SOL입니다. 최근 우선순위 수수료 사용이 늘어 상한은 평소보다 높아졌습니다. 현재 SOL 가격인 ~$98.96를 기준으로 하면 이 수수료는 약 $0.000494~$0.006968 USD입니다. 

이러한 수수료는 예측할 수도 있습니다. Solana는 수요를 관리하기 위해 지역화된 수수료 시장을 사용합니다. 특정 활동의 핫스폿이 블록 공간을 장악하고 네트워크 전체의 수수료를 높이지 못하도록 블록 공간이 구성됩니다. 예를 들어 화제가 된 NFT 민팅이 이에 해당합니다. 수요가 높은 특정 핫스폿에 접근하려는 트랜잭션의 수수료만 오릅니다. 지역화된 수수료 시장은 전면적인 가스 전쟁을 일으키지 않으면서 우선순위 수수료를 지원합니다. 이는 트랜잭션을 순차 처리하고 전역 혼잡으로 수수료가 크게 변동하는 Ethereum 같은 가스 기반 네트워크와 다릅니다.

Ethereum은 한 번에 하나의 컨트랙트만 처리하는 단일 스레드 런타임 환경입니다. 현재 최신 멀티코어 하드웨어를 활용하지 않아 검증인 하드웨어의 성능이 충분히 사용되지 않습니다. 노드의 하드웨어 요구 사항을 낮게 유지하려는 목표도 Ethereum의 트랜잭션 처리 한계를 키웁니다. 반면 Solana는 병렬 처리 기능을 통해 검증인이 사용할 수 있는 모든 코어를 활용하여 더 많은 트랜잭션을 처리합니다. 지역화된 수수료 시장까지 결합한 Solana는 훨씬 더 뛰어난 성능의 글로벌 상태 머신입니다. 

스마트 컨트랙트 언어

Ethereum 스마트 컨트랙트 개발의 주요 언어는 Solidity입니다. EVM에서 실행되는 스마트 컨트랙트를 만들도록 설계된 정적 타입의 중괄호 기반 언어입니다. JavaScript, C++, Python의 영향을 크게 받아 이러한 언어에 익숙한 개발자는 쉽게 배울 수 있습니다.

Yul은 EVM 호환 블록체인에 최적화된 중간 수준의 저수준 언어입니다. Yul은 개발자에게 바이트코드 실행을 더 세밀하게 제어할 수 있는 기능을 제공하므로 가스 소비를 미세 조정하고 기타 저수준 작업을 관리하는 데 매우 효율적입니다. 개발자는 Yul로 직접 코딩하거나 이를 컴파일 대상으로 사용해 더 효율적인 컨트랙트를 만들 수 있습니다.

Vyper는 단순성과 보안을 강조하는 인기 있는 Python 스타일 언어입니다. 복잡성과 잠재적인 보안 취약성을 줄이기 위해 의도적으로 Solidity보다 적은 기능을 제공합니다. Vyper의 설계 철학은 무엇보다 가독성과 감사 용이성을 중시합니다. 이 접근 방식은 Fe 같은 유사 언어와 Dasy처럼 Vyper로 컴파일되는 언어의 개발에 영향을 주었습니다.

Rust는 Solana에서 스마트 컨트랙트, 일반적으로 프로그램이라 부르는 것을 개발할 때 사용하는 공용어입니다. C++에 견줄 만한 성능을 제공하는 빠르고 메모리 효율적인 언어입니다. 이러한 특성 덕분에 높은 처리량과 낮은 지연 시간의 네트워크용 애플리케이션 개발에 적합합니다. Rust의 풍부한 타입 시스템과 소유권 모델은 메모리와 스레드 안전성을 보장하며, 개발자가 신뢰할 수 있고 안전한 애플리케이션을 구축하도록 합니다. 

시스템 프로그래밍을 처음 접하는 사람에게 Rust의 학습 곡선은 가파르지만, 그 대신 견고하고 효율적인 코드를 만들 수 있습니다. 대부분의 Rust 개발에는 Anchor가 사용됩니다. Anchor는 반복 코드를 줄이고 다양한 표준 보안 검사를 수행하며 직렬화와 역직렬화 프로세스를 간소화하는 독자적인 프레임워크입니다. 따라서 시작 단계에서 고급 Rust 지식의 중요성이 줄어듭니다. 참고로 Anchor 문서는 사용자가 Rust Book의 첫 9개 장, 즉 Rust의 기본 사항을 숙지할 것을 권장합니다. Solana Playground에는 시작에 도움이 되는 여러 Anchor 튜토리얼이 있습니다.

Rust가 선호되지만 개발자가 Rust만 사용해야 하는 것은 아닙니다. C, C++, 그리고 LLVM의 BPF 백엔드를 대상으로 하는 모든 언어, 즉 BPF 바이트코드로 컴파일할 수 있는 모든 언어를 사용할 수 있습니다. Vyper 같은 Python 스타일 언어에 익숙하다면 Seahorse Lang을 사용해 Python으로 프로그램을 작성할 수 있습니다. 개발자는 Python의 사용 편의성을 누리면서도 Rust로 코딩할 때와 동일한 안전성을 유지할 수 있습니다. Seahorse University와 Seahorse Cookbook 등 유용한 Seahorse 튜토리얼을 활용해 지금 바로 Solana 프로그래밍을 시작할 수 있습니다.

Ethereum에서 마이그레이션하는 개발자가 Rust만 사용해야 하는 것은 아닙니다. 인기와 보안성을 고려하면 Anchor와 Seahorse 같은 프레임워크를 배우고 사용하는 것이 좋지만, 항상 가능한 것은 아닙니다. Solang과 Neon Labs가 최근 선보인 컴파일러와 EVM 호환 개발 환경 덕분에 개발자는 프로그램 개발에 Solidity를 사용할 수 있습니다. Solidity는 Solana에서도 사용할 수 있습니다. Solidity 프로그램 개발을 시작하려면 Solana 프로그래밍 모델의 세부 사항을 더 깊이 살펴봐야 합니다. Solana의 계정 모델은 네트워크에서 프로그램을 개발하고 상호작용하는 기반이므로, 마이그레이션하려는 개발자는 이를 반드시 이해해야 합니다.

계정 모델 이해하기

Ethereum은 계정을 외부 소유 계정(EOA)과 컨트랙트 계정이라는 두 가지 주요 범주로 구분합니다. EOA는 사용자를 위한 표준 계정 유형입니다. 개인 키로 제어하며 Ether 잔액을 보유하고 트랜잭션을 전송하거나 컨트랙트 계정과 상호작용할 수 있습니다. 반면 컨트랙트 계정은 내부에 포함된 스마트 컨트랙트 코드에 따라 작업이 제어된다는 점에서 다릅니다. 자체적으로 트랜잭션을 시작할 수 없으며 EOA 또는 다른 컨트랙트 계정에서 받은 트랜잭션에 대한 응답으로만 작동할 수 있습니다.

Solana는 계정을 데이터를 영구적으로 저장하는 다면적 컨테이너로 취급하는 더 일관된 계정 모델을 채택합니다. 이 모델에서는 모든 계정이 프로그램이 될 수 있어 계정과 스마트 컨트랙트의 전통적인 경계가 흐려집니다. 코드와 상태를 하나의 계정에 결합하는 Ethereum과 달리 Solana 프로그램은 상태 비저장입니다. 즉, 내부에 상태를 저장하지 않습니다. 대신 작동에 필요한 모든 데이터는 별도의 계정에 저장되며 트랜잭션과 함께 참조로 전달됩니다. 계정을 참조로 전달하면 하나의 범용 프로그램 배포가 여러 계정과 상호작용할 수 있습니다. Solana의 계정 모델은 코드와 데이터를 분리해 더 효율적이고 모듈화된 개발 환경을 조성합니다. 여러 프로그램 간에 자산을 옮기지 않고 다양한 프로토콜과 상호작용하려는 사용자에게 유리합니다. Solana에서는 모든 것이 계정입니다. 

Solana 계정은 실행 가능 계정과 실행 불가능 계정으로 구분할 수 있습니다. 간단히 말해 실행 가능 계정은 코드를 실행할 수 있는 계정입니다. 실행 불가능 계정은 코드를 실행할 수 없으며 데이터 저장에 사용됩니다. 코드를 저장하지 않기 때문입니다.

실행 가능 계정은 프로그램을 보유하는 계정입니다. 프로그램은 추가 계정을 소유하고, 다른 계정에서 데이터를 읽거나 금액을 입금하고, 데이터를 수정하거나 자신이 소유한 계정에서 금액을 출금할 수 있습니다. 실행 가능 계정은 다시 온체인 프로그램과 네이티브 프로그램으로 나눌 수 있습니다. 온체인 프로그램은 사용자가 작성해 네트워크에 배포한 코드입니다. 일반적으로 프로그램을 배포한 계정인 업그레이드 권한이 프로그램을 업그레이드할 수 있습니다. 네이티브 프로그램은 Ethereum의 사전 컴파일된 컨트랙트와 유사하지만 더 광범위한 기능을 제공하는 특별한 실행 가능 계정입니다. 이러한 프로그램은 Solana 코어에 통합되어 검증인 운영에 필요한 기능을 제공합니다. 네이티브 프로그램의 예로 System Program이 있습니다. 이 프로그램은 새 계정 생성, 계정 데이터 할당, 프로그램에 계정 배정, 자신이 소유한 계정에서 lamport 전송, 트랜잭션 수수료 지불을 담당합니다. 네이티브 프로그램의 전체 목록은 여기에서 확인할 수 있습니다.

실행 가능 여부와 관계없이 모든 계정에는 다음과 같은 필드가 있습니다. 

  • 계정의 SOL 네이티브 잔액을 추적하는 lamports 필드
  • 계정에 저장된 원시 데이터 바이트 배열을 나타내는 데이터 필드
  • 이 계정을 수정할 수 있는 프로그램을 나타내는 소유자 필드
  • 트랜잭션에서 계정이 트랜잭션을 승인할 수 있는지 나타내는 서명자 필드
  • 계정 데이터를 수정할 수 있는지 지정하는 쓰기 가능 필드. 트랜잭션에 포함된 계정을 읽기 전용 또는 쓰기 가능으로 표시해 병렬 처리를 지원합니다
  • 계정이 프로그램을 저장하는지 나타내는 실행 가능 필드
  • 계정이 다음 임대료를 지불해야 하는 에포크를 나타내는 임대 에포크 필드

임대료는 Solana에서 계정을 유지하고 검증인 메모리에 보관하기 위한 스토리지 비용입니다. 계정이 활성 상태를 유지하려면 최소 잔액을 보유해야 합니다. 임대료는 사용되지 않거나 자금이 부족한 계정을 네트워크가 최종적으로 회수하도록 해 상태 팽창을 줄입니다. 최근 업데이트에 따라 메인넷에는 임대료를 지불하는 계정이 없습니다. 대신 모든 계정은 생성 시 임대료 면제 상태여야 합니다. 2년치 임대료에 해당하는 최소 잔액을 유지하면 임대료가 면제됩니다. Test Drive와 Solana CLI의 rent 하위 명령 같은 도구로 계정의 임대료 면제에 필요한 SOL을 추정할 수 있습니다. 이는 명시적으로 삭제하지 않는 한 스토리지가 유지되는 Ethereum의 리소스 할당 시스템과 다릅니다. Solana의 접근 방식은 상태 팽창을 줄이면서 상태 스토리지에 더 예측 가능한 비용 구조를 제공합니다.

주소와 프로그램 파생 주소(PDA)

모든 계정은 고유한 32바이트 공개 키인 주소로 식별됩니다. 주소가 20바이트인 Ethereum의 데이터 모델과 다릅니다. Solana는 ed25519, 즉 SHA-512와 Curve22519 타원 곡선을 사용하는 EdDSA 서명 체계로 주소를 생성합니다. 계정에 유효한 키 쌍이 있으려면 ed25519 곡선 위의 점이어야 합니다. 

프로그램 파생 주소(PDA)는 bump, 즉 출력을 곡선 밖으로 밀어내는 값을 사용해 곡선 밖에서 생성되는 계정입니다. PDA에는 상위 프로그램의 주소, seed 집합, bump라는 세 가지 주요 요소가 필요합니다. seed는 임의의 값을 가질 수 있는 문자열 배열입니다. 하지만 대부분의 개발자는 해시맵과 유사한 데이터 구조를 만들기 위해 상위 프로그램의 상태 변수와 관련된 특정 seed를 생성합니다. 따라서 PDA는 SHA-512 해시 함수로 프로그램 ID, seed, bump를 해싱해 생성합니다.

PDA를 곡선 밖에 두는 이유는 해당 PDA가 파생된 프로그램만 PDA를 대신해 서명할 수 있게 하기 위해서입니다. 트랜잭션 서명을 프로그래밍 방식으로 생성해 트랜잭션 흐름을 간소화하므로, 신뢰가 필요 없는 dApp이 개입 없이 원활하게 작동할 수 있습니다.

계정과 상호작용하기

Ethereum 트랜잭션은 EOA가 시작하는 상태 변경 작업입니다. 스마트 컨트랙트는 독립적으로 트랜잭션을 시작할 수 없으며 트랜잭션에 반응만 할 수 있습니다. 컨트랙트의 코드가 이러한 반응을 결정하며, 트랜잭션 실행 비용은 가스로 측정됩니다. 사용자는 이 가스 수수료를 충당할 만큼의 Ether를 제공해야 합니다.

Ethereum 트랜잭션은 일반적으로 특정 스마트 컨트랙트 함수를 호출하는 것과 같은 단일 작업을 수행합니다. 하나의 트랜잭션이 컨트랙트 내에서 여러 상태 변경을 일으킬 수 있지만, 모든 변경은 해당 단일 컨트랙트 호출 범위로 제한됩니다.

Solana에서 트랜잭션은 읽거나 쓸 계정 배열, 하나 이상의 서명, 하나 이상의 명령어로 구성됩니다. 명령어는 단일 프로그램 호출을 위한 지시입니다. 실행 로직의 최소 단위이자 가장 기본적인 작업 단위입니다. 명령어는 실행할 프로그램, 관련된 모든 계정, 작업 데이터를 지정합니다. 프로그램은 명령어의 데이터를 해석하고 지정된 계정에서 작업을 수행합니다. 이 구조 덕분에 하나의 트랜잭션이 여러 프로그램에서 일련의 작업을 원자적으로 수행할 수 있습니다. 즉, 모든 명령어가 함께 성공하거나 함께 실패합니다. Solana의 트랜잭션 흐름은 트랜잭션이 일반적으로 단일 스마트 컨트랙트 또는 EOA 작업에 연결되는 Ethereum 모델과 다릅니다.

프로그램 간 호출(CPI)을 사용하면 한 프로그램이 동일한 트랜잭션 중 다른 프로그램을 호출할 수 있습니다. CPI 중 컴퓨트 유닛당 부과되는 계정 데이터 바이트 수는 250입니다. 200,000유닛 기준으로 약 50MB입니다. CPI를 사용하면 프로그램 간 호출 시 프로그래밍 방식으로 생성된 서명을 사용할 수 있습니다. 이는 Ethereum 스마트 컨트랙트가 다른 컨트랙트를 효율적이고 원자적으로 호출하는 것과 유사합니다. 다만 호출된 프로그램이 명령어 처리를 마칠 때까지 호출한 프로그램은 중단됩니다. 재진입은 고정 깊이 4로 제한된 직접 자기 재귀만 허용됩니다. 이를 통해 프로그램이 나중에 자신이 다시 호출될 수 있다는 사실을 모르는 채 중간 상태에서 다른 프로그램을 호출하는 상황을 방지합니다.

Solana 트랜잭션은 Ethereum의 가스 한도와 마찬가지로 효율성을 위해 크기 제한을 따릅니다. 다만 데이터 크기에 초점을 맞춥니다. Solana 트랜잭션은 안정적인 데이터 전송을 보장하기 위해 IPv6 최대 전송 단위(MTU) 표준을 따릅니다. 필요한 헤더 공간을 제외하면 패킷 데이터에 1,232바이트를 사용할 수 있습니다. Solana는 크기 제약을 극복하고 여러 트랜잭션 형식을 지원하기 위해 버전 지정 트랜잭션을 도입했습니다. 레거시 형식, 즉 원래 트랜잭션 형식에 더해 주소 조회 테이블(ALT)을 지원하는 Version 0이 출시되었습니다. ALT는 각 주소를 1바이트 u8 인덱스로 색인한 테이블 형태의 데이터 구조에 주소를 온체인으로 저장합니다. 계정마다 32바이트가 아닌 1바이트만 필요하므로 트랜잭션 크기가 크게 줄어듭니다.

Solang

Solang은 Solana와 Polkadot을 위한 Solidity 컴파일러입니다. 몇 가지 차이는 있지만 Solana와 Polkadot의 아키텍처에 맞추면서 EVM Solidity 컴파일러 버전 0.8과 소스 파일 호환성을 확보하는 것이 목표입니다. Solang의 중요한 특징은 강력한 컴파일러 인프라인 LLVM을 사용한다는 점입니다. LLVM의 유연성 덕분에 Solang은 향후 다른 프로그래밍 언어까지 지원을 확장하여 구현과 컴파일을 간소화할 수 있습니다. 이는 개발자가 Solana 또는 Polkadot으로 쉽게 전환하고 Solidity 개발 범위를 넓히려는 Solang의 목표와도 일치합니다. Solang은 호환성, 효율성, 사용 편의성 향상에 초점을 두고 지속적으로 개발되고 있습니다. 자신의 역량을 크로스체인으로 확장하려는 Ethereum 개발자라면 Solang과 그 주의 사항을 이해해야 합니다.

설치

Solang을 설치하는 방법은 몇 가지가 있습니다. Mac에서는 비공개 tap을 사용해 Brew로 Solang을 다운로드할 수 있습니다.

코드
brew install hyperledger/solang/solang

Solang 컨테이너를 사용하는 방법도 있습니다. 새 이미지가 이 컨테이너에 자동으로 제공되므로 Docker 사용을 선호한다면 적합합니다. v.0.3.3 태그와 latest 태그가 있습니다.

코드
docker pull ghcr.io/hyperledger/solang:latest

Solang을 쉽게 설치하고 사용하는 방법 중 하나는 Anchor를 이용하는 것입니다. 먼저 시스템에 Rust와 Node.js가 설치되어 있는지 확인하세요. Windows 사용자는 Windows Subsystem for Linux도 설정해야 합니다. 이제 Solana 도구 모음을 설치해야 합니다. Mac과 Linux에서는 다음 명령어로 설치할 수 있습니다.

코드
 sh -c "$(curl -sSfL https://release.solana.com/v1.17.13/install)"

다른 소프트웨어 버전을 원한다면 “v1.17.13”을 적절한 릴리스 태그로 바꾸세요. 또는 stable, beta, edge 중 하나의 심볼릭 채널 이름을 사용할 수 있습니다. 시스템에 따라 PATH 환경 변수를 업데이트해야 할 수도 있습니다. 이 메시지가 표시되면 아래의 권장 명령어를 복사해 붙여 넣어 PATH를 업데이트하세요. solana --version을 실행하면 원하는 solana 버전인지 확인할 수 있습니다.

Windows에서는 명령 프롬프트를 관리자 권한으로 여세요. 다음 명령어를 복사해 붙여 넣어 Solana 설치 프로그램을 임시 디렉터리에 다운로드하세요.

코드
cmd /c "curl https://release.solana.com/v1.17.13/solana-install-init-x86_64-pc-windows-msvc.exe --output C:\solana-install-tmp\solana-install-init.exe --create-dirs"

그런 다음 다음 명령어를 복사해 붙여 넣어 최신 버전의 Solana를 설치하세요. C:\solana-install-tmp\solana-install-init.exe v1.17.13. 설치가 끝나면 Enter를 누르세요.

명령 프롬프트 창을 닫고 일반 사용자로 새 창을 여세요. **solana –-version**을 실행하여 원하는 버전의 solana가 설치되었는지 확인하세요.

다음으로 Anchor를 설치하세요.

Anchor 버전 관리자(avm)를 사용해 Anchor를 설치하는 것이 좋습니다. 다음 명령어로 cargo를 통해 설치할 수 있습니다.

코드
cargo install -git https://github.com/coral-xyz/anchor avm --locked --force.

그런 다음 최신 버전을 설치하고 사용하세요.

코드
avm install latest
avm use latest

# Verify the installation
avm –version

Anchor 버전 0.28부터 개발자는 Solang으로 직접 빌드할 수 있습니다. anchor init project_name —solidity 명령어로 새 Solang 프로젝트를 만들 수 있습니다. 이 명령어는 새 Solang 프로그램과 클라이언트를 통해 프로그램과 상호작용하는 방법을 보여주는 테스트 파일을 생성합니다.

Visual Studio Code를 사용한다면 구문 강조를 지원하는 Solang 확장 프로그램을 설치해 보세요. Solang 확장 프로그램이 올바르게 작동하도록 활성화된 다른 Solidity 확장 프로그램은 모두 비활성화하세요.

새 프로젝트 만들기

anchor-init project_name –solidity 명령어로 새 프로젝트를 만드세요. 프로젝트 이름으로 새 디렉터리가 생성됩니다. Solidity 플래그는 Solang을 사용하겠다는 뜻을 Anchor에 전달합니다. 프로젝트의 ./solidity 디렉터리에는 시작용 프로그램이 제공됩니다. 컨트랙트는 다음과 같습니다.

코드
@program_id("F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC")
contract test {
    bool private value = true;

    @payer(payer)
    constructor() {
        print("Hello, World!");
    }

    /// A message that can be called on instantiated contracts.
    /// This one flips the value of the stored `bool` from `true`
    /// to `false` and vice versa.
    function flip() public {
            value = !value;
    }

    /// Simply returns the current value of our `bool`.
    function get() public view returns (bool) {
            return value;
    }
}

이 프로그램에는 프로그램을 생성하고 상태 변수 value을 true으로 초기화한 뒤 프로그램 로그에 “Hello, World!”를 기록하는 constructor가 있습니다. flip 함수는 호출될 때 상태 변수를 업데이트합니다. get 함수는 상태 변수의 현재 값을 반환합니다. 몇 가지 주의 사항을 제외하면 일반적인 Solidity 스마트 컨트랙트와 비슷합니다. 가장 큰 차이는 어노테이션을 사용한다는 점입니다.

어노테이션

어노테이션은 계정 관리에 사용됩니다. @program_id(“...”) 어노테이션을 살펴보세요. 프로그램의 온체인 주소를 미리 알고 있을 때 이를 지정하는 데 사용합니다. 외부 호출로 컨트랙트를 호출하려면 프로그램에 @program_id 표기가 있거나 {program_id: … } 인수를 사용해 호출해야 합니다.

코드
@program_id(“...”);

contract Foo {
	function hello() public pure {
		print(“Hello”);
	}
}

contract Foo2 {
	function bye() public pure {
		print(“Bye”);
	}
}

contract Bar {
	function new_foo() external {
		Foo.new();
	}
}

contract Bar2 {
	function new_foo(address new_foo_id) external {
		Foo2.new{program_id: new_foo_id}();
	}
}

새 프로젝트를 만들었을 때 제공된 샘플 컨트랙트의 생성자 위에는 @payer 어노테이션이 있었습니다. 이 어노테이션은 프로그램 데이터 계정의 초기화 비용을 지불할 계정을 정의합니다. @payer(payer) 구문은 payer이라는 계정을 선언하며, 생성자를 호출할 때마다 필요합니다.

컨트랙트를 인스턴스화하려면 실행 코드를 보관할 프로그램 계정과 상태 변수를 저장할 데이터 계정이 필요합니다. 데이터 계정은 클라이언트 측 코드로 생성한 뒤 생성자를 호출하는 트랜잭션에 전달할 수 있습니다. 또는 생성자가 데이터 계정을 만들 수도 있습니다. 최소한 @payer 어노테이션을 제공해야 합니다. 데이터 계정이 PDA라면 seed와 bump를 제공해야 합니다. @seed 어노테이션은 PDA를 파생하는 seed를 지정하며, 문자열 리터럴 또는 hex”1234” 형식의 16진수 문자열을 사용할 수 있습니다. seed 어노테이션이 인수 앞에 있다면 bytes, address 또는 고정 길이 바이트 배열 타입의 인수를 참조해야 합니다. @bump 어노테이션은 곡선 밖 주소를 생성하는 데 사용할 값을 지정합니다. bytes1 타입의 단일 바이트여야 합니다. 선택 사항인 @space 어노테이션으로 데이터 계정의 크기를 지정할 수 있습니다. 이는 상수이거나 생성자 인수 중 하나를 사용할 수 있는 uint64 표현식입니다. Solang 문서에 따르면 @space은 최소한 solang -v 명령어를 실행할 때 지정한 크기여야 합니다.

코드
$ solang compile --target solana -v examples/solana/flipper.sol
...
info: contract flipper uses at least 17 bytes account data
…

프로그램에 생성자가 없다면 이 어노테이션들을 빈 생성자와 함께 사용할 수 있습니다. Solang 문서는 다음 예시를 제공합니다.

코드
@program_id("Foo5mMfYo5RhRcWa4NZ2bwFn4Kdhe8rNK5jchxsKrivA")
contract Foo {

    @space(500 + 12)
    @seed("Foo")
    @payer(payer)
    constructor(@seed bytes seed_val, @bump bytes1 bump_val) {
        // ...
    }
}

함수 어노테이션은 외부 함수에 필요한 계정을 선언하는 데 사용합니다.

  • @account(foo)은 foo 계정을 읽기 전용 계정으로 선언합니다
  • @mutableAccount(bar)은 bar 계정을 변경 가능한 계정으로 선언합니다
  • @signer(fizz)은 fizz 계정을 읽기 전용 서명자로 선언합니다
  • @mutableSigner(buzz)은 buzz 계정을 변경 가능한 서명자로 선언합니다

생성자에 @payer 어노테이션으로 선언한 계정은 생성자 내부에서 접근할 수 있습니다. 함수 어노테이션으로 선언한 계정은 tx.accounts 벡터에서 사용할 수 있습니다. 예를 들어 @account(ichigo)으로 선언한 계정에 접근하려면 tx.accounts.ichigo을 사용합니다. 그러면 AccountInfo 내장 구조체가 반환됩니다. 이 구조체는 “계정 모델 이해하기” 섹션에서 설명한 것과 동일한 구조를 따릅니다. 명칭은 조금 다릅니다.

  • key: 계정의 주소 또는 공개 키입니다. 타입은 address입니다
  • lamports: 계정의 lamport 잔액입니다. 타입은 uint64입니다
  • data: 계정의 데이터입니다. 타입은 bytes입니다
  • owner: 계정의 소유자입니다. 타입은 address입니다
  • rent_epoch: 계정의 rent가 청구되는 다음 epoch입니다. 타입은 uint64입니다
  • is_signer: 계정이 트랜잭션에 서명했는지를 나타냅니다. 타입은 bool입니다
  • is_writable: 이 트랜잭션에서 계정에 쓸 수 있는지를 나타냅니다. 타입은 bool입니다
  • executable: 계정이 프로그램인지를 나타냅니다. 타입은 bool입니다

제한 사항

Solang과 기존 Ethereum 개발 간의 주요 비호환성은 다음과 같습니다.

  • msg.sender은 Solana에서 사용할 수 없습니다. 계정 모델에서 Rust 컨트랙트는 여러 데이터 계정에 접근할 수 있는데, 이 중 어느 계정을 호출자로 간주해야 할까요? 하나의 계정을 호출자로 특정할 수 없는 경우가 많습니다. 또한 런타임에는 호출자 계정을 가져오는 메커니즘이 없습니다
  • ecrecover() 함수는 없지만 ed25519 서명을 확인하는 signatureVerify() 함수는 있습니다.
  • try-catch 문은 작동하지 않습니다. 외부 호출이나 컨트랙트 생성이 실패하면 런타임이 실행을 중단하고 전체 트랜잭션을 되돌립니다.
  • 오류 정의와 오류 메시지를 포함한 되돌리기는 아직 작동하지 않습니다
  • 함수 호출로 네이티브 값을 전송하는 기능은 작동하지 않습니다.
  • 많은 Yul 내장 기능을 사용할 수 없습니다. Solang은 대부분의 내장 기능을 지원하지만 메모리와 체인 연산은 구현되지 않았습니다.
  • 현재 ERC-20 형식은 지원되지 않습니다. SPL Token은 Token Program에 따라 정의됩니다. Token Program은 Solana에서 토큰을 생성, 발행, 전송, 소각하는 기본 방식입니다. SplToken 라이브러리를 사용하려면 spl_token.sol 파일을 소스 트리에 복사하고 필요한 곳에서 가져와야 합니다

또한 SVM의 레지스터 폭은 64비트이므로 256비트 정수보다 64비트 정수(예: uint64 및 int64)를 사용하는 것이 좋습니다. uint256 또는 int256처럼 64비트보다 넓은 타입의 연산은 여러 연산으로 분할됩니다. 따라서 속도가 느려지고 더 많은 컴퓨트 유닛을 사용합니다.

주소 리터럴은 address"36VtvSbE6jVGGQytYWSaDPG7uZphaxEjpJHUUpuUbq4D" 구문으로 지정해야 합니다. Ethereum의 16진수 구문(예: 0xE0f5206BBD039e7b0592d8918820024e2a7437b9)은 지원되지 않습니다. Solana의 모든 잔액과 값은 64비트 폭입니다. 따라서 주소용 내장 함수(예: .balance(), .transfer(), .send())는 64비트 정수를 사용합니다.

Solang은 EVM 개발자가 Solana 생태계에 진출할 수 있는 매력적인 경로를 제공하지만, 신중히 검토해야 할 여러 제한 사항이 있습니다. Solana의 계정 기반 모델로 전환하는 것은 단순한 구문 변경이 아닙니다. 스마트 컨트랙트 로직의 근본적인 변화를 의미합니다. 일부 함수, 코딩 패턴, EVM 전용 기능은 Solang에서 사용할 수 없습니다. Ethereum 스마트 컨트랙트를 Solang으로 이식한 뒤 큰 수정 없이 작동하리라 기대할 수는 없습니다. 적절한 오류 정의와 오류 메시지를 포함한 되돌리기 기능이 없는 등 누락된 기능은 상당히 치명적일 수 있습니다. 그러나 Solang은 계속 발전하는 도구이며, 모든 업데이트는 Solana에서 Solidity 개발자 경험을 개선하는 데 초점을 맞춥니다. Solang은 이 경험을 개선하는 몇 가지 분명한 장점도 제공합니다.

장점

이러한 제한에도 불구하고 개발자 경험을 개선하는 여러 내장 함수와 설계 방식을 제공합니다. 예를 들어 Solang으로 개발한 컨트랙트는 Anchor 프로그램과 상호작용할 수 있습니다. Anchor 프로그램의 IDL에서 Solidity 인터페이스를 생성하면 됩니다. IDL은 “Interface Description Language”의 약자입니다. 본질적으로 프로그램의 모든 사양을 담은 JSON 파일입니다. Anchor 프로그램과 상호작용하는 데 필요한 모든 정보가 포함됩니다. Anchor는 프레임워크로 프로그램을 개발할 때 IDL을 자동 생성합니다. Ethereum의 ABI와 매우 유사합니다. IDL에서 Solidity 인터페이스를 생성하려면 solang idl [-output directory] [IDL file] 명령어를 사용하세요. 이제 import “...”; 구문으로 파일을 가져올 수 있습니다.

Solang은 Solidity 컨트랙트가 Solana 전용 명령과 상호작용할 수 있도록 여러 라이브러리로 구성된 Solana 라이브러리를 제공합니다. 토큰 발행, 소각, 전송을 위한 SPL Token 라이브러리도 제공합니다. ERC-20과 ERC-721에 대응하는 것으로 볼 수 있습니다. 또한 개발자가 Solana의 System Program과 상호작용할 수 있도록 System Instructions 라이브러리를 제공합니다.

Solang에는 solana로 가져올 수 있는 몇 가지 내장 기능도 있습니다. 여기에는 AccountMeta 및 AccountInfo 구조체가 포함됩니다. AccountMeta 구조체는 외부 호출, 즉 CPI에서 호출 대상에 전달할 계정을 지정하는 데 사용합니다. AccountMeta 구조체의 구조는 다음과 같습니다.

  • pubkey: 계정의 주소 또는 공개 키입니다. 타입은 address입니다
  • is_writable: 호출 대상이 이 계정에 쓸 수 있는지를 나타냅니다. 타입은 bool입니다
  • is_signer: 호출 대상이 이 계정이 트랜잭션에 서명했다고 간주할 수 있는지를 나타냅니다. 타입은 bool입니다

외부 호출에서 accounts 인수를 생략하면 Solang 컴파일러가 AccountMeta 배열을 자동으로 생성합니다. 이는 함수가 external로 선언된 경우에만 작동합니다. 그렇지 않으면 IDL에 지정된 계정 순서에 따라 AccountMeta 배열을 수동으로 만들어야 합니다. 특정 호출에 계정이 필요하지 않다면 빈 벡터 {accounts: []}을 전달하세요. Solang 문서는 AccountMetas 배열을 만드는 방법에 대한 좋은 예시를 제공합니다.

코드
function build_this() external {
	// When calling a constructor from an external function, the data account for the contract
	// 'BeingBuilt' should be passed as the 'BeingBuilt_dataAccount' in the client code.
	BeingBuilt.new("my_seed");
}

function build_that(address data_account, address payer_account) public {
	AccountMeta[3] metas = [
		AccountMeta({
			pubkey: data_account,
      is_signer: true,
      is_writable: true
		}),
    AccountMeta({
    	pubkey: payer_account,
      is_signer: true,
      is_writable: true
    }),
    AccountMeta({
      pubkey: address"11111111111111111111111111111111",
      is_writable: false,
      is_signer: false
    })
  ];
  BeingBuilt.new{accounts: metas}("my_seed");

	// No accounts are needed in this call, so we pass an empty vector.
	BeingBuilt.say_this{accounts: []}("It's summertime!");
}

Solang에는 다음 기능을 위한 내장 함수도 있습니다.

Solang은 EVM 개발자의 전환을 돕는 풍부한 도구 모음과 컴파일러를 제공하여 Solana와 Ethereum 사이의 간극을 메웁니다. 제한 사항은 있지만 Anchor 프로그램과 상호작용하고, Solana 전용 라이브러리에 접근하며, 내장 함수를 사용할 수 있는 등 개발자 경험을 개선하는 여러 기능을 제공합니다. IDL과 토큰 표준처럼 익숙한 개념을 통합해 학습 부담도 줄입니다. 가스를 걱정하거나 최적화할 필요가 없다는 점도 반갑습니다. Solang은 Solana와 Ethereum 간 상호 운용성을 향한 중요한 진전입니다. 고성능 Solana 세계로 영역을 확장하려는 EVM 개발자에게 Solang은 유용한 도구가 될 수 있습니다.

Neon EVM

Solana에서 빌드하려는 Solidity 개발자에게 Solang만이 유일한 선택지는 아닙니다. Neon EVM은 세계 최초의 병렬화 가능한 EVM을 표방합니다. Solana에서 완전한 호환성을 제공하는 Ethereum 환경입니다. 개발자 친화적인 방식으로 Solana에서 Ethereum dApp을 확장하려는 개발자에게 시너지를 제공하는 솔루션입니다. 개발자는 선호하는 언어로 작성한 스마트 컨트랙트를 재구성하지 않고도 익숙한 도구를 사용해 dApp을 배포할 수 있습니다.

아키텍처

Neon EVM은 Neon EVM 프로그램, Neon Proxy, Neon DAO라는 세 가지 주요 구성 요소로 이루어집니다.

Neon EVM은 Ethereum과 유사한 트랜잭션을 받아 EVM 규칙에 따라 Solana에서 처리하는 Solana 프로그램입니다. Neon EVM으로 전달되는 이러한 Ethereum 유사 트랜잭션을 Neon Transactions라고 합니다. 이는 Ethereum JSON-RPC API에 따른 JSON RPC 메서드의 하위 집합입니다.

Neon Proxy를 사용하면 Ethereum 개발자가 최소한의 변경으로 dApp을 Neon에 이식할 수 있습니다. EVM 트랜잭션을 Solana 트랜잭션으로 패키징하며 Neon Operator를 위한 컨테이너형 솔루션 역할을 합니다. 이 운영자들은 Neon Proxy 서버를 실행하고 NEON으로 결제를 받으며 Solana 생태계 내에서는 SOL로 결제합니다. NEON은 유틸리티 토큰이자 거버넌스 토큰입니다. Neon Operator는 트랜잭션 실행에 필요한 가스 수수료를 지불하기 위해 이를 수집하며, 보유자는 Neon DAO에 참여할 수 있습니다.

Neon DAO는 NEON 토큰 보유자가 Neon EVM의 의사 결정 과정에 참여하도록 설계된 커뮤니티 주도형 거버넌스 모델입니다. 사용자, 운영자, 기여자, 코어 및 애플리케이션 개발자가 함께 거버넌스 규칙과 프로토콜 발전 방향을 논의합니다. DAO는 생태계, 개발, 보안에 초점을 둔 여러 탈중앙화 의회를 통해 운영됩니다. 각 의회는 담당 영역에서 협력적인 의사 결정과 제안 검토를 지원합니다. 생태계 중심 의회는 보조금과 이니셔티브 자금을 관리하며 생태계의 지속 가능한 성장을 감독합니다. 개발 중심 의회는 Neon 프로그램의 기술 업그레이드와 긴급 개입을 담당합니다. 보안 중심 의회는 잠재적 위협으로부터 Neon의 재무 자산과 Neon 프로그램을 보호합니다.

EVM 호환성

Neon EVM은 다음 방식으로 Solana에서 EVM 상호작용을 지원합니다.

Neon EVM과 상호작용하는 방식은 다른 EVM과 비슷합니다. 개발자는 익숙한 RPC API 메서드를 Neon Proxy로 보내 원활한 개발자 경험을 누릴 수 있습니다. 주요 기능은 다음과 같습니다.

  • Solidity 및 Vyper 스마트 컨트랙트와 Metamask, Foundry, Remix 같은 표준 Ethereum 개발 도구와의 호환성
  • 대부분의 Ethereum Opcode를 그대로 지원
  • 타입 0/레거시 Ethereum 트랜잭션 요청 허용. 현재 EIP-1559 트랜잭션은 지원되지 않습니다

물론 Neon EVM이 Solana에서 올바르게 작동하려면 몇 가지 조정이 필요합니다. 주요 차이점은 다음과 같습니다.

  • Neon EVM은 evm.code에 정의된 모든 사전 컴파일 컨트랙트를 지원합니다. 하지만 bigModExp, bn256Add, bn256ScalarMult, bn256Pairing 호출을 포함하는 Solidity 컨트랙트는 실행되지 않습니다. 향후 이러한 컨트랙트를 지원하려면 Neon EVM에 Solana 시스템 호출을 구현해야 합니다
  • 대부분의 opcode는 그대로 지원되지만 COINBASE, PREVRANDAO (FKA DIFFICULTY), GASLIMIT, BASEFEE, GAS opcode는 지원되지 않습니다. 이들은 변형 opcode로 알려져 있으며 Neon EVM에서 사용할 수 있도록 조정됩니다.
  • 가스 소비량과 수수료 계산 방식이 Ethereum과 다릅니다. Solana가 결제 레이어 역할을 하므로 일반적으로 비용이 더 낮습니다
  • 가스 계산 방식이 다르기 때문에 Solidity의 transfer() 및 send() 메서드는 Neon EVM에서 재진입에 안전하지 않습니다
  • Solana의 계정 모델은 실행 가능 계정과 실행 불가능 계정에 서로 다른 저장 기능과 접근 권한을 적용하여 스마트 컨트랙트 스토리지에 영향을 줍니다
  • Neon EVM은 버전이 지정된 트랜잭션을 사용하므로 단일 트랜잭션에서 사용할 수 있는 최대 계정 수가 64개로 제한됩니다
  • Neon EVM은 힙 메모리 한도가 256KB인 Solana의 Berkeley Packet Filter(BPF)를 사용합니다. 이로 인해 컨트랙트 호출 크기가 제한되며, 메모리 사용량을 효과적으로 관리하려면 최적화 전략이 필요합니다
  • block.number 및 block.timestamp 같은 시간 기반 함수는 다르게 작동합니다. Neon EVM에서 개발할 때 이러한 함수를 사용하지 않을 것을 강력히 권고합니다

Neon EVM은 익숙하고 호환되는 환경을 제공하지만, EVM 개발자가 Solana로 성공적으로 마이그레이션하고 빌드하려면 주요 차이점을 이해하고 이에 맞게 조정해야 합니다.

Neon RPC 연결

개발자는 Chainlist를 사용해 Neon RPC에 쉽게 연결할 수 있습니다. 여기에서 Neon EVM Mainnet 또는 Devnet에 연결할 수 있습니다. Mainnet 또는 Devnet 모달에서 지갑 연결을 클릭한 다음 지갑이 표시되면 승인을 클릭하세요.

사용자는 Neon EVM에 트랜잭션을 보내기 전에 최적의 운영자를 선택해야 합니다. 카드 세부 정보를 펼쳐 각 네트워크에서 사용 가능한 RPC 엔드포인트를 확인하세요.

지갑을 연결할 때 제공된 기본 운영자와 다른 운영자를 선택하면 수동 연결이 필요합니다. Neon EVM 문서는 Foundry, Hardhat, Remix, Truffle을 사용해 Proxy에 연결하는 방법을 자세히 안내합니다. 배포 섹션에서는 Foundry로 연결하는 방법을 살펴보겠습니다.

연결한 후 개발자는 Neon Faucet을 사용해 NEON 또는 다른 ERC-20 테스트 토큰을 받을 수 있습니다. request_neon 엔드포인트를 사용해 프로그래밍 방식으로 토큰을 요청할 수도 있습니다.

코드
curl -i -X POST \
	-d '{"wallet": "Your wallet", "amount": 1}' \
	'http://localhost:3333/request_neon'

이 명령어는 지정된 지갑 주소로 토큰을 받기 위한 POST 요청을 전송합니다.

트랜잭션 수명 주기와 가스 수수료

Neon EVM을 통해 Solana에서 Ethereum dApp의 트랜잭션을 실행하는 과정은 크게 세 단계입니다.

  • 사용자가 Neon RPC 엔드포인트로 서명된 Ethereum 유사 트랜잭션을 전송합니다
  • 트랜잭션이 Ethereum API를 통해 Neon Proxy로 전달됩니다. Proxy는 트랜잭션 실행에 필요한 가스를 추정하고 Ethereum 유사 트랜잭션을 Solana 트랜잭션으로 래핑해 브로드캐스트를 시작한 뒤, 래핑된 트랜잭션을 Neon EVM으로 전송합니다. 그 결과 Solana 영수증과 이에 대응하는 Neon EVM 트랜잭션 영수증이 생성됩니다. Neon 스마트 컨트랙트는 트랜잭션의 래핑을 해제하고 사용자 서명을 검증한 뒤 Solana 스토리지에서 EVM 상태를 불러옵니다. 트랜잭션은 Solana의 BPF 내부에서 실행됩니다
  • Solana와 Neon EVM이 상태를 업데이트하여 트랜잭션 요청을 완료합니다

이것이 Neon EVM에서 트랜잭션이 시작되어 실행되기까지의 전체 수명 주기입니다. 개발자는 NeonScan에서 최신 트랜잭션과 블록을 확인하고 계정, 토큰, 블록 또는 트랜잭션 해시를 조회할 수 있습니다.

개발자는 가스 없는 트랜잭션도 전송할 수 있습니다. 이는 초기 트랜잭션 수수료를 충당할 NEON 토큰이 부족한 사용자를 지원하기 위해 구현되었습니다. 개발자는 info@neonevm.org에 문의해 가스 없는 트랜잭션 스타터 팩을 받을 수 있습니다. 개발자가 선택한 Proxy Operator가 이러한 트랜잭션을 계속 처리하지만 트랜잭션 비용은 Neon이 부담합니다. 일반적으로 새 Neon 계정마다 최소 3개의 가스 없는 트랜잭션이 제공됩니다.

가스 없는 트랜잭션의 처리 과정은 다음과 같습니다.

  • 최종 사용자가 dApp을 통해 트랜잭션을 시작합니다
  • dApp이 선택한 Proxy Operator에 현재 가스 가격을 요청합니다. 자격 요건을 충족하면 Proxy Operator는 지정된 수의 가스 없는 트랜잭션을 사용할 수 있도록 Neon 계정을 표시합니다
  • 해당 트랜잭션에 대해 dApp은 가스 비용을 0으로 표시합니다
  • 최종 사용자는 가스 수수료 없이 트랜잭션에 서명합니다
  • Proxy Operator가 트랜잭션을 실행하며 SOL 가스 수수료는 Neon Foundation이 지불합니다

Neon EVM 문서는 가스 없는 트랜잭션을 요청하는 방법을 보여주기 위해 다음 코드를 제공합니다.

코드
try {
	// Get gasless transaction if user account is eligible
  const rawGasPrice = await axios.post(rpcApiUrl, {
  	method: 'neon_gasPrice',
    params: [{ from: address }],
    jsonrpc: "2.0",
    id: new Date().getTime()
   })

   tx.gasPrice = rawGasPrice.data?.result;
	} catch (e) {
  	//Else, get standard GAS price
   	setError('Can\'t retrieve gas price for transaction')

    const rawGasPrice = await web3.eth.getGasPrice();

    tx.gasPrice = web3.utils.toHex(rawGasPrice);
} finally {
    setTx(tx)
}

NeonPass

NeonPass는 Solana와 Neon EVM 간에 토큰을 전송하는 도구입니다. Solana의 Associated Token Account와 Neon EVM의 ERC-20 토큰 계정 간에 자산을 원활하게 전송할 수 있습니다. NeonPass는 Neon EVM의 인터페이스 컨트랙트와 특수 계정 스토리지를 사용합니다. Solana Program Library(SPL) 토큰은 Neon EVM의 팩토리 컨트랙트 내에서 ERC-20 인터페이스로 패키징됩니다. 따라서 SPL을 Solidity dApp과 호환되는 ERC-20 토큰 계정에 저장할 수 있습니다. NeonPass는 Solana 계정과 Neon EVM 계정 간 토큰의 양방향 전송을 지원합니다. 자산을 잠그고 새로 발행하는 기존 브리지와 달리, 두 계정 타입 간에 토큰을 직접 이동합니다. 이를 통해 Solana와 Neon EVM 토큰을 원활하게 전환할 수 있습니다.

배포

개발자는 Hardhat, Foundry, Truffle, Remix을 사용해 Neon EVM에 배포할 수 있습니다. Solang을 가장 쉽게 사용하는 방법은 Anchor를 이용하는 것이므로 모든 테스트는 TypeScript로 진행됩니다. 하지만 Solidity를 선호하는 개발자도 이제 Foundry를 통해 Solidity dApp을 테스트하고 Solana에 배포할 수 있습니다.

먼저 EVM 호환 지갑이 Neon EVM Devnet에 연결되어 있는지 확인하세요. 그런 다음 Neon의 Foundry 예제 프로젝트를 복제하고 해당 디렉터리로 이동하세요.

코드
git clone https://github.com/neonlabsorg/neon-tutorials
cd neon-tutorials/foundry

그런 다음 Foundry 도구 체인 설치 프로그램인 Foundryup을 설치하고 foundryup을 실행하여 최신 nightly 사전 컴파일 바이너리, 즉 force, cast, anvil, chisel을 설치하세요.

코드
curl -L https://foundry.paradigm.xyz | bash
foundryup

필요한 라이브러리를 설치하세요.

코드
forge install foundry-rs/forge-std --no-commit
forge install openzeppelin/openzeppelin-contracts --no-commit

이제 지갑 계정의 비공개 키를 가져오세요. 예를 들어 Metamask에서는 햄버거 메뉴를 클릭하고 Account Details > Show Private Key으로 이동하면 비공개 키를 확인할 수 있습니다. 비밀번호를 입력하라는 메시지가 표시됩니다. 확인을 클릭하여 계정의 비공개 키에 접근하세요. 이 키를 누구에게도 공유하지 말고, 필요한 보호 조치를 취하세요.

그런 다음 다음 변수가 포함된 .env 파일을 만드세요.

코드
RPC_URL_DEVNET=https://devnet.neonevm.org
CHAIN_ID_DEVNET=245022926
RPC_URL_MAINNET=https://neon-proxy-mainnet.solana.p2p.org
CHAIN_ID_MAINNET=245022934
PRIVATE_KEY=
VERIFIER_URL_BLOCKSCOUT=https://neon-devnet.blockscout.com/api

<YOUR_PRIVATE_KEY>를 비공개 키로 바꾸고 source .env을 실행하세요.

프로젝트의 컨트랙트를 컴파일하려면 src 디렉터리로 이동하여 forge build을 실행하세요. 콘솔에 컴파일러 실행이 성공했다는 메시지가 표시되어야 합니다. forge test 명령어로 컨트랙트를 테스트할 수도 있습니다. 프로젝트의 컨트랙트를 배포하려면 다음 명령어를 실행하세요.

코드
forge create --rpc-url $RPC_URL_DEVNET --private-key $PRIVATE_KEY src/TestERC20/TestERC20.sol:TestERC20 --constructor-args "Test ERC20 Token" "TERC20" --legacy

다음과 비슷한 출력이 표시됩니다.

코드
[⠰] Compiling...
No files changed, compilation skipped
Deployer: 0x4455E84Eaa56a01676365D4f86348B311969a4f4
Deployed to: 0x5537599aa2F97Dd60a66342522a465A7f2e40Ff9
Transaction hash: 0x6de9dab8a526cbac33008056d185b93dff725605efb791bf116b6bece4f0c486

컨트랙트를 검증하려면 다음 명령어를 실행하세요.

코드
forge verify-contract --chain-id $CHAIN_ID_DEVNET  src/TestERC20/TestERC20.sol:TestERC20 --verifier-url $VERIFIER_URL_BLOCKSCOUT --verifier blockscout

<contract_address>를 스마트 컨트랙트 주소로 바꾸세요. Neon의 Devnet 탐색기인 BlockScout의 컨트랙트 주소를 가리키는 URL과 함께 OK 응답을 받게 됩니다. Devnet도 지원하는 NeonScan을 BlockScout 대신 사용하도록 .env 파일을 구성할 수도 있습니다.

장점과 제한 사항

Ethereum과 유사한 개발자 경험을 원하는 개발자는 Neon EVM을 선택하는 것이 좋습니다. Ethereum과 유사한 트랜잭션을 전송하고 익숙한 도구를 사용하는 EVM 호환 환경입니다. NeonPass, 대부분의 EVM opcode 지원, 가스 없는 트랜잭션 제공 등의 기능 덕분에 Solana로 훨씬 쉽게 전환할 수 있습니다. 하지만 Neon EVM도 완벽하지는 않습니다. Solana 인프라에 맞추기 위한 스마트 컨트랙트 로직 변경, Solana의 Berkeley Packet Filter(BFP)와 계정 모델의 제약 내에서의 운영, 일부 opcode 및 사전 컴파일 컨트랙트 제한 등의 한계가 있습니다. EVM 호환 환경에서 Solana로 성공적으로 배포하려면 이러한 차이를 이해하고 적절히 대응해야 합니다.

과거 마이그레이션 사례

대규모 Ethereum 프로토콜을 비 EVM 체인에 배포하려면 여러 복잡한 과제를 해결해야 합니다. Solidity가 아닌 언어를 다루는 엔지니어를 확보해 코드베이스를 처음부터 다시 구축하고, 신뢰할 수 있는 감사 파트너를 찾고, 새 코드베이스를 다시 감사하고, DAO의 결정을 집행하도록 거버넌스 컨트랙트를 조정해야 합니다. 이 작업에는 수백만 달러의 비용과 수개월의 전담 작업이 필요할 수 있습니다. 대부분의 프로젝트에는 현실적인 선택지가 아닙니다. 하지만 실제로 이를 해낸 사례가 있습니다.

Helium은 사물 인터넷(IoT) 기기를 지원하는 탈중앙화 무선 인프라 구축을 목표로 하는 LoRaWAN 네트워크입니다. 이를 위해 소형 기지국과 유사한 저전력 소형 기기인 핫스팟을 사용합니다. 핫스팟은 장거리에서 다른 핫스팟과 연결됩니다. 원래 자체 레이어 1(L1) 블록체인에서 운영되었지만, Helium 개발팀은 HIP 70을 통해 Solana로의 이전을 제안했습니다. 이를 통해 높은 수준의 보안과 낮은 사용 비용을 유지하면서 가동 시간, 결합성, 사용자 경험 속도를 개선할 수 있었습니다. 커뮤니티는 압도적인 찬성으로 제안을 통과시켰고, Helium은 2023년 4월 Solana로 마이그레이션했습니다. Helium COO Scott Sigel은 마이그레이션을 지루할 정도로 평온한 이벤트라고 설명했습니다. 네트워크나 Helium 인프라에는 아무런 문제도 발생하지 않았습니다. 엔지니어에게는 꿈과 같은 결과였습니다. 또한 팀은 문서의 가이드 시리즈에서 전체 마이그레이션 과정을 설명했습니다. Helium의 마이그레이션은 큰 성공을 거두었고 커뮤니티에도 상당한 이점을 제공했습니다.

이 마이그레이션은 단발성 사례가 아닙니다. 세계 최초의 탈중앙화 GPU 렌더링 플랫폼인 The Render Network는 2023년 11월 핵심 인프라를 Ethereum에서 Solana로 성공적으로 업그레이드했습니다. 커뮤니티는 Solana로 마이그레이션하는 RNP-002에 찬성했습니다. Render 창립자 Jules Urbach는 이 마이그레이션을 중대한 전환점이라고 설명했습니다. 그는 다음과 같이 말했습니다. “Solana의 놀라운 트랜잭션 속도와 낮은 비용, 웹 규모 아키텍처에 대한 확고한 의지는 확장 가능하고 탈중앙화된 메타버스 인프라를 계속 구축하려는 Render Network에 완벽하게 부합합니다.”

Render의 마이그레이션은 사용자에게 큰 부정적 영향을 주지 않았습니다. 사용자는 Render의 Upgrade Assistant를 사용해 자금을 Ethereum에서 Solana로 브리지할 수 있습니다. Ethereum 지갑을 연결하고 마이그레이션할 RNDR 수량을 지정한 다음, 지정한 Solana 지갑으로 토큰이 전송될 때까지 기다리면 됩니다. 

Maker는 Ethereum을 대표하는 프로젝트입니다. Maker Platform을 통해 탈중앙화 금융의 잠재력을 실현하는 것을 목표로 합니다. Maker Platform은 경제적 자립과 글로벌 금융 시장에 대한 공평한 접근을 강화하도록 설계된 포용적 플랫폼입니다. 이 플랫폼은 Maker 프로젝트를 관리하는 MakerDAO와 “세계 최초의 편향 없는 통화이자 선도적인 탈중앙화 스테이블코인”인 DAI를 위한 Maker 프로토콜로 구성됩니다. Maker 창립자 Rune Christensen은 Solana 코드베이스의 포크를 사용해 Maker 앱체인을 개발하는 방안에 관해 다음과 같이 트윗했습니다. 

마이그레이션은 본질적으로 복잡합니다. 프로젝트의 전체 인프라를 Ethereum에서 Solana로 이전하면 금전적 비용과 평판 훼손 위험, 많은 시간이 들지만, 프로젝트들은 Solana를 선택하고 있습니다. Solang 및 Neon EVM 같은 도구를 사용하면 마이그레이션이 반드시 복잡할 필요는 없습니다. Helium의 마이그레이션처럼 지루할 정도로 평온할 수 있으며, Render Network처럼 사용자 자금에 미치는 부정적 영향도 거의 또는 전혀 없을 수 있습니다. Solana는 웹 규모에 맞게 구축된, 시장에서 가장 높은 성능을 제공하는 블록체인입니다. 바로 이곳에서 구축해야 합니다. 점점 더 많은 프로젝트가 이 사실을 깨닫고 있습니다.

결론

Solidity는 스마트 컨트랙트 개발의 공용어입니다. EVM은 등장한 이래 스마트 컨트랙트 환경을 지배해 왔습니다. 그러나 약점도 있습니다. 웹 규모의 소비자용 애플리케이션을 운영하려면 높은 처리량과 짧은 지연 시간을 제공하는 네트워크가 필요합니다. Ethereum의 단일 스레드 및 변동성 높은 가스 기반 환경은 Helium과 같이 높은 처리량이 필요한 탈중앙화 물리 인프라 네트워크(DePIN) 프로젝트를 지원할 수 없습니다. 

이러한 과제에 대한 강력한 대안으로 Solana가 부상하고 있습니다. Solana의 진정한 이점을 활용하는 가장 좋은 방법은 실제로 Solana에서 구축하는 것입니다. 최근의 발전으로 Solang 및 Neon EVM 같은 도구가 등장하면서 EVM 개발자는 익숙한 도구와 언어를 사용해 마이그레이션할 수 있게 되었습니다. 이 글에서는 Solana의 아키텍처와 Ethereum과의 차이를 종합적으로 설명하고, EVM 개발자가 Solana에서 개발을 시작하는 방법을 제시합니다. Solana는 시장에서 가장 높은 성능을 제공하는 블록체인입니다. 성장 동력과 인지도를 확보하고 있으며, 신뢰할 수 있는 소비자용 애플리케이션도 늘고 있습니다. 빠르고 확장 가능한 블록체인의 이점을 지금 누릴 수 있는데, 왜 Ethereum이 확장되기를 기다려야 할까요?

여기까지 읽어주셔서 감사합니다, 익명의 독자님! 아래에 이메일 주소를 입력하고 Solana의 새로운 소식을 빠짐없이 받아보세요. 더 깊이 알아볼 준비가 되셨나요? 지금 바로 Discord에 참여해 가장 높은 성능을 제공하는 블록체인에서 미래를 구축해 보세요.

추가 자료

Helius 구독하기

최신 Solana 개발 소식을 확인하고 새 게시물 알림을 받아보세요

확대 이미지