Skip to content

Commit 0a06683

Browse files
author
shell
committed
feat: add frontend contract deployment functionality with wallet connection and Polkadot Hub TestNet integration
1 parent 42ffc60 commit 0a06683

9 files changed

Lines changed: 2438 additions & 157 deletions

File tree

contracts/ArrowTowerMinter.json

Lines changed: 452 additions & 0 deletions
Large diffs are not rendered by default.

contracts/ArrowTowerNFT.json

Lines changed: 293 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
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+
})

docker-compose.yml

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)