forked from rmrk-team/evm
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdeploy_diamond_equippable.ts
499 lines (427 loc) · 19.2 KB
/
deploy_diamond_equippable.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
import { Contract } from 'ethers';
import { ethers } from 'hardhat';
import { getSelectors, FacetCutAction } from './libraries/diamond';
const HARDHAT_NETWORK_CHAIN_ID = 31337;
const PUBLIC_ACCOUNT_PRIVATE_KEY =
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
const TRANSACTION_SIGNER_ADDRESS = '0xFBa50dD46Af71D60721C6E38F40Bce4d2416A34B';
export const version = '0.5.0-alpha';
export const create2DeployerAddress = '0xcf2281070e6a50e4050694eef1a9a7376628d663';
async function isHardhatChain() {
const network = await ethers.provider.getNetwork();
const chainId = network.chainId;
if (chainId === HARDHAT_NETWORK_CHAIN_ID) {
return true;
}
return false;
}
/**
* @param usePreSign Will use pre-sign transaction by default to make sure `create2Deployer` address is always the same on every chain, but if you suffer `legacy pre-eip-155 transactions not supported`,
* try call this function with `false` to not use pre-sign transaction (NOTE this will make `create2Deployer` address differ).
* @returns create2DeployerAddress
*/
export async function deployCreate2Deployer(usePreSign = true) {
// if is in local chain, send some value to transaction signer address
if (await isHardhatChain()) {
const publicAccount = new ethers.Wallet(PUBLIC_ACCOUNT_PRIVATE_KEY, ethers.provider);
const tx = await publicAccount.sendTransaction({
to: TRANSACTION_SIGNER_ADDRESS,
value: ethers.constants.WeiPerEther.mul(20),
});
await tx.wait();
}
if (usePreSign) {
// This pre-signed transaction is signed by 0xFBa50dD46Af71D60721C6E38F40Bce4d2416A34B,
// gasLimit at 300_000, gasPrice at 100Gwei.
// The EIP-155 is disabled to make sure transaction can be replayed on every single EVM chain,
// to let Create2Deployer address same and then every facet contract address will be same on
// every EVM chain.
// NOTE that now a lot of RPC nodes will not support pre-signed transaction without `chainId`,
// because it's a default config in go-ethereum client, to be able to send pre-signed transaction below,
// you can install `geth`(or similar client from other chain) yourself and add `--rpc.allow-unprotected-txs`
// flag when you run the full node, and you can also find and try available RPCs on https://chainlist.org.
const preSignedTransaction =
'0xf904158085174876e800830493e08080b903c2608060405234801561001057600080fd5b506103a2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063481286e61461004657806366cfa057146100755780637806530614610088575b600080fd5b61005961005436600461022b565b61009b565b6040516001600160a01b03909116815260200160405180910390f35b610059610083366004610263565b6100af565b610059610096366004610327565b6100c4565b60006100a88383306100c4565b9392505050565b60006100bc848484610121565b949350505050565b604080516001600160f81b03196020808301919091526bffffffffffffffffffffffff19606085901b16602183015260358201869052605580830186905283518084039091018152607590920190925280519101206000906100bc565b600080844710156101795760405162461bcd60e51b815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b82516000036101ca5760405162461bcd60e51b815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f6044820152606401610170565b8383516020850187f590506001600160a01b0381166100bc5760405162461bcd60e51b815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f79000000000000006044820152606401610170565b6000806040838503121561023e57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561027857600080fd5b8335925060208401359150604084013567ffffffffffffffff8082111561029e57600080fd5b818601915086601f8301126102b257600080fd5b8135818111156102c4576102c461024d565b604051601f8201601f19908116603f011681019083821181831017156102ec576102ec61024d565b8160405282815289602084870101111561030557600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60008060006060848603121561033c57600080fd5b833592506020840135915060408401356001600160a01b038116811461036157600080fd5b80915050925092509256fea26469706673582212201f9fe2803ac8899d261e3d051dcdeb776303ddea50a3f731d492db8a2fab05f964736f6c634300081000331ba069cb3319d1af304d3e28e72959c77c4bdec0c32fe1dfe36809fa843406615411a02c7cb4752a8fdb2793e6b2659903aa95b8258123770a1ebdab66e901f56c883b';
const transactionRes = await ethers.provider.sendTransaction(preSignedTransaction);
console.log('Transaction Response is', transactionRes);
} else {
console.log(
'Will not use pre-signed transaction, make sure you use the correct create2Deployer address in the next deploying process',
);
const signer = (await ethers.getSigners())[0];
const create2DeployerFactory = await ethers.getContractFactory('Create2Deployer', signer);
const create2Deployer = await create2DeployerFactory.deploy();
await create2Deployer.deployed();
console.log('Transaction Response is', create2Deployer.deployTransaction);
return create2Deployer.address;
}
return create2DeployerAddress;
}
/**
* @param create2DeployerAddress The deployer used for deploy contract in `CREATE2` way
* @param deployed Mark if "the contract waiting to be deployed" is already `deployed`,
* when set to `true`, this function will only compute the address of all facets.
* @param overrideOrAddOn Can be set to add-on / override some config, such as `toBeRemovedFunctions`, `FacetNames` etc.
*/
export async function oneTimeDeploy(
create2DeployerAddress: string,
deployed = false,
noNormalDeploy = false,
overrideOrAddOn?: {
addOn?: { toBeRemovedFunctions?: { [k: string]: string[] } };
override?: { FacetNames?: string[]; useNormalDeploy?: Record<string, boolean> };
},
) {
const create2Deployer = await ethers.getContractAt('Create2Deployer', create2DeployerAddress);
const RMRKMultiAssetRenderUtils = await ethers.getContractFactory('RMRKMultiAssetRenderUtils');
const LightmValidatorLib = await ethers.getContractFactory('LightmValidatorLib');
// ---------- Normal deployment
// if (!deployed) {
// const rmrkMultiAssetRenderUtils = await RMRKMultiAssetRenderUtils.deploy();
// await rmrkMultiAssetRenderUtils.deployed();
// }
// -----------------
// ---------- Create2 deployment
const rmrkMultiAssetRenderUtilsHash = ethers.utils.id(`RMRKMultiAssetRenderUtils-${version}`);
if (!deployed) {
await create2Deployer.deploy(
0,
rmrkMultiAssetRenderUtilsHash,
RMRKMultiAssetRenderUtils.bytecode,
);
}
const rmrkMultiAssetRenderUtilsAddress: string = await create2Deployer[
'computeAddress(bytes32,bytes32)'
](rmrkMultiAssetRenderUtilsHash, ethers.utils.keccak256(RMRKMultiAssetRenderUtils.bytecode));
const rmrkMultiAssetRenderUtils = await ethers.getContractAt(
'LightmValidatorLib',
rmrkMultiAssetRenderUtilsAddress,
);
console.log('RMRKMultiAssetRender Utils deployed', rmrkMultiAssetRenderUtils.address);
// ------------------
// ---------- Normal deployment
// if (!deployed) {
// const lightmValidatorLib = await LightmValidatorLib.deploy();
// await lightmValidatorLib.deployed();
// }
// -----------------
// ---------- Create2 deployment
const lightmValidatorLibHash = ethers.utils.id(`LightmValidatorLib-${version}`);
if (!deployed) {
await create2Deployer.deploy(0, lightmValidatorLibHash, LightmValidatorLib.bytecode);
}
const lightmValidatorLibAddress: string = await create2Deployer[
'computeAddress(bytes32,bytes32)'
](lightmValidatorLibHash, ethers.utils.keccak256(LightmValidatorLib.bytecode));
const lightmValidatorLib = await ethers.getContractAt(
'LightmValidatorLib',
lightmValidatorLibAddress,
);
console.log('RMRKValidator Lib deployed', lightmValidatorLib.address);
// ------------------
const LightmEquippableRenderUtils = await ethers.getContractFactory(
'LightmEquippableRenderUtils',
{ libraries: { LightmValidatorLib: lightmValidatorLibAddress } },
);
// ---------- Normal deployment
// if (!deployed) {
// const lightmEquippableRenderUtils = await LightmEquippableRenderUtils.deploy();
// await lightmEquippableRenderUtils.deployed();
// }
// -----------------
// ---------- Create2 deployment
const lightmEquippableRenderUtilsHash = ethers.utils.id(`LightmEquippableRenderUtils-${version}`);
if (!deployed) {
await create2Deployer.deploy(
0,
lightmEquippableRenderUtilsHash,
LightmEquippableRenderUtils.bytecode,
);
}
const lightmEquippableRenderUtilsAddress: string = await create2Deployer[
'computeAddress(bytes32,bytes32)'
](lightmEquippableRenderUtilsHash, ethers.utils.keccak256(LightmEquippableRenderUtils.bytecode));
const lightmEquippableRenderUtils = await ethers.getContractAt(
'LightmValidatorLib',
lightmEquippableRenderUtilsAddress,
);
console.log('LightmEquippableRender Utils deployed', lightmEquippableRenderUtils.address);
// ------------------
// deploy DiamondCutFacet
const DiamondCutFacet = await ethers.getContractFactory('DiamondCutFacet');
// ---------- Normal deployment
// if (!deployed) {
// const diamondCutFacet = await DiamondCutFacet.deploy();
// await diamondCutFacet.deployed();
// }
// -----------------
// ---------- Create2 deployment
const diamondCutFacetHash = ethers.utils.id(`DiamondCutFacet-${version}`);
if (!deployed) {
await create2Deployer.deploy(0, diamondCutFacetHash, DiamondCutFacet.bytecode);
}
const diamondCutFacetAddress: string = await create2Deployer['computeAddress(bytes32,bytes32)'](
diamondCutFacetHash,
ethers.utils.keccak256(DiamondCutFacet.bytecode),
);
const diamondCutFacet = await ethers.getContractAt('DiamondCutFacet', diamondCutFacetAddress);
console.log('DiamondCutFacet deployed:', diamondCutFacet.address);
// ------------------
// deploy facets
console.log('');
console.log('Deploying facets');
let FacetNames = [
'DiamondLoupeFacet',
'LightmEquippableMultiAssetFacet',
'LightmEquippableNestableFacet',
'LightmEquippableFacet',
'RMRKEquippableFacet',
'RMRKCollectionMetadataFacet',
'LightmMintModuleImplementer',
'LightmImpl',
];
if (overrideOrAddOn?.override?.FacetNames) {
FacetNames = overrideOrAddOn.override.FacetNames;
}
let useNormalDeploy: { [k: string]: boolean } = {
LightmImpl: true,
};
if (overrideOrAddOn?.override?.useNormalDeploy) {
useNormalDeploy = overrideOrAddOn.override.useNormalDeploy;
}
const constructorParams: { [k: string]: [any[], any[]] } = {
LightmEquippableNestableFacet: [
['string', 'string'],
[`LightmNestable-${version}`, `LN-${version}`],
],
LightmEquippableMultiAssetFacet: [
['string', 'string'],
[`LightmMultiAsset-${version}`, `LMA-${version}`],
],
};
const libraryLinking: { [k: string]: any } = {
LightmEquippableNestableFacet: {
libraries: {
RMRKMultiAssetRenderUtils: rmrkMultiAssetRenderUtils.address,
},
},
LightmEquippableMultiAssetFacet: {
libraries: {
RMRKMultiAssetRenderUtils: rmrkMultiAssetRenderUtils.address,
},
},
LightmEquippableFacet: {
libraries: {
LightmValidatorLib: lightmValidatorLib.address,
},
},
RMRKEquippableFacet: {
libraries: {
LightmValidatorLib: lightmValidatorLib.address,
},
},
};
const toBeRemovedFunctions: { [k: string]: string[] } = {
LightmEquippableNestableFacet: [
// Take them in RMRKMultiAsset
'tokenURI(uint256)',
],
LightmEquippableMultiAssetFacet: [
// Take them in RMRKNestable
'name()',
'symbol()',
'ownerOf(uint256)',
'balanceOf(address)',
'safeTransferFrom(address,address,uint256,bytes)',
'safeTransferFrom(address,address,uint256)',
'transferFrom(address,address,uint256)',
'approve(address,uint256)',
'setApprovalForAll(address,bool)',
'getApproved(uint256)',
'isApprovedForAll(address,address)',
],
};
if (overrideOrAddOn?.addOn?.toBeRemovedFunctions) {
for (const [facetName, functionSelectors] of Object.entries(
overrideOrAddOn.addOn.toBeRemovedFunctions,
)) {
if (facetName in toBeRemovedFunctions) {
const newToBeRemovedFunctionsOfFacet = new Set(toBeRemovedFunctions[facetName]);
for (const functionSelector of functionSelectors) {
newToBeRemovedFunctionsOfFacet.add(functionSelector);
}
toBeRemovedFunctions[facetName] = Array.from(newToBeRemovedFunctionsOfFacet);
}
}
}
const cut = [];
for (const FacetName of FacetNames) {
const Facet = await (libraryLinking[FacetName]
? ethers.getContractFactory(FacetName, libraryLinking[FacetName])
: ethers.getContractFactory(FacetName));
let facet: Contract;
if (!noNormalDeploy && useNormalDeploy[FacetName]) {
// ---------- Normal deployment
facet = await Facet.deploy();
await facet.deployed();
// -----------------
} else {
// ---------- Create2 deployment
const facetHash = ethers.utils.id(`${FacetName}-${version}`);
const constructorParam = constructorParams[FacetName];
const facetByteCode = constructorParam
? ethers.utils.concat([
Facet.bytecode,
ethers.utils.defaultAbiCoder.encode(...constructorParam),
])
: Facet.bytecode;
if (!deployed) {
const tx = await create2Deployer.deploy(0, facetHash, facetByteCode);
await tx.wait();
}
const facetAddress = await create2Deployer['computeAddress(bytes32,bytes32)'](
facetHash,
ethers.utils.keccak256(facetByteCode),
);
facet = await ethers.getContractAt(FacetName, facetAddress);
// -----------------
}
console.log(`${FacetName} deployed: ${facet.address}`);
// use `DiamondLoupeFacet`'s `supportsInterface` function
let facetFunctionSelectors = getSelectors(facet);
facetFunctionSelectors =
FacetName !== 'DiamondLoupeFacet' &&
facetFunctionSelectors.get!(['supportsInterface(bytes4)']).length > 0
? facetFunctionSelectors.remove!(['supportsInterface(bytes4)'])
: facetFunctionSelectors;
facetFunctionSelectors = toBeRemovedFunctions[FacetName]
? facetFunctionSelectors.remove!(toBeRemovedFunctions[FacetName])
: facetFunctionSelectors;
cut.push({
facetAddress: facet.address,
action: FacetCutAction.Add,
functionSelectors: facetFunctionSelectors,
});
}
// wait 1min
if (!deployed && !(await isHardhatChain())) {
console.log('Wait 1 min to make sure block is confirmed');
await new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, 6e4);
});
}
return {
cut,
lightmValidatorLibAddress,
rmrkMultiAssetRenderUtilsAddress,
lightmEquippableRenderUtilsAddress,
diamondCutFacetAddress,
};
}
type PromiseResultType<P> = P extends Promise<infer R> ? R : P;
export async function deployDiamondAndCutFacet(
create2DeployerAddress: string,
{ diamondCutFacetAddress, cut }: PromiseResultType<ReturnType<typeof oneTimeDeploy>>,
) {
const contractOwner = (await ethers.getSigners())[0];
// deploy Diamond
const Diamond = await ethers.getContractFactory('Diamond');
// ---------- Normal deployment
const diamond = await Diamond.deploy(contractOwner.address, diamondCutFacetAddress);
await diamond.deployed();
// -----------------
// ---------- Create2 deployment
// const create2Deployer = await ethers.getContractAt('Create2Deployer', create2DeployerAddress);
// const diamondHash = ethers.utils.id('Diamond');
// const diamondByteCode = ethers.utils.concat([
// Diamond.bytecode,
// ethers.utils.defaultAbiCoder.encode(
// ['address', 'address'],
// [contractOwner.address, diamondCutFacetAddress],
// ),
// ]);
// await create2Deployer.deploy(0, diamondHash, diamondByteCode);
// const diamondAddress = await create2Deployer['computeAddress(bytes32,bytes32,byte32)'](
// diamondHash,
// ethers.utils.keccak256(diamondByteCode),
// deployerAddressForComputing,
// );
// const diamond = await ethers.getContractAt('Diamond', diamondAddress);
// -------------------
console.log('Diamond deployed:', diamond.address);
// deploy LightmInit
// LightmInit provides a function that is called when the diamond is upgraded to initialize state variables
// Read about how the diamondCut function works here: https://eips.ethereum.org/EIPS/eip-2535#addingreplacingremoving-functions
const LightmInit = await ethers.getContractFactory('LightmInit');
// ---------- Normal deployment
const lightmInit = await LightmInit.deploy();
await lightmInit.deployed();
const lightmInitAddress = lightmInit.address;
// -----------------
// ---------- Create2 deployment
// const lightmInitHash = ethers.utils.id('LightmInit');
// const lightmInitByteCode = LightmInit.bytecode;
// await create2Deployer.deploy(0, lightmInitHash, lightmInitByteCode);
// const lightmInitAddress = await create2Deployer['computeAddress(bytes32,bytes32,byte32)'](
// lightmInitHash,
// ethers.utils.keccak256(lightmInitByteCode),
// deployerAddressForComputing,
// );
// ------------------
console.log('LightmInit deployed:', lightmInitAddress);
// upgrade diamond with facets
console.log('');
const diamondCut = await ethers.getContractAt('IDiamondCut', diamond.address);
// Write down your own token's name & symbol & fallbackURI & collectionMetadataURI & mintConfig below
const initStruct = [
[
'Test',
'TEST',
'',
'',
350,
{
whitelistMintPrice: ethers.utils.parseEther('0.1'),
publicMintPrice: ethers.utils.parseEther('0.15'),
whitelistMintLimit: 1,
publicMintLimit: 2,
// 0 -> linear, 1 -> assignable
mintStyle: 1,
maxSupply: 0,
},
],
contractOwner.address,
];
// call to init function
const functionCall = LightmInit.interface.encodeFunctionData('init', initStruct);
const tx = await diamondCut.diamondCut(cut, lightmInitAddress, functionCall);
console.log('Diamond cut tx: ', tx.hash);
const receipt = await tx.wait();
if (!receipt.status) {
throw Error(`Diamond upgrade failed: ${tx.hash}`);
}
console.log('Completed diamond cut');
return diamond.address;
}
async function deploy() {
// If create2Deployer is already deployed, comment this line.
const create2DeployerAddress = await deployCreate2Deployer();
// - If these one-time deployment contracts in this function have been deployed,
// you should set the 2nd param to `true` to avoid deploying and take the return value for invoking `deployDiamondAndCutFacet`.
// - If wanna take the `cut` for using in `RMRKUniversalFactory` to make deployment process totally happen on chain,
// set 2nd param to `true`, set 3rd param to the address of `RMRKUniversalFactory` to make sure the address is computed correctly.
const returnValue = await oneTimeDeploy(create2DeployerAddress);
return await deployDiamondAndCutFacet(create2DeployerAddress, returnValue);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
if (require.main === module) {
deploy()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
}