irongit
302 lines10 KBJavaScript
1require('dotenv').config({ path: require('path').resolve(__dirname, '../.env') });
2
3const hre = require("hardhat");
4const { execSync } = require("child_process");
5const { Pool } = require("pg");
6
7// Sample data
8const SAMPLE_PROPERTIES = [
9 {
10 title: "Luxury Downtown Apartment",
11 propertyFor: "sell",
12 description: "Stunning modern apartment in the heart of downtown with panoramic city views. Features high-end finishes, smart home technology, and access to premium amenities.",
13 locality: "Financial District",
14 address: "123 Main Street, Apt 45",
15 price: 850000,
16 priceEth: 425,
17 roiPercentage: 7.2,
18 totalTokens: 85000,
19 availableTokens: 9350,
20 minInvestment: 10,
21 images: ["https://images.unsplash.com/photo-1613490493576-7fde63acd811?w=800&q=80"],
22 },
23 {
24 title: "Modern Tech District Complex",
25 propertyFor: "sell",
26 description: "Prime commercial space in the booming tech corridor. Perfect for startups and established companies alike. High foot traffic and excellent public transit access.",
27 locality: "Tech Hub",
28 address: "456 Innovation Boulevard",
29 price: 1200000,
30 priceEth: 600,
31 roiPercentage: 6.8,
32 totalTokens: 120000,
33 availableTokens: 6000,
34 minInvestment: 10,
35 images: ["https://images.unsplash.com/photo-1512917774080-9991f1c4c750?w=800&q=80"],
36 },
37 {
38 title: "Waterfront Commercial Space",
39 propertyFor: "sell",
40 description: "Exceptional waterfront property with direct water access. Ideal for hospitality or retail ventures. Breathtaking sunset views and ample parking.",
41 locality: "Harbor District",
42 address: "789 Waterfront Drive",
43 price: 2100000,
44 priceEth: 1050,
45 roiPercentage: 7.5,
46 totalTokens: 210000,
47 availableTokens: 115500,
48 minInvestment: 10,
49 images: ["https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=800&q=80"],
50 },
51 {
52 title: "Historic Victorian Estate",
53 propertyFor: "sell",
54 description: "Beautifully restored Victorian mansion with original architectural details. Features 6 bedrooms, 4 bathrooms, and a sprawling garden. Perfect blend of historic charm and modern comfort.",
55 locality: "Heritage Hills",
56 address: "321 Oak Avenue",
57 price: 1750000,
58 priceEth: 875,
59 roiPercentage: 5.9,
60 totalTokens: 175000,
61 availableTokens: 52500,
62 minInvestment: 10,
63 images: ["https://images.unsplash.com/photo-1564013799919-ab600027ffc6?w=800&q=80"],
64 },
65 {
66 title: "Beachfront Vacation Rental",
67 propertyFor: "rent",
68 description: "Stunning beachfront property generating consistent rental income. Fully furnished with luxury amenities. Strong booking history and established vacation rental business.",
69 locality: "Coastal Paradise",
70 address: "555 Ocean View Lane",
71 price: 950000,
72 priceEth: 475,
73 roiPercentage: 8.4,
74 totalTokens: 95000,
75 availableTokens: 28500,
76 minInvestment: 10,
77 images: ["https://images.unsplash.com/photo-1499793983690-e29da59ef1c2?w=800&q=80"],
78 },
79];
80
81async function resetDatabase() {
82 console.log("=== RESETTING DATABASE ===\n");
83
84 try {
85 console.log("Dropping database...");
86 execSync("dbmate drop", { stdio: "inherit" });
87 } catch (e) {
88 // Database might not exist yet, that's ok
89 console.log("(Database didn't exist, continuing...)");
90 }
91
92 console.log("Running migrations...");
93 execSync("dbmate up", { stdio: "inherit" });
94
95 console.log("Database reset complete.\n");
96}
97
98async function seedDatabase(wallets) {
99 console.log("=== SEEDING DATABASE ===\n");
100
101 const pool = new Pool({ connectionString: process.env.DATABASE_URL });
102
103 try {
104 // Create states
105 const stateResult = await pool.query(
106 "INSERT INTO states (name) VALUES ('California'), ('Texas'), ('Washington') RETURNING id, name"
107 );
108 const states = stateResult.rows;
109 console.log(`Created ${states.length} states`);
110
111 // Create cities
112 const cityResult = await pool.query(`
113 INSERT INTO cities (name, state_id) VALUES
114 ('Los Angeles', $1), ('San Francisco', $1), ('Beverly Hills', $1),
115 ('Austin', $2), ('Houston', $2),
116 ('Seattle', $3)
117 RETURNING id, name, state_id
118 `, [states[0].id, states[1].id, states[2].id]);
119 const cities = cityResult.rows;
120 console.log(`Created ${cities.length} cities`);
121
122 // Create property types
123 const typeResult = await pool.query(`
124 INSERT INTO property_types (title, type) VALUES
125 ('Apartment', 'residential'),
126 ('House', 'residential'),
127 ('Commercial Building', 'commercial'),
128 ('Office Space', 'commercial'),
129 ('Farm', 'agricultural')
130 RETURNING id, title
131 `);
132 const propertyTypes = typeResult.rows;
133 console.log(`Created ${propertyTypes.length} property types`);
134
135 // Create test users linked to wallets
136 const [deployer, realtor, seller, buyer] = wallets;
137
138 const userResult = await pool.query(`
139 INSERT INTO users (fname, lname, email, phone_no, password, wallet_address, is_admin) VALUES
140 ('Admin', 'User', 'admin@goldenprop.com', '555-0001', '$2b$10$test', $1, true),
141 ('Rachel', 'Realtor', 'realtor@goldenprop.com', '555-0002', '$2b$10$test', $2, false),
142 ('Sam', 'Seller', 'seller@goldenprop.com', '555-0003', '$2b$10$test', $3, false),
143 ('Bob', 'Buyer', 'buyer@goldenprop.com', '555-0004', '$2b$10$test', $4, false)
144 RETURNING id, fname, lname, wallet_address
145 `, [
146 deployer.address.toLowerCase(),
147 realtor.address.toLowerCase(),
148 seller.address.toLowerCase(),
149 buyer.address.toLowerCase()
150 ]);
151 const users = userResult.rows;
152 console.log(`Created ${users.length} users`);
153
154 console.table(users.map(u => ({
155 name: `${u.fname} ${u.lname}`,
156 wallet: u.wallet_address
157 })));
158
159 // Create properties
160 const propertyIds = [];
161 for (let i = 0; i < SAMPLE_PROPERTIES.length; i++) {
162 const p = SAMPLE_PROPERTIES[i];
163 const city = cities[i % cities.length];
164 const cityId = city.id;
165 const stateId = city.state_id; // Use the city's actual state, not a separate cycle
166 const typeId = propertyTypes[i % propertyTypes.length].id;
167 const slug = p.title.toLowerCase().replace(/\s+/g, '-');
168
169 const result = await pool.query(`
170 INSERT INTO properties (
171 title, property_for, description, type_id, state_id, city_id,
172 locality, length, breadth, address, email, price, price_eth,
173 phone_no, pincode, user_id, slug, images, roi_percentage,
174 total_tokens, available_tokens, min_investment, status
175 ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, 'available')
176 RETURNING id, title
177 `, [
178 p.title, p.propertyFor, p.description, typeId, stateId, cityId,
179 p.locality, 100, 100, p.address, 'contact@goldenprop.com', p.price, p.priceEth,
180 '555-0000', '90210', users[1].id, slug, p.images, p.roiPercentage,
181 p.totalTokens, p.availableTokens, p.minInvestment
182 ]);
183
184 propertyIds.push(result.rows[0].id);
185 console.log(`Created property: ${result.rows[0].title}`);
186 }
187
188 await pool.end();
189 return { users, propertyIds };
190
191 } catch (error) {
192 await pool.end();
193 throw error;
194 }
195}
196
197async function deployContracts(wallets, propertyIds) {
198 console.log("\n=== DEPLOYING SMART CONTRACTS ===\n");
199
200 const [deployer, realtor, seller, buyer] = wallets;
201
202 console.log("Deploying with account:", deployer.address);
203 console.log("Account balance:", hre.ethers.utils.formatEther(await deployer.getBalance()), "ETH");
204
205 // Deploy Factory
206 const Factory = await hre.ethers.getContractFactory("Factory", deployer);
207 const factory = await Factory.deploy();
208 await factory.deployed();
209 console.log("\nFactory deployed to:", factory.address);
210
211 // Create contracts for each property and link to database
212 const pool = new Pool({ connectionString: process.env.DATABASE_URL });
213 const contractAddresses = [];
214
215 for (let i = 0; i < SAMPLE_PROPERTIES.length; i++) {
216 const p = SAMPLE_PROPERTIES[i];
217 const price = hre.ethers.utils.parseEther(p.priceEth.toString());
218 const realtorFee = price.mul(5).div(100); // 5% fee
219
220 const tx = await factory.connect(realtor).create(
221 p.address,
222 "90210",
223 p.locality,
224 realtorFee,
225 price,
226 seller.address,
227 buyer.address
228 );
229 await tx.wait();
230
231 const contractAddress = await factory.getInstance(i);
232 contractAddresses.push(contractAddress);
233
234 // Link contract to property in database
235 await pool.query(
236 "UPDATE properties SET contract_address = $1 WHERE id = $2",
237 [contractAddress.toLowerCase(), propertyIds[i]]
238 );
239
240 // Record deployment transaction
241 await pool.query(`
242 INSERT INTO blockchain_transactions
243 (property_id, contract_address, tx_type, from_address, status)
244 VALUES ($1, $2, 'deploy', $3, 'confirmed')
245 `, [propertyIds[i], contractAddress.toLowerCase(), realtor.address.toLowerCase()]);
246
247 console.log(`Property ${i + 1}: ${p.title}`);
248 console.log(` Contract: ${contractAddress}`);
249 }
250
251 await pool.end();
252 return { factory, contractAddresses };
253}
254
255async function main() {
256 const mnemonic = "test test test test test test test test test test test junk";
257
258 // Derive wallets
259 const roles = ["Deployer", "Realtor", "Seller", "Buyer"];
260 const wallets = roles.map((_, i) => {
261 const path = `m/44'/60'/0'/0/${i}`;
262 return hre.ethers.Wallet.fromMnemonic(mnemonic, path).connect(hre.ethers.provider);
263 });
264
265 console.log("=== KEY DERIVATION ===\n");
266 console.log(`Mnemonic: "${mnemonic}"\n`);
267
268 console.table(wallets.map((w, i) => ({
269 role: roles[i],
270 path: `m/44'/60'/0'/0/${i}`,
271 address: w.address,
272 privateKey: w.privateKey
273 })));
274
275 // Reset and seed database
276 await resetDatabase();
277 const { users, propertyIds } = await seedDatabase(wallets);
278
279 // Deploy contracts
280 const { factory, contractAddresses } = await deployContracts(wallets, propertyIds);
281
282 console.log("\n=== DEPLOYMENT COMPLETE ===\n");
283 console.log("Factory:", factory.address);
284 console.log("Properties with contracts:", contractAddresses.length);
285
286 console.log("\n=== ENVIRONMENT VARIABLES ===\n");
287 console.log(`REACT_APP_FACTORY_ADDRESS=${factory.address}`);
288
289 console.log("\n=== METAMASK SETUP ===\n");
290 console.log("Network Name: Hardhat Local");
291 console.log("RPC URL: http://127.0.0.1:8545");
292 console.log("Chain ID: 31337");
293 console.log("Currency: ETH");
294 console.log("\nImport accounts using private keys from table above");
295}
296
297main()
298 .then(() => process.exit(0))
299 .catch((error) => {
300 console.error(error);
301 process.exit(1);
302 });