> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stablenet.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Hardhat으로 컨트랙트 배포하기

> Hardhat을 StableNet Testnet으로 설정하고 ethers.js를 사용해 WKRC 결제 컨트랙트를 배포합니다.

> Hardhat으로 Solidity 컨트랙트를 StableNet Testnet에 배포하고 Explorer에서 확인합니다.

## 배우는 내용

이 튜토리얼을 완료하면 다음을 할 수 있습니다:

* StableNet Testnet을 위한 Hardhat과 `hardhat.config.js` 구성
* Hardhat 배포 스크립트로 WKRC 결제 컨트랙트 배포
* StableNet Explorer에서 배포 확인

## 사전 준비

* Node.js ≥ 18 — `node --version`으로 확인
* [Faucet](https://faucet.stablenet.network)에서 테스트넷 WKRC 받기

## Hardhat 프로젝트 생성

```shell theme={null}
mkdir stablenet-hardhat && cd stablenet-hardhat
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox dotenv
npx hardhat init
```

프롬프트에서 **"Create a JavaScript project"** 를 선택합니다.

<Warning>
  개인 키를 `.env` 파일에 저장하고 `.gitignore`에 추가하세요. 버전 관리 시스템에 커밋하지 마세요.
</Warning>

```shell theme={null}
echo "PRIVATE_KEY=0xYourPrivateKey" > .env
echo ".env" >> .gitignore
```

## hardhat.config.js 설정

`hardhat.config.js` 내용을 교체합니다:

```javascript theme={null}
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();

module.exports = {
  solidity: {
    version: "0.8.20",
    settings: {
      // StableNet은 blob 옵코드를 지원하지 않습니다 — cancun 사용 금지
      evmVersion: "shanghai",
    },
  },
  networks: {
    stablenet_testnet: {
      url: "https://api.test.stablenet.network",
      chainId: 8283,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
```

<Warning>
  `evmVersion`을 `cancun`으로 설정하지 마세요. StableNet은 blob 옵코드(`BLOBHASH`, `BLOBBASEFEE`)를 지원하지 않습니다. `paris` 또는 `shanghai`를 사용하세요.
</Warning>

## WKRCPayment.sol 작성

기본 파일을 삭제하고 `contracts/WKRCPayment.sol`을 생성합니다:

```shell theme={null}
rm contracts/Lock.sol
```

```solidity theme={null}
// 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 승인된 발신자로부터 WKRC 결제를 수락합니다.
contract WKRCPayment {
    /// @dev NativeCoinAdapter (WKRC) — StableNet 고정 시스템 컨트랙트 주소.
    IERC20 public constant WKRC =
        IERC20(0x0000000000000000000000000000000000001000);

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

    /// @notice msg.sender로부터 `amount`의 WKRC를 당겨와 `to`에 전송합니다.
    ///         사전에 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);
    }

    function allowanceOf(address owner) external view returns (uint256) {
        return WKRC.allowance(owner, address(this));
    }
}
```

```shell theme={null}
npx hardhat compile
```

```text theme={null}
Compiled 1 Solidity file successfully (evm target: shanghai).
```

## 배포 스크립트 작성

`scripts/deploy.js`를 생성합니다:

```javascript theme={null}
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은 모든 트랜잭션에 maxPriorityFeePerGas가 필요합니다
  const contract = await WKRCPayment.deploy({
    maxPriorityFeePerGas: ethers.parseUnits("27600", "gwei"),
    maxFeePerGas: ethers.parseUnits("80000", "gwei"),
  });

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

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

<Warning>
  모든 배포와 전송 호출에 `maxPriorityFeePerGas`와 `maxFeePerGas`를 전달해야 합니다. Hardhat은 네트워크 적용 최솟값을 자동으로 가져오지 않아, 생략하면 트랜잭션이 거부됩니다.
</Warning>

## Testnet에 배포

```shell theme={null}
npx hardhat run scripts/deploy.js --network stablenet_testnet
```

```text theme={null}
Deploying with: 0xYourWalletAddress
WKRCPayment deployed to: 0xABC...
```

<Note>
  블록 포함까지 최대 1\~2분이 걸릴 수 있습니다. `waitForDeployment()`가 멈춘 것처럼 보이면 트랜잭션은 이미 멤풀에 있습니다. 취소 후 재시도하지 마세요 — 논스 충돌이 발생할 수 있습니다.
</Note>

## Explorer에서 확인

[explorer.stablenet.network](https://explorer.stablenet.network)를 열고 컨트랙트 주소를 검색합니다. 상태가 **Success**인 `Contract Creation` 트랜잭션을 확인할 수 있습니다.

## 다음 단계

<CardGroup cols={2}>
  <Card title="Foundry로 배포하기" href="/ko/v0.3/build/tutorials/deploy-with-foundry">Foundry의 `forge`와 `cast`로 같은 컨트랙트를 배포합니다.</Card>
  <Card title="이벤트 구독하기" href="/ko/v0.3/build/tutorials/listen-to-events">WebSocket으로 온체인 이벤트를 구독합니다.</Card>
  <Card title="RPC API 레퍼런스" href="/ko/v0.3/reference/rpc-api">지원되는 JSON-RPC 메서드 전체 목록.</Card>
  <Card title="수수료 위임" href="/ko/v0.3/build/tutorials/fee-delegation">별도 계정이 사용자 대신 가스를 납부하게 합니다.</Card>
</CardGroup>
