1+ import { createWalletClient , createPublicClient , http , parseEther } from 'viem'
2+ import { privateKeyToAccount } from 'viem/accounts'
3+ import { defineChain } from 'viem'
4+ import * as fs from 'fs'
5+ import path from 'path'
6+
7+ // 定义 Polkadot Hub TestNet
8+ const polkadotHubTestnet = defineChain ( {
9+ id : 420420422 ,
10+ name : 'Polkadot Hub TestNet' ,
11+ network : 'polkadot-hub-testnet' ,
12+ nativeCurrency : {
13+ decimals : 18 ,
14+ name : 'PAS' ,
15+ symbol : 'PAS' ,
16+ } ,
17+ rpcUrls : {
18+ default : {
19+ http : [ 'https://testnet-passet-hub-eth-rpc.polkadot.io' ] ,
20+ } ,
21+ public : {
22+ http : [ 'https://testnet-passet-hub-eth-rpc.polkadot.io' ] ,
23+ } ,
24+ } ,
25+ blockExplorers : {
26+ default : {
27+ name : 'Blockscout' ,
28+ url : 'https://blockscout-passet-hub.parity-testnet.parity.io/' ,
29+ } ,
30+ } ,
31+ testnet : true ,
32+ } )
33+
34+ // 从环境变量获取私钥
35+
36+ const PRIVATE_KEY = process . env . PRIVATE_KEY as `0x${string } `
37+ if ( ! PRIVATE_KEY ) {
38+ throw new Error ( '请设置 PRIVATE_KEY 环境变量' )
39+ }
40+
41+ // 创建客户端
42+ const walletClient = createWalletClient ( {
43+ chain : polkadotHubTestnet ,
44+ transport : http ( )
45+ } )
46+
47+ const publicClient = createPublicClient ( {
48+ chain : polkadotHubTestnet ,
49+ transport : http ( )
50+ } )
51+
52+ // 从部署账户
53+ const account = privateKeyToAccount ( PRIVATE_KEY )
54+
55+ // 读取合约 ABI 和字节码
56+ function getContractArtifacts ( contractName : string ) {
57+ const artifactPath = path . join ( __dirname , `../artifacts-pvm/contracts/${ contractName } .sol/${ contractName } .json` )
58+ const artifact = JSON . parse ( fs . readFileSync ( artifactPath , 'utf8' ) )
59+ return {
60+ abi : artifact . abi ,
61+ bytecode : artifact . bytecode
62+ }
63+ }
64+
65+ async function main ( ) {
66+ console . log ( "=====================================" )
67+ console . log ( "🚀 箭塔村 NFT 项目部署脚本" )
68+ console . log ( "📱 网络: Polkadot Hub TestNet" )
69+ console . log ( "=====================================" )
70+ console . log ( "部署者地址:" , account . address )
71+
72+ // 获取余额
73+ const balance = await publicClient . getBalance ( { address : account . address } )
74+ console . log ( "部署者余额:" , parseEther ( balance . toString ( ) ) , "PAS\n" )
75+
76+ // ========================================
77+ // 阶段 1: 独立部署 NFT 合约
78+ // ========================================
79+ console . log ( "=====================================" )
80+ console . log ( "📦 阶段 1: 独立部署 ArrowTowerNFT 合约" )
81+ console . log ( "=====================================" )
82+
83+ const nftName = "Arrow Tower Village NFT"
84+ const nftSymbol = "ATVNFT"
85+ const nftBaseURI = "https://arrowtower.netlify.app/metadata/"
86+
87+ console . log ( "NFT 配置参数:" )
88+ console . log ( ` 名称: ${ nftName } ` )
89+ console . log ( ` 符号: ${ nftSymbol } ` )
90+ console . log ( ` 基础URI: ${ nftBaseURI } ` )
91+ console . log ( "\n正在部署..." )
92+
93+ const nftArtifacts = getContractArtifacts ( "ArrowTowerNFT" )
94+
95+ const nftHash = await walletClient . deployContract ( {
96+ account,
97+ abi : nftArtifacts . abi ,
98+ bytecode : nftArtifacts . bytecode as `0x${string } `,
99+ args : [ nftName , nftSymbol , nftBaseURI ]
100+ } )
101+
102+ // 等待交易确认
103+ const nftReceipt = await publicClient . waitForTransactionReceipt ( { hash : nftHash } )
104+ if ( ! nftReceipt . contractAddress ) {
105+ throw new Error ( 'NFT 合约部署失败' )
106+ }
107+
108+ const standaloneNFTAddr = nftReceipt . contractAddress
109+ console . log ( "✅ ArrowTowerNFT 部署成功" )
110+ console . log ( " 交易哈希:" , nftHash )
111+ console . log ( " 合约地址:" , standaloneNFTAddr )
112+ console . log ( " 区块高度:" , nftReceipt . blockNumber )
113+ console . log ( " 状态: 已初始化并可用\n" )
114+
115+ // 验证 NFT 基础信息
116+ const nftNameCheck = await publicClient . readContract ( {
117+ address : standaloneNFTAddr ,
118+ abi : nftArtifacts . abi ,
119+ functionName : 'name'
120+ } )
121+
122+ const nftSymbolCheck = await publicClient . readContract ( {
123+ address : standaloneNFTAddr ,
124+ abi : nftArtifacts . abi ,
125+ functionName : 'symbol'
126+ } )
127+
128+ const nftTotalSupply = await publicClient . readContract ( {
129+ address : standaloneNFTAddr ,
130+ abi : nftArtifacts . abi ,
131+ functionName : 'totalSupply'
132+ } )
133+
134+ console . log ( "NFT 合约验证:" )
135+ console . log ( ` 名称匹配: ${ nftNameCheck === nftName ? "✓" : "✗" } ` )
136+ console . log ( ` 符号匹配: ${ nftSymbolCheck === nftSymbol ? "✓" : "✗" } ` )
137+ console . log ( ` 初始供应量: ${ nftTotalSupply } \n` )
138+
139+ // ========================================
140+ // 阶段 2: 独立部署 Minter 合约
141+ // ========================================
142+ console . log ( "=====================================" )
143+ console . log ( "📦 阶段 2: 独立部署 ArrowTowerMinter 合约" )
144+ console . log ( "=====================================" )
145+
146+ console . log ( "Minter 配置参数:" )
147+ console . log ( ` NFT 合约地址: ${ standaloneNFTAddr } ` )
148+ console . log ( "\n正在部署..." )
149+
150+ const minterArtifacts = getContractArtifacts ( "ArrowTowerMinter" )
151+
152+ const minterHash = await walletClient . deployContract ( {
153+ account,
154+ abi : minterArtifacts . abi ,
155+ bytecode : minterArtifacts . bytecode as `0x${string } `,
156+ args : [ standaloneNFTAddr ]
157+ } )
158+
159+ // 等待交易确认
160+ const minterReceipt = await publicClient . waitForTransactionReceipt ( { hash : minterHash } )
161+ if ( ! minterReceipt . contractAddress ) {
162+ throw new Error ( 'Minter 合约部署失败' )
163+ }
164+
165+ const standaloneMinterAddr = minterReceipt . contractAddress
166+ console . log ( "✅ ArrowTowerMinter 部署成功" )
167+ console . log ( " 交易哈希:" , minterHash )
168+ console . log ( " 合约地址:" , standaloneMinterAddr )
169+ console . log ( " 区块高度:" , minterReceipt . blockNumber )
170+ console . log ( " 状态: 已初始化并可用\n" )
171+
172+ // 验证 Minter 基础信息
173+ const minterNFTAddr = await publicClient . readContract ( {
174+ address : standaloneMinterAddr ,
175+ abi : minterArtifacts . abi ,
176+ functionName : 'nftContract'
177+ } )
178+
179+ const minterPaused = await publicClient . readContract ( {
180+ address : standaloneMinterAddr ,
181+ abi : minterArtifacts . abi ,
182+ functionName : 'paused'
183+ } )
184+
185+ const minterOwner = await publicClient . readContract ( {
186+ address : standaloneMinterAddr ,
187+ abi : minterArtifacts . abi ,
188+ functionName : 'owner'
189+ } )
190+
191+ console . log ( "Minter 合约验证:" )
192+ console . log ( ` NFT 地址匹配: ${ minterNFTAddr === standaloneNFTAddr ? "✓" : "✗" } ` )
193+ console . log ( ` 暂停状态: ${ minterPaused } ` )
194+ console . log ( ` 所有者: ${ minterOwner } \n` )
195+
196+ // ========================================
197+ // 阶段 3: 绑定 NFT 和 Minter 合约
198+ // ========================================
199+ console . log ( "=====================================" )
200+ console . log ( "🔗 阶段 3: 绑定 NFT 和 Minter 合约" )
201+ console . log ( "=====================================\n" )
202+
203+ console . log ( "设置 Minter 为 NFT 的授权铸造者..." )
204+
205+ const setMinterHash = await walletClient . writeContract ( {
206+ account,
207+ address : standaloneNFTAddr ,
208+ abi : nftArtifacts . abi ,
209+ functionName : 'setMinterContract' ,
210+ args : [ standaloneMinterAddr ]
211+ } )
212+
213+ await publicClient . waitForTransactionReceipt ( { hash : setMinterHash } )
214+
215+ const currentMinter = await publicClient . readContract ( {
216+ address : standaloneNFTAddr ,
217+ abi : nftArtifacts . abi ,
218+ functionName : 'minterContract'
219+ } )
220+
221+ console . log ( "✅ Minter 绑定成功" )
222+ console . log ( ` 交易哈希: ${ setMinterHash } ` )
223+ console . log ( ` 当前 Minter: ${ currentMinter } ` )
224+ console . log ( ` 绑定验证: ${ currentMinter === standaloneMinterAddr ? "✓" : "✗" } \n` )
225+
226+ // ========================================
227+ // 部署信息汇总
228+ // ========================================
229+ console . log ( "=====================================" )
230+ console . log ( "📋 部署信息汇总" )
231+ console . log ( "=====================================" )
232+ console . log ( `网络: ${ polkadotHubTestnet . name } ` )
233+ console . log ( `链ID: ${ polkadotHubTestnet . id } ` )
234+ console . log ( `NFT 合约地址: ${ standaloneNFTAddr } ` )
235+ console . log ( `Minter 合约地址: ${ standaloneMinterAddr } ` )
236+ console . log ( `部署者地址: ${ account . address } ` )
237+ console . log ( `区块浏览器: ${ polkadotHubTestnet . blockExplorers . default . url } ` )
238+ console . log ( "=====================================\n" )
239+
240+ // 保存部署信息到文件
241+ const deploymentInfo = {
242+ network : polkadotHubTestnet . name ,
243+ chainId : polkadotHubTestnet . id ,
244+ nftContract : standaloneNFTAddr ,
245+ minterContract : standaloneMinterAddr ,
246+ deployer : account . address ,
247+ deploymentTime : new Date ( ) . toISOString ( ) ,
248+ blockExplorer : polkadotHubTestnet . blockExplorers . default . url ,
249+ transactions : {
250+ nftDeployment : nftHash ,
251+ minterDeployment : minterHash ,
252+ setMinter : setMinterHash
253+ }
254+ }
255+
256+ fs . writeFileSync (
257+ path . join ( __dirname , '../deployment-info.json' ) ,
258+ JSON . stringify ( deploymentInfo , null , 2 )
259+ )
260+
261+ console . log ( "📄 部署信息已保存到 deployment-info.json" )
262+
263+ // 提供区块浏览器链接
264+ console . log ( "🔍 区块浏览器链接:" )
265+ console . log ( ` NFT 部署交易: ${ polkadotHubTestnet . blockExplorers . default . url } /tx/${ nftHash } ` )
266+ console . log ( ` Minter 部署交易: ${ polkadotHubTestnet . blockExplorers . default . url } /tx/${ minterHash } ` )
267+ console . log ( ` Minter 绑定交易: ${ polkadotHubTestnet . blockExplorers . default . url } /tx/${ setMinterHash } ` )
268+ console . log ( ` NFT 合约: ${ polkadotHubTestnet . blockExplorers . default . url } /address/${ standaloneNFTAddr } ` )
269+ console . log ( ` Minter 合约: ${ polkadotHubTestnet . blockExplorers . default . url } /address/${ standaloneMinterAddr } ` )
270+ }
271+
272+ main ( ) . catch ( ( error ) => {
273+ console . error ( "部署发生错误:" , error )
274+ process . exit ( 1 )
275+ } )
0 commit comments