Skip to content

Commit

Permalink
feat: deployment scripts for nft-collection
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres Adjimann committed Dec 10, 2024
1 parent 5c44710 commit 2d2404c
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const owner = await read('CollectionFactory', 'owner');

if (nftCollectionAdmin?.toLocaleLowerCase() !== owner?.toLocaleLowerCase()) {
await catchUnknownSigner(
await catchUnknownSigner(async () => {
// This only starts the fist step in a 2step ownership transfer
// the second step involves the "nftCollectionAdmin" multisig to accept the new ownership
// by calling the "acceptOwnership" function on the CollectionFactory
execute(
await execute(
'CollectionFactory',
{from: owner, log: true},
'transferOwnership',
nftCollectionAdmin
)
);
);
await execute(
'CollectionFactory',
{from: nftCollectionAdmin, log: true},
'acceptOwnership'
);
});
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {DeployFunction} from 'hardhat-deploy/types';
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DEPLOY_TAGS} from '../../hardhat.config';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts} = hre;
const {deployer} = await getNamedAccounts();

await deployments.deploy('NFTCollection_Implementation', {
from: deployer,
contract: 'NFTCollection',
log: true,
skipIfAlreadyDeployed: true,
});
};

export default func;
func.tags = [
'PolygonNFTCollection',
'PolygonNFTCollectionImplementation_deploy',
DEPLOY_TAGS.L2,
DEPLOY_TAGS.L2_PROD,
DEPLOY_TAGS.L2_TEST,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {ZeroAddress} from 'ethers';
import {DeployFunction} from 'hardhat-deploy/types';
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DEPLOY_TAGS} from '../../hardhat.config';
import {
getEventArgsFromReceipt,
saveDeployment,
} from '../../utils/hardhatDeployUtils';
import {getNamedAccounts} from 'hardhat';

// hardhat-deploy don't support factory and beacons the way we use it
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, ethers} = hre;
const beaconAlias = ethers.encodeBytes32String('nft-collection-v2');
const implementation = await deployments.get('NFTCollection_Implementation');
const beaconAddress = await deployments.read(
'CollectionFactory',
'aliasToBeacon',
beaconAlias
);
if (beaconAddress == ZeroAddress) {
await deployments.catchUnknownSigner(
deployBeacon(hre, beaconAlias, implementation)
);
} else {
await performSanityChecks(hre, beaconAddress, implementation);
}
};

async function deployBeacon(hre, beaconAlias, implementation) {
const {deployments, ethers} = hre;
const {nftCollectionAdmin} = await getNamedAccounts();
const receipt = await deployments.execute(
'CollectionFactory',
{from: nftCollectionAdmin, log: true},
'deployBeacon',
implementation.address,
beaconAlias
);
const eventArgs: {beaconAlias: string; beaconAddress: string} =
getEventArgsFromReceipt(
await ethers.getContract('CollectionFactory'),
receipt,
'BeaconAdded'
);
await saveDeployment(
deployments,
eventArgs.beaconAddress,
'NFTCollection_Beacon',
'UpgradeableBeacon',
receipt
);
}

async function performSanityChecks(hre, beaconAddress: string, implementation) {
const {deployments, ethers} = hre;
const beaconArtifact = await deployments.getArtifact('UpgradeableBeacon');
const beacon = await ethers.getContractAt(beaconArtifact.abi, beaconAddress);

const i = await beacon.implementation();
if (i != implementation.address) {
throw new Error(
'something went wrong: Beacon already deployed but has wrong implementation address, must call updateBeaconImplementation'
);
}
const factory = await deployments.get('CollectionFactory');
const o = await beacon.owner();
if (o != factory.address) {
throw new Error(
'something went wrong: Beacon already deployed but has wrong owner'
);
}
}

export default func;
func.tags = [
'PolygonNFTCollection',
'PolygonNFTCollection_Beacon',
'PolygonNFTCollectionBeacon_deploy',
DEPLOY_TAGS.L2,
DEPLOY_TAGS.L2_PROD,
DEPLOY_TAGS.L2_TEST,
];
func.dependencies = [
'CollectionFactory_deploy',
'CollectionFactory_change_admin',
'PolygonNFTCollectionImplementation_deploy',
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {DeployFunction} from 'hardhat-deploy/types';
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DEPLOY_TAGS} from '../../hardhat.config';
import {
getEventArgsFromReceipt,
saveDeployment,
} from '../../utils/hardhatDeployUtils';

// Collections are created via backoffice, this script creates a collection
// for testing (TO BE USED ONLY ON TESTNETS)
// hardhat-deploy don't support factory and beacons the way we use it
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts, ethers} = hre;

const skipIfAlreadyExists = !!(await deployments.getOrNull(
'NFTCollection_CollectionProxy'
));
if (skipIfAlreadyExists) {
console.log('skip NFTCollection_CollectionProxy already exist');
return;
}
const {treasury, raffleSignWallet, nftCollectionAdmin} =
await getNamedAccounts();

// TODO: set the right arguments
const metadataUrl =
'https://contracts.sandbox.game/avatarcollection-unrevealed/';
const collectionName = 'NFTCollectionTest';
const collectionSymbol = 'TEST';
const MAX_SUPPLY = 500;

const TRUSTED_FORWARDER = await deployments.get('TRUSTED_FORWARDER_V2');
const sandContract = await deployments.get('PolygonSand');
const implementation = await ethers.getContract(
'NFTCollection_Implementation'
);

await deployments.catchUnknownSigner(async () => {
const receipt = await deployments.execute(
'CollectionFactory',
{from: nftCollectionAdmin, log: true},
'deployCollection',
ethers.encodeBytes32String('nft-collection-v2'),
implementation.interface.encodeFunctionData('initialize', [
nftCollectionAdmin,
metadataUrl,
collectionName,
collectionSymbol,
treasury,
raffleSignWallet,
TRUSTED_FORWARDER.address,
sandContract.address,
MAX_SUPPLY,
])
);
const eventArgs: {collectionProxy: string; beaconAddress: string} =
getEventArgsFromReceipt(
await ethers.getContract('CollectionFactory'),
receipt,
'CollectionAdded'
);
await saveDeployment(
deployments,
eventArgs.collectionProxy,
'NFTCollectionMat_Proxy',
'CollectionProxy',
receipt,
await implementation.getAddress()
);
});
};

export default func;
func.tags = [
'PolygonNFTCollectionTest_deploy',
DEPLOY_TAGS.L2,
DEPLOY_TAGS.L2_TEST,
];
func.dependencies = [
'PolygonNFTCollectionBeacon_deploy',
'PolygonSand_deploy',
'TRUSTED_FORWARDER_V2',
];
9 changes: 8 additions & 1 deletion packages/deploy/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import {

// Package name : solidity source code path
const importedPackages = {
'@sandbox-smart-contracts/avatar': 'contracts/',
'@sandbox-smart-contracts/avatar': [
'contracts/nft-collection/NFTCollection.sol',
'contracts/avatar/AvatarCollection.sol',
'contracts/proxy',
'contracts/raffle',
'contracts/raffleold/contracts',
],
'@sandbox-smart-contracts/[email protected]': [
'contracts/Asset.sol',
'contracts/AssetCreate.sol',
Expand Down Expand Up @@ -312,6 +318,7 @@ const namedAccounts = {
default: 'sandAdmin',
mainnet: null,
polygon: '0xF06dD9b61d480704Cc7bEF717e5Ea6efB6Af75bE', // Final admin should be 0xE79AF6BEb7D31c7faF7a1b891d9684960522D22e
amoy: '0x4BF86138e9DC66Fb65F8b9387C53aB4439FC41FF',
},
lazyMintingCatSeller: {
default: 4,
Expand Down
39 changes: 39 additions & 0 deletions packages/deploy/utils/hardhatDeployUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Hardhat-deploy don't support factory and beacons the way we use it
// We are forced to save the deployment by hand
import {
DeploymentsExtension,
DeploymentSubmission,
Receipt,
} from 'hardhat-deploy/types';
import {Contract} from 'ethers';

export async function saveDeployment(
deployments: DeploymentsExtension,
address: string,
artifactName: string,
contractName: string,
receipt: Receipt,
implementationAddress?: string
) {
const extendedArtifact = await deployments.getExtendedArtifact(contractName);
console.log(
`saving "${artifactName}" (tx: ${receipt.transactionHash})...: deployed at ${address} with ${receipt.gasUsed} gas`
);
await deployments.save(artifactName, {
address,
...extendedArtifact,
receipt,
transactionHash: receipt.transactionHash,
...(implementationAddress ? {implementation: implementationAddress} : {}),
} as DeploymentSubmission);
}

export function getEventArgsFromReceipt(
contract: Contract,
receipt: Receipt,
eventName: string
) {
const fragment = contract.filters[eventName].fragment;
const ev = receipt.events.find((x) => x.topics[0] == fragment.topicHash);
return ev.args;
}

1 comment on commit 2d2404c

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage for this commit

90.36%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
packages/avatar/contracts/avatar
   AvatarCollection.sol74.25%56.56%73.81%90.37%285, 329–337, 339–340, 345, 403–407, 425, 442, 459, 483–487, 491–492, 495, 526, 537, 545, 545–546, 565, 599, 621, 631, 712, 732, 745, 745, 745, 763, 763, 765, 774, 774, 776, 786, 786–787, 797, 797–798, 809, 809–810, 823, 832, 861–862, 992–993, 996, 999
   CollectionAccessControl.sol60.87%53.33%63.64%67.86%120, 130–131, 140, 150–151, 161–162, 162, 162, 164–165, 167, 173, 173–174, 185, 192, 192–194, 98–99
   ERC721BurnMemoryEnumerableUpgradeable.sol93.33%75%100%100%62, 75
packages/avatar/contracts/common
   IERC4906.sol100%100%100%100%
   IERC5313.sol100%100%100%100%
packages/avatar/contracts/common/BaseWithStorage/ERC2771
   ERC2771HandlerUpgradeable.sol44.44%25%60%44.44%22, 26, 29, 38, 38, 38–39, 41
packages/avatar/contracts/common/OperatorFilterer
   IOperatorFilterRegistry.sol100%100%100%100%
   UpdatableOperatorFiltererUpgradeable.sol11.11%10%25%9.52%18, 23–24, 24, 24–25, 27, 27, 27–28, 30, 38, 38, 38, 42, 42, 42–44, 46, 46, 46–47, 50, 55, 55, 55–56, 56, 56–57, 60, 68, 68, 68–69, 71
packages/avatar/contracts/nft-collection
   ERC2771HandlerUpgradeable.sol100%100%100%100%
   ERC721BurnMemoryUpgradeable.sol100%100%100%100%
   INFTCollection.sol100%100%100%100%
   NFTCollection.sol99.76%99.28%100%100%300
   NFTCollectionSignature.sol100%100%100%100%
   UpdatableOperatorFiltererUpgradeable.sol100%100%100%100%
packages/avatar/contracts/proxy
   CollectionFactory.sol86.59%89.06%75%87.50%149, 230, 293, 362–366, 368, 385, 402, 410, 410–411, 423, 423–424
   CollectionProxy.sol88.24%50%100%100%55, 70
packages/avatar/contracts/raffle
   DanceFight.sol75%50%100%100%20
   FistOfTheNorthStar.sol75%50%100%100%20
   GenericRaffle.sol67.92%52.22%62.07%84.95%178, 189–195, 225–229, 252–259, 266–267, 270, 288, 288–289, 289, 289–290, 308, 381, 390, 409, 409–410, 419, 419, 421, 430, 430, 432, 442, 442–443, 453, 453–454, 465, 465–466, 566, 575, 626–627, 630, 633
   HellsKitchen.sol75%50%100%100%20
   MadBalls.sol75%50%100%100%20
   ParisHilton.sol75%50%100%100%20
   Rabbids.sol75%50%100%100%20

Please sign in to comment.