메인 콘텐츠로 건너뛰기
이 가이드에서는 Hardhat 프로젝트를 생성하고, WKRC 결제 컨트랙트를 작성하고, StableNet 테스트넷에 배포한 다음 Explorer에서 검증합니다. 소요 시간: 약 15분 도구: Node.js, Hardhat 네트워크: StableNet 테스트넷 (Chain ID 8283)

사전 준비

시작 전에 아래 항목을 확인하세요:
  • Node.js ≥ 18node --version으로 확인하세요. 없다면 nodejs.org에서 설치합니다.
  • 잔액이 있는 테스트넷 지갑. Faucet에서 무료 WKRC를 받으세요:

    Faucet

    테스트넷 WKRC 요청 — 회원가입 불필요.

    Explorer

    배포 전 잔액을 확인하세요.
StableNet은 27,600 Gwei 최소 Priority Fee를 강제합니다. 모든 배포 및 전송 트랜잭션에 maxPriorityFeePerGas를 이 값 이상으로 설정해야 합니다. 이 임계값 미만의 트랜잭션은 트랜잭션 풀에서 즉시 거절됩니다.

네트워크 정보

항목
Chain ID8283
RPC URLhttps://api.test.stablenet.network
Explorerexplorer.stablenet.network
Faucetfaucet.stablenet.network
WKRC 컨트랙트0x0000000000000000000000000000000000001000
최소 Priority Fee27,600 Gwei (27600000000000 wei)
EVM 버전shanghai (blob 옵코드 미지원 — cancun 사용 금지)

단계별 가이드

1

Hardhat 프로젝트 생성

새 디렉터리를 만들고, Node.js 프로젝트를 초기화한 다음 Hardhat을 설치합니다:
mkdir stablenet-hardhat && cd stablenet-hardhat
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
Hardhat 프로젝트를 초기화합니다. 프롬프트에서 **“Create a JavaScript project”**를 선택하세요:
npx hardhat init
프로젝트 구조:
stablenet-hardhat/
├── contracts/        # Solidity 소스 파일
├── scripts/          # 배포 스크립트
├── test/             # 테스트 파일
└── hardhat.config.js # Hardhat 설정
프라이빗 키를 소스 코드에서 분리하기 위해 dotenv를 설치합니다:
npm install dotenv
.env 파일을 만들고 .gitignore에 추가합니다:
echo "PRIVATE_KEY=0xYourPrivateKey" > .env
echo ".env" >> .gitignore
2

hardhat.config.js 설정

hardhat.config.js 내용을 아래로 교체합니다:
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();

module.exports = {
  solidity: {
    version: "0.8.20",
    settings: {
      // Blob opcodes are not supported on StableNet — do not use cancun
      evmVersion: "shanghai",
    },
  },
  networks: {
    stablenet_testnet: {
      url: "https://api.test.stablenet.network",
      chainId: 8283,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
evmVersioncancun으로 설정하지 마세요. StableNet은 blob 옵코드(BLOBHASH, BLOBBASEFEE)를 지원하지 않습니다. paris 또는 shanghai를 사용하세요.
3

WKRCPayment.sol 작성

기본 제공 컨트랙트를 삭제하고 contracts/WKRCPayment.sol을 만듭니다:
rm contracts/Lock.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC20 {
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
}

/// @title WKRCPayment
/// @notice Accepts WKRC payments from an approved sender.
///         The sender must approve this contract before calling pay().
contract WKRCPayment {
    /// @dev NativeCoinAdapter (WKRC) — fixed system contract address on StableNet.
    IERC20 public constant WKRC =
        IERC20(0x0000000000000000000000000000000000001000);

    event Payment(address indexed from, address indexed to, uint256 amount);

    /// @notice Pull `amount` of WKRC from msg.sender and forward it to `to`.
    ///         Requires prior WKRC.approve(address(this), amount).
    function pay(address to, uint256 amount) external {
        require(WKRC.transferFrom(msg.sender, to, amount), "WKRC transfer failed");
        emit Payment(msg.sender, to, amount);
    }

    /// @notice Check how much WKRC `owner` has approved for this contract.
    function allowanceOf(address owner) external view returns (uint256) {
        return WKRC.allowance(owner, address(this));
    }
}
4

컨트랙트 컴파일

npx hardhat compile
예상 출력:
Compiled 1 Solidity file successfully (evm target: shanghai).
컴파일된 아티팩트는 artifacts/cache/에 저장됩니다.
5

배포 스크립트 작성

기본 스크립트를 삭제하고 scripts/deploy.js를 만듭니다:
rm scripts/deploy.js 2>/dev/null; true
const { ethers } = require("hardhat");

async function main() {
  const [deployer] = await ethers.getSigners();
  console.log("Deploying with:", deployer.address);

  const WKRCPayment = await ethers.getContractFactory("WKRCPayment");

  // StableNet requires maxPriorityFeePerGas >= 27,600 Gwei on every transaction
  const contract = await WKRCPayment.deploy({
    maxPriorityFeePerGas: ethers.parseUnits("27600", "gwei"),
  });

  await contract.waitForDeployment();
  console.log("WKRCPayment deployed to:", await contract.getAddress());
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
maxPriorityFeePerGas모든 배포 및 전송 호출에 반드시 포함해야 합니다. Hardhat은 네트워크 강제 최솟값을 자동으로 감지하지 않습니다 — 생략하면 트랜잭션이 거절됩니다.
6

테스트넷에 배포

배포 스크립트를 StableNet 테스트넷에 대해 실행합니다:
npx hardhat run scripts/deploy.js --network stablenet_testnet
성공 시 출력 예시:
Deploying with: 0xYourWalletAddress
WKRCPayment deployed to: 0xABC...
블록 포함까지 최대 1-2분 걸릴 수 있습니다. waitForDeployment()에서 멈춰 보여도 tx는 mempool에 있으며 곧 확정됩니다. 취소 후 재시도 시 nonce 충돌이 발생할 수 있습니다.
배포된 컨트랙트 주소를 저장해 두세요 — 컨트랙트와 상호작용할 때 필요합니다.
7

Explorer에서 검증

explorer.stablenet.network에서 컨트랙트 주소를 검색합니다.다음 항목을 확인할 수 있어야 합니다:
  • Contract Creation 레이블이 붙은 배포 트랜잭션
  • 트랜잭션 발신자로 배포자 주소
  • 온체인에 저장된 컨트랙트 바이트코드
트랜잭션에 블록 번호와 Success 상태가 표시되면 배포가 완료된 것입니다.

무슨 일이 일어났나요?

  • **hardhat.config.js**는 Chain ID 8283과 공개 RPC 엔드포인트를 사용해 Hardhat을 StableNet 테스트넷에 연결합니다.
  • **evmVersion: "shanghai"**는 Solidity가 StableNet에서 지원하지 않는 blob 옵코드를 생성하지 않도록 합니다.
  • **maxPriorityFeePerGas: ethers.parseUnits("27600", "gwei")**는 GovValidator가 강제하는 최솟값을 충족합니다. 없으면 트랜잭션 풀에서 즉시 거절됩니다.
  • **waitForDeployment()**는 배포 트랜잭션이 블록에 포함될 때까지 eth_getTransactionReceipt를 폴링합니다.
  • WKRC는 StableNet의 KRW 연동 네이티브 코인으로, NativeCoinAdapter 시스템 컨트랙트를 통해 표준 ERC-20 인터페이스로 노출됩니다. 가스비에 사용되는 잔액과 balanceOf()로 조회되는 잔액이 동일합니다.

트러블슈팅

  • 트랜잭션 거절 / “fee too low” — 모든 배포 및 전송 호출에 maxPriorityFeePerGas를 최소 ethers.parseUnits("27600", "gwei")로 설정했는지 확인하세요.
  • waitForDeployment() 멈춤 — tx가 mempool에 있습니다. 최대 2분 기다리세요. 블록 타임은 ~1초이지만 mempool 전파에 지연이 발생할 수 있습니다.
  • “replacement transaction underpriced” — 동일한 nonce의 이전 트랜잭션이 대기 중입니다. maxFeePerGasmaxPriorityFeePerGas를 모두 높여 대체하거나, 원래 트랜잭션이 확정될 때까지 기다리세요.
  • WKRC 전송 시 “transfer amount exceeds balance” — 전송 금액을 줄이거나 gas fee를 낮춰 지갑 잔액이 전송액과 가스 비용을 모두 감당할 수 있도록 하세요.
  • BLOBHASH / BLOBBASEFEE 옵코드 오류evmVersion: "cancun"을 사용 중입니다. hardhat.config.js에서 "shanghai"로 변경하세요.
maxFeePerGas를 생략하면 0이 됩니다. maxPriorityFeePerGas만 설정하면 maxFeePerGas0으로 처리돼 트랜잭션이 거절됩니다. 모든 호출에 두 값을 명시하세요:
{
  maxPriorityFeePerGas: ethers.parseUnits("27600", "gwei"),
  maxFeePerGas: ethers.parseUnits("80000", "gwei"),
}
Hardhat v3 — npx hardhat init이 인터랙티브 전용으로 변경됐습니다. Hardhat v3는 npx hardhat init이 인터랙티브 전용으로 바뀌어 일부 환경에서 정상 동작하지 않을 수 있습니다. 표준 프로젝트 구조를 사용하려면 Hardhat v2를 설치하세요:
npm install --save-dev hardhat@2
27,600 Gwei에서 블록 포함이 느린 경우. 트랜잭션 확정이 느리면 두 값을 모두 높이세요:
{
  maxPriorityFeePerGas: ethers.parseUnits("35000", "gwei"),  // 35000000000000 wei
  maxFeePerGas: ethers.parseUnits("80000", "gwei"),          // 80000000000000 wei
}

다음 단계

Foundry 빠른 시작

Foundry로 동일한 WKRC 결제 컨트랙트를 배포합니다 — approve 및 pay 흐름 포함.

EVM 호환성 레퍼런스

지원 옵코드, 도구 설정 스니펫, 수수료 위임(tx type 0x16), 미지원 기능.

네트워크 정보

전체 네트워크 파라미터 — Chain ID, RPC 엔드포인트, 시스템 컨트랙트 주소.

Explorer

온체인에서 컨트랙트와 트랜잭션을 조회합니다.