[ZK] Cairo언어를 이용한 Starknet 컨트랙트 배포

무앙·2025년 7월 4일

이번 글에서는 로컬 개발 환경과 Sepolia 테스트넷에서 Cairo로 작성된 ZK 증명 기반의 스마트 컨트랙트를 작성하고, sncast를 이용해 배포하고 호출해보겠습니다.


0. Devnet 실행

로컬 네트워크에서 개발하기 위해 Starknet Devnet을 실행합니다. Ethereum의 Anvil과 유사하게 동작합니다.

starknet-devnet --seed=0
  • --seed=0을 지정하면 동일한 계정 및 주소가 항상 생성됩니다.
  • 실행 후, predeployed account와 ETH/STRK 토큰 주소 정보가 출력됩니다.

1. Scarb 프로젝트 설정

Scarb.toml 예시

[package]
name = "cairo_contract"
version = "0.1.0"
edition = "2024_07"

[dependencies]
starknet = "2.11.4"

[dev-dependencies]
snforge_std = "0.43.1"
assert_macros = "2.11.4"

[[target.starknet-contract]]
sierra = true
casm = true

[scripts]
test = "snforge test"

[tool.scarb]
allow-prebuilt-plugins = ["snforge_std"]

기본 설정에서 casm = true만 추가했습니다.

sierra = true

  • Cairo 스마트 컨트랙트를 중간 표현(IR) 형태인 Sierra로 변환합니다.
  • Starknet에서 보안성 및 검증을 위해 필수적인 단계입니다.

casm = true

  • Sierra를 실제 실행 가능한 저수준 Cairo 어셈블리(CASM) 코드로 컴파일합니다.
  • CASM은 Starknet 네트워크에서 배포되고 실행되는 코드입니다.

2. 계정 가져오기 (sncast)

Devnet에서 미리 배포된 계정을 사용하여 snfoundry에 불러옵니다:

sncast account import \
  --name=local-test-account \
  --address=0x064b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691 \
  --type=oz \
  --url=http://127.0.0.1:5050 \
  --private-key=0x0000000000000000000000000000000071d7bb07b9a64f6f78ac4c816aff4da9 \
  --add-profile=devnet \
  --silent

--name= 옵션에 계정 alias를 설정할 수 있습니다.


3. 빌드

scarb build

빌드하면 target/release 폴더 아래에 .contract_class.json.compiled_contract_class.json 파일이 생성됩니다.


4. 컨트랙트 선언 (declare)

sncast --profile=devnet --account=local-test-account declare --contract-name=VerifierContract

출력 예시:

class_hash: 0x04eefd80...
transaction_hash: 0x01871398...

5. 컨트랙트 배포 (deploy)

sncast --profile=devnet --account=local-test-account deploy \
  --class-hash=0x04eefd80... \
  --salt=0

출력 예시:

contract_address: 0x0044bd8c...

6. 컨트랙트 호출 (call)

sncast --profile=devnet --account=local-test-account call \
  --contract-address=0x0044bd8c... \
  --function verify_add_and_mul \
  --arguments 5,6,11,30

결과:

response: true
response_raw: [0x1]

7. Cairo 컨트랙트 코드 예제

/// 두 수를 더해서 결과가 예상한 값과 같은지 검증하는 함수
fn verify_addition(a: u16, b: u16, expected: u16) -> bool {
    let result = a + b;
    result == expected
}

/// 두 수를 곱해서 결과가 예상한 값과 같은지 검증하는 함수
fn verify_multiplication(a: u16, b: u16, expected: u16) -> bool {
    let result = a * b;
    result == expected
}

#[starknet::contract]
mod VerifierContract {
    use super::{verify_addition, verify_multiplication};

    #[external(v0)]
    fn verify_add_and_mul(x: u16, y: u16, expected_add: u16, expected_mul: u16) -> bool {
        verify_addition(x, y, expected_add) && verify_multiplication(x, y, expected_mul)
    }
}

8. Starknet Sepolia에서 테스트

Sepolia는 Starknet의 퍼블릭 테스트넷으로, 실제 메인넷 배포 전 테스트하기에 적합합니다. 이 섹션에서는 Sepolia 네트워크에서 계정을 생성하고, 컨트랙트를 선언/배포/호출하는 전체 과정을 설명합니다.


1) Sepolia 계정 생성

sncast account create \
    --network=sepolia \
    --name=sepolia-test-account
  • account create: 새로운 Starknet 계정 생성
  • --network=sepolia: Sepolia 네트워크 대상으로 생성
  • --name=...: 계정 이름을 snfoundry.toml에 저장할 때 사용할 alias

생성 직후 해당 계정은 아직 배포되지 않은 상태이며, DEPLOY_ACCOUNT 트랜잭션 전송을 위해 사용 전 STRK로 충전 해야 합니다.


2) Faucet에서 STRK 받기

Starknet Sepolia Faucet에 접속하여 위에서 생성한 주소에 STRK 또는 ETH를 지급받습니다:

https://faucet.sepolia.starknet.io

STRK 토큰이 있어야 계정 배포(deploy) 및 트랜잭션 수수료 지불이 가능합니다.


3) 계정 배포

sncast account deploy \
    --network=sepolia \
    --name=sepolia-test-account
  • account deploy: 생성한 계정을 Starknet Sepolia에 실제 배포
  • 이 과정은 DEPLOY_ACCOUNT 트랜잭션을 발생시키며, STRK 수수료가 필요합니다.

예시 결과:

transaction: https://sepolia.starkscan.co/tx/0x...

4) 컨트랙트 선언 (declare)

sncast declare \
    --account=sepolia-test-account \
    --contract-name=VerifierContract \
    --network=sepolia
  • declare: 스마트 컨트랙트의 클래스 코드를 네트워크에 등록하는 과정입니다.
  • 한 번 등록된 class_hash는 여러 번 재사용할 수 있습니다.
  • 이미 선언된 코드라면 에러 메시지로 알림됩니다.
error: Transaction execution error = TransactionExecutionErrorData { transaction_index: 0, execution_error: Message("Class with hash 0x04eefd807f4b568213d65322e5031c08bfc4d496cf859ea3bcfdbff12e173bda is already declared.") }

위와 같이 class_hash가 이미 네트워크에 등록되어 있으면, 중복 선언이 감지되어 오류가 발생합니다. 이 경우에는 에러 메시지에 반환되는 기존 class_hash를 그대로 사용하여 배포(deploy) 단계를 진행하면 됩니다.


5) 컨트랙트 배포 (deploy)

sncast deploy \
    --account=sepolia-test-account \
    --class-hash=0x... \
    --network=sepolia
  • deploy: 선언한 class_hash를 바탕으로 컨트랙트 인스턴스를 배포합니다.
  • 성공 시 컨트랙트 주소가 출력됩니다.
  • --salt를 지정하면 배포 주소를 고정할 수도 있습니다.

6) 컨트랙트 호출 (call)

sncast call \
    --contract-address=0x... \
    --function=verify_add_and_mul \
    --arguments=5,6,11,30 \
    --network=sepolia
  • call: 상태 변경 없는 읽기 전용 함수를 실행합니다.
  • 이 예제는 5 + 6 == 11, 5 * 6 == 30인지 검증하는 함수입니다.

출력 결과:

response: true
response_raw: [0x1]

Sepolia 테스트 요약 명령어

단계명령어설명
계정 생성sncast account create --network=sepolia --name=...계정 정보를 로컬에 생성
Faucet 사용Starknet Sepolia Faucet테스트용 STRK/ETH 지급
계정 배포sncast account deploy --network=sepolia --name=...계정 인스턴스를 네트워크에 배포
선언sncast declare --account=... --contract-name=... --network=sepolia스마트 컨트랙트 코드 등록
배포sncast deploy --account=... --class-hash=... --network=sepoliaclass hash 기반으로 컨트랙트 생성
호출sncast call --contract-address=... --function=... --arguments=... --network=sepolia컨트랙트 함수 호출 (read-only)

참고 링크

0개의 댓글