StableNet은 Anzeon EVM 을 실행합니다 — go-ethereum 포크로 London + Shanghai 옵코드와 Cancun 일부(blob 제외)를 구현합니다. 표준 EVM 개발 툴은 수정 없이 그대로 작동하며, StableNet에서만 반드시 지켜야 할 두 가지 사항이 있습니다.
사전 준비
이 페이지의 툴 스니펫을 사용하기 전에 아래 항목을 준비하세요:
Foundry (forge, cast) 또는 Hardhat 설치
가스비 납부를 위한 WKRC 잔액이 있는 테스트넷 지갑
Faucet 무료 테스트넷 WKRC 받기 — 회원가입 불필요.
EVM 버전
항목 값 기반 go-ethereum 포크 (Anzeon WBFT) EVM 명령어 세트 London + Shanghai + partial Cancun PUSH0 ✅ 지원 (Shanghai) Blob 트랜잭션 (type 0x03) ❌ 미지원 최대 콜 스택 깊이 1,024 Solidity 타겟 paris 또는 shanghai 권장
Blob 옵코드(BLOBHASH, BLOBBASEFEE)는 사용할 수 없습니다. 컴파일러 설정에서 --evm-version cancun을 사용하지 마세요.
지원 툴
아래 모든 툴은 표준 테스트넷 RPC와 Chain ID로 작동합니다. 스니펫을 복사해 플레이스홀더 값만 교체하세요.
툴 상태 비고 Foundry (forge, cast) ✅ 모든 브로드캐스트에 --priority-gas-price 필수 Hardhat ✅ 배포 스크립트에 maxPriorityFeePerGas 설정 필요 MetaMask ✅ wallet_addEthereumChain으로 커스텀 네트워크 추가Remix IDE ✅ “External HTTP Provider”로 테스트넷 RPC 연결 ethers.js / viem ✅ 각 트랜잭션에 maxPriorityFeePerGas 전달 필요
Foundry
foundry.toml에 StableNet 테스트넷 엔드포인트를 추가합니다:
[ profile . default ]
src = "src"
out = "out"
libs = [ "lib" ]
# Blob 옵코드 미지원 — paris 또는 shanghai 사용
evm_version = "shanghai"
[ rpc_endpoints ]
stablenet_testnet = "https://api.test.stablenet.network"
모든 forge script와 cast send 브로드캐스트에 --priority-gas-price가 필요합니다:
forge script script/Deploy.s.sol \
--rpc-url https://api.test.stablenet.network \
--private-key $PRIVATE_KEY \
--priority-gas-price 27600000000000 \
--broadcast
cast send $CONTRACT "myFunction()" \
--rpc-url https://api.test.stablenet.network \
--private-key $PRIVATE_KEY \
--priority-gas-price 27600000000000
Hardhat
// hardhat.config.js
require ( "@nomicfoundation/hardhat-toolbox" );
module . exports = {
solidity: {
version: "0.8.20" ,
settings: {
evmVersion: "shanghai" , // cancun 사용 금지
},
},
networks: {
stablenet_testnet: {
url: "https://api.test.stablenet.network" ,
chainId: 8283 ,
accounts: [ process . env . PRIVATE_KEY ],
},
},
};
배포 스크립트에서 Priority Fee를 지정합니다:
// scripts/deploy.js
const { ethers } = require ( "hardhat" );
async function main () {
const MyContract = await ethers . getContractFactory ( "MyContract" );
const contract = await MyContract . deploy ({
maxPriorityFeePerGas: ethers . parseUnits ( "27600" , "gwei" ),
});
await contract . waitForDeployment ();
console . log ( "Deployed to:" , await contract . getAddress ());
}
main (). catch ( console . error );
브라우저 콘솔에서 아래 코드를 실행해 StableNet 테스트넷을 MetaMask에 추가합니다:
await window . ethereum . request ({
method: "wallet_addEthereumChain" ,
params: [
{
chainId: "0x205B" , // 8283 (hex)
chainName: "StableNet Testnet" ,
nativeCurrency: {
name: "WKRC" ,
symbol: "WKRC" ,
decimals: 18 ,
},
rpcUrls: [ "https://api.test.stablenet.network" ],
blockExplorerUrls: [ "https://explorer.stablenet.network" ],
},
],
});
Remix IDE
Remix IDE 를 엽니다
Deploy & Run 패널에서 Environment 를 External HTTP Provider 로 설정합니다
RPC URL 입력창에 https://api.test.stablenet.network를 입력합니다
Remix가 Chain ID 8283을 자동으로 감지합니다
Remix UI는 maxPriorityFeePerGas 필드를 직접 노출하지 않습니다. MetaMask를 통한 트랜잭션은 Priority Fee 설정이 부족하면 실패할 수 있습니다. 안정적인 배포에는 Foundry 또는 Hardhat을 사용하세요.
ethers.js
테스트넷에 연결하고 모든 트랜잭션에 maxPriorityFeePerGas를 지정합니다:
import { ethers } from "ethers" ;
const provider = new ethers . JsonRpcProvider ( "https://api.test.stablenet.network" );
const wallet = new ethers . Wallet ( process . env . PRIVATE_KEY , provider );
// StableNet은 27,600 Gwei 최솟값을 강제합니다 — 모든 트랜잭션에 포함
const tx = await wallet . sendTransaction ({
to: "0xRecipientAddress" ,
value: ethers . parseEther ( "1.0" ),
maxPriorityFeePerGas: ethers . parseUnits ( "27600" , "gwei" ),
});
await tx . wait ();
console . log ( "confirmed in block" , tx . blockNumber );
viem
StableNet 테스트넷 체인 객체를 한 번 정의하고 클라이언트에서 재사용합니다:
import {
createPublicClient ,
createWalletClient ,
http ,
parseEther ,
parseGwei ,
} from "viem" ;
import { privateKeyToAccount } from "viem/accounts" ;
const stablenetTestnet = {
id: 8283 ,
name: "StableNet Testnet" ,
nativeCurrency: { name: "WKRC" , symbol: "WKRC" , decimals: 18 },
rpcUrls: {
default: { http: [ "https://api.test.stablenet.network" ] },
},
};
const publicClient = createPublicClient ({
chain: stablenetTestnet ,
transport: http (),
});
const account = privateKeyToAccount ( process . env . PRIVATE_KEY );
const walletClient = createWalletClient ({
chain: stablenetTestnet ,
transport: http (),
account ,
});
// StableNet은 27,600 Gwei 최솟값을 강제합니다 — 모든 트랜잭션에 포함
const hash = await walletClient . sendTransaction ({
to: "0xRecipientAddress" ,
value: parseEther ( "1.0" ),
maxPriorityFeePerGas: parseGwei ( "27600" ),
});
StableNet 특유 동작
최소 Priority Fee: 27,600 Gwei
GovValidator 거버넌스 컨트랙트가 네트워크 전체에 최소 Priority Fee를 강제합니다. 이 값 미만의 maxPriorityFeePerGas를 가진 트랜잭션은 트랜잭션 풀에서 즉시 거절됩니다.
항목 값 최소 maxPriorityFeePerGas 27,600 Gwei (27600000000000 wei) 적용 지점 트랜잭션 풀 — 블록 포함 전 거절
컨트랙트 배포, 함수 호출, 네이티브 토큰 전송 등 모든 트랜잭션 타입 에 적용됩니다.
현재 강제 값을 조회하려면:
curl -X POST https://api.test.stablenet.network \
-H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_maxPriorityFeePerGas","params":[],"id":1}'
Ethereum과 달리, 이 메서드는 최근 블록 기반 예측값이 아닌 GovValidator 컨트랙트의 거버넌스 값을 반환합니다.
가스 정책
Base Fee : 동적 (네트워크 사용률에 따라 조정), 소각되지 않고 검증자에게 지급
Priority Fee : 거버넌스 최솟값, GovValidator가 강제
블록 가스 한도 : 105,000,000
블록 생성 시간 : ~1초
수수료 위임 (트랜잭션 타입 0x16)
StableNet은 별도 계정(FeePayer)이 발신자 대신 가스를 납부할 수 있는 수수료 위임 트랜잭션 타입을 지원합니다. 사용자 가스비를 앱이 대신 부담하는 UX에 활용할 수 있습니다.
항목 값 트랜잭션 타입 0x16 (decimal 22)활성화 조건 Applepie 포크 필요 서명 2개 — 발신자 + 수수료 납부자 (독립 서명) EVM 내 tx.origin 항상 발신자 (수수료 납부자 아님)
수수료 위임 트랜잭션 서명에는 StableNet RPC 확장 메서드를 사용합니다:
RPC 메서드 설명 personal_signRawFeeDelegateTransaction패스워드 언락 계정으로 수수료 납부자 서명 eth_signRawFeeDelegateTransaction이미 언락된 계정으로 수수료 납부자 서명
서명이 완료된 트랜잭션은 eth_sendRawTransaction으로 제출합니다.
ethers.js 서명 흐름 (provider.send)
타입 0x16은 StableNet 전용 트랜잭션 타입입니다. provider.send()로 RPC 메서드를 직접 호출합니다:
import { ethers } from "ethers" ;
const provider = new ethers . JsonRpcProvider ( "https://api.test.stablenet.network" );
const SENDER = "0xSenderAddress" ;
const FEE_PAYER = "0xFeePayerAddress" ;
const RECIPIENT = "0xRecipientAddress" ;
// Step 1: 발신자가 type 0x16 트랜잭션에 서명
// (노드에서 SENDER 계정이 언락되어 있어야 합니다)
const senderSignedRlp = await provider . send ( "eth_signTransaction" , [{
type: "0x16" ,
from: SENDER ,
to: RECIPIENT ,
value: ethers . toBeHex ( ethers . parseEther ( "1.0" )),
gas: "0x5208" ,
maxFeePerGas: ethers . toBeHex ( ethers . parseUnits ( "50000" , "gwei" )),
maxPriorityFeePerGas: ethers . toBeHex ( ethers . parseUnits ( "27600" , "gwei" )),
nonce: ethers . toBeHex ( await provider . getTransactionCount ( SENDER )),
chainId: "0x205B" , // 8283
feePayer: FEE_PAYER ,
}]);
// Step 2: 수수료 납부자가 독립 서명 추가
// eth_signRawFeeDelegateTransaction (계정 이미 언락)
// 또는 personal_signRawFeeDelegateTransaction (패스워드 언락)
const fullySignedRlp = await provider . send ( "eth_signRawFeeDelegateTransaction" , [
senderSignedRlp ,
FEE_PAYER ,
]);
// Step 3: 브로드캐스트 — 가스는 FEE_PAYER, 전송값은 SENDER에서 차감
const txHash = await provider . send ( "eth_sendRawTransaction" , [ fullySignedRlp ]);
console . log ( "txHash:" , txHash );
EVM 내부에서 tx.origin은 항상 발신자 (SENDER)입니다. 수수료 납부자가 아닙니다. 스마트 컨트랙트는 수수료 위임 사용 여부를 감지할 수 없습니다.
네이티브 토큰의 ERC-20 이중성
네이티브 가스 코인(KRC)은 NativeCoinAdapter 시스템 컨트랙트를 통해 동시에 표준 ERC-20 토큰으로도 작동합니다. 별도 래핑이 필요 없으며, 가스에 사용되는 잔액과 balanceOf()로 조회되는 잔액이 동일합니다.
항목 값 컨트랙트 주소 0x0000000000000000000000000000000000001000심볼 WKRC소수점 18인터페이스 ERC-20, FiatTokenV2_2 (USDC 호환)
표준 approve / transferFrom 패턴이 Ethereum과 동일하게 작동합니다.
미지원 / 제한 기능
기능 상태 비고 Blob 트랜잭션 (type 0x03) ❌ 미지원 Anzeon에 blob Cancun 옵코드 없음 BLOBHASH 옵코드 (0x49)❌ 미지원 BLOBBASEFEE 옵코드 (0x4a)❌ 미지원 eth_maxPriorityFeePerGas 오라클 동작⚠️ 동작 다름 거버넌스 값 반환 (예측값 아님) personal / admin / debug 네임스페이스⚠️ 공개 RPC 미노출 로컬/개발 노드에서만 사용 가능 27,600 Gwei 미만 Gas Tip ❌ 거절 트랜잭션 풀 강제 적용
RPC 네임스페이스
공개 RPC 엔드포인트에서 사용 가능한 네임스페이스:
네임스페이스 사용 가능 eth✅ net✅ web3✅ txpool✅ istanbul✅ (WBFT 합의 정보) personal / admin / debug로컬 / 개발 노드 전용
다음 단계
첫 컨트랙트 배포 Foundry 단계별 튜토리얼 — WKRC 결제 컨트랙트를 약 15분 안에 배포합니다.
네트워크 정보 Chain ID, RPC 엔드포인트, 시스템 컨트랙트 주소.
API 레퍼런스 curl 및 ethers.js 예제가 포함된 전체 JSON-RPC 메서드 레퍼런스.
Explorer 온체인에서 트랜잭션, 컨트랙트, 계정을 조회합니다.
테스트넷 WKRC 받기 Faucet에서 무료 테스트넷 WKRC를 요청합니다.