diff --git a/contracts/blast/AtomicManualPartyBlast.sol b/contracts/blast/AtomicManualPartyBlast.sol new file mode 100644 index 00000000..488cebfe --- /dev/null +++ b/contracts/blast/AtomicManualPartyBlast.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { AtomicManualParty, IPartyFactory } from "../crowdfund/AtomicManualParty.sol"; + +contract AtomicManualPartyBlast is AtomicManualParty, BlastClaimableYield { + constructor( + IPartyFactory partyFactory, + address blast, + address governor + ) AtomicManualParty(partyFactory) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/BasicMetadataProviderBlast.sol b/contracts/blast/BasicMetadataProviderBlast.sol new file mode 100644 index 00000000..86a4d58b --- /dev/null +++ b/contracts/blast/BasicMetadataProviderBlast.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { BasicMetadataProvider, IGlobals } from "../renderers/BasicMetadataProvider.sol"; + +contract BasicMetadataProviderBlast is BasicMetadataProvider, BlastClaimableYield { + constructor( + IGlobals globals, + address blast, + address governor + ) BasicMetadataProvider(globals) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/BondingCurveAuthorityBlast.sol b/contracts/blast/BondingCurveAuthorityBlast.sol new file mode 100644 index 00000000..35145083 --- /dev/null +++ b/contracts/blast/BondingCurveAuthorityBlast.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { BondingCurveAuthority } from "../authorities/BondingCurveAuthority.sol"; + +contract BondingCurveAuthorityBlast is BondingCurveAuthority, BlastClaimableYield { + constructor( + address payable partyDao, + uint16 initialPartyDaoFeeBps, + uint16 initialTreasuryFeeBps, + uint16 initialCreatorFeeBps, + address blast, + address governor + ) + BondingCurveAuthority( + partyDao, + initialPartyDaoFeeBps, + initialTreasuryFeeBps, + initialCreatorFeeBps + ) + BlastClaimableYield(blast, governor) + {} +} diff --git a/contracts/blast/ContributionRouterBlast.sol b/contracts/blast/ContributionRouterBlast.sol new file mode 100644 index 00000000..a3d26bad --- /dev/null +++ b/contracts/blast/ContributionRouterBlast.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { ContributionRouter } from "../crowdfund/ContributionRouter.sol"; + +contract ContributionRouterBlast is ContributionRouter, BlastClaimableYield { + constructor( + address owner, + uint96 initialFeePerMint, + address blast, + address governor + ) ContributionRouter(owner, initialFeePerMint) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/CrowdfundFactoryBlast.sol b/contracts/blast/CrowdfundFactoryBlast.sol new file mode 100644 index 00000000..1ee72167 --- /dev/null +++ b/contracts/blast/CrowdfundFactoryBlast.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { CrowdfundFactory } from "../crowdfund/CrowdfundFactory.sol"; + +contract CrowdfundFactoryBlast is CrowdfundFactory, BlastClaimableYield { + constructor(address blast, address governor) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/InitialETHCrowdfundBlast.sol b/contracts/blast/InitialETHCrowdfundBlast.sol new file mode 100644 index 00000000..8090fb4d --- /dev/null +++ b/contracts/blast/InitialETHCrowdfundBlast.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { IBlast, YieldMode, GasMode } from "./utils/IBlast.sol"; +import { InitialETHCrowdfund, MetadataProvider, IGlobals } from "../crowdfund/InitialETHCrowdfund.sol"; +import { MetadataProvider } from "../renderers/MetadataProvider.sol"; + +contract InitialETHCrowdfundBlast is InitialETHCrowdfund { + IBlast immutable BLAST; + constructor(IGlobals globals, address blast) InitialETHCrowdfund(globals) { + BLAST = IBlast(blast); + } + + function initialize( + InitialETHCrowdfund.InitialETHCrowdfundOptions memory crowdfundOpts, + InitialETHCrowdfund.ETHPartyOptions memory partyOpts, + MetadataProvider customMetadataProvider, + bytes memory customMetadata + ) public payable override { + super.initialize(crowdfundOpts, partyOpts, customMetadataProvider, customMetadata); + BLAST.configure(YieldMode.AUTOMATIC, GasMode.CLAIMABLE, address(party)); + } +} diff --git a/contracts/blast/MetadataProviderBlast.sol b/contracts/blast/MetadataProviderBlast.sol new file mode 100644 index 00000000..854f41b0 --- /dev/null +++ b/contracts/blast/MetadataProviderBlast.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { MetadataProvider, IGlobals } from "../renderers/MetadataProvider.sol"; + +contract MetadataProviderBlast is MetadataProvider, BlastClaimableYield { + constructor( + IGlobals globals, + address blast, + address governor + ) MetadataProvider(globals) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/MetadataRegistryBlast.sol b/contracts/blast/MetadataRegistryBlast.sol new file mode 100644 index 00000000..189128a0 --- /dev/null +++ b/contracts/blast/MetadataRegistryBlast.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { MetadataRegistry, IGlobals } from "../renderers/MetadataRegistry.sol"; + +contract MetadataRegistryBlast is MetadataRegistry, BlastClaimableYield { + constructor( + IGlobals globals, + address[] memory registrars, + address blast, + address governor + ) MetadataRegistry(globals, registrars) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/PartyBlast.sol b/contracts/blast/PartyBlast.sol new file mode 100644 index 00000000..cd1d9145 --- /dev/null +++ b/contracts/blast/PartyBlast.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { IBlast, YieldMode, GasMode } from "./utils/IBlast.sol"; +import { Party, IGlobals } from "../party/Party.sol"; + +contract PartyBlast is Party { + IBlast immutable BLAST; + + constructor(IGlobals globals, address blast) Party(globals) { + BLAST = IBlast(blast); + } + + function initialize(PartyInitData memory initData) public override { + super.initialize(initData); + BLAST.configure(YieldMode.AUTOMATIC, GasMode.CLAIMABLE, address(this)); + } +} diff --git a/contracts/blast/SSTORE2MetadataProviderBlast.sol b/contracts/blast/SSTORE2MetadataProviderBlast.sol new file mode 100644 index 00000000..3b9dc59a --- /dev/null +++ b/contracts/blast/SSTORE2MetadataProviderBlast.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { SSTORE2MetadataProvider, IGlobals } from "../renderers/SSTORE2MetadataProvider.sol"; + +contract SSTORE2MetadataProviderBlast is SSTORE2MetadataProvider, BlastClaimableYield { + constructor( + IGlobals globals, + address blast, + address governor + ) SSTORE2MetadataProvider(globals) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/TokenDistributorBlast.sol b/contracts/blast/TokenDistributorBlast.sol new file mode 100644 index 00000000..c85709d7 --- /dev/null +++ b/contracts/blast/TokenDistributorBlast.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.20; + +import { BlastClaimableYield } from "./utils/BlastClaimableYield.sol"; +import { TokenDistributor, IGlobals } from "../distribution/TokenDistributor.sol"; + +contract TokenDistributorBlast is TokenDistributor, BlastClaimableYield { + constructor( + IGlobals globals, + uint40 emergencyDisabledTimestamp, + address blast, + address governor + ) TokenDistributor(globals, emergencyDisabledTimestamp) BlastClaimableYield(blast, governor) {} +} diff --git a/contracts/blast/utils/BlastAutomaticYield.sol b/contracts/blast/utils/BlastAutomaticYield.sol new file mode 100644 index 00000000..520e2961 --- /dev/null +++ b/contracts/blast/utils/BlastAutomaticYield.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.20; + +import { IBlast, YieldMode, GasMode } from "./IBlast.sol"; + +abstract contract BlastAutomaticYield { + /// @notice Constructor to configure the contract with Blast. + constructor(address blast, address governor) { + bytes memory blastCalldata = abi.encodeCall( + IBlast.configure, + (YieldMode.AUTOMATIC, GasMode.CLAIMABLE, governor) + ); + address(blast).call(blastCalldata); + } +} diff --git a/contracts/blast/utils/BlastClaimableYield.sol b/contracts/blast/utils/BlastClaimableYield.sol new file mode 100644 index 00000000..55917c5d --- /dev/null +++ b/contracts/blast/utils/BlastClaimableYield.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.20; + +import { IBlast, YieldMode, GasMode } from "./IBlast.sol"; + +abstract contract BlastClaimableYield { + /// @notice Constructor to configure the contract with Blast. + constructor(address blast, address governor) { + bytes memory blastCalldata = abi.encodeCall( + IBlast.configure, + (YieldMode.CLAIMABLE, GasMode.CLAIMABLE, governor) + ); + address(blast).call(blastCalldata); + } +} diff --git a/contracts/blast/utils/IBlast.sol b/contracts/blast/utils/IBlast.sol new file mode 100644 index 00000000..d3b113b3 --- /dev/null +++ b/contracts/blast/utils/IBlast.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +enum YieldMode { + AUTOMATIC, + VOID, + CLAIMABLE +} + +enum GasMode { + VOID, + CLAIMABLE +} + +interface IBlast { + // configure + function configureContract( + address contractAddress, + YieldMode _yield, + GasMode gasMode, + address governor + ) external; + function configure(YieldMode _yield, GasMode gasMode, address governor) external; + + // base configuration options + function configureClaimableYield() external; + function configureClaimableYieldOnBehalf(address contractAddress) external; + function configureAutomaticYield() external; + function configureAutomaticYieldOnBehalf(address contractAddress) external; + function configureVoidYield() external; + function configureVoidYieldOnBehalf(address contractAddress) external; + function configureClaimableGas() external; + function configureClaimableGasOnBehalf(address contractAddress) external; + function configureVoidGas() external; + function configureVoidGasOnBehalf(address contractAddress) external; + function configureGovernor(address _governor) external; + function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external; + + // claim yield + function claimYield( + address contractAddress, + address recipientOfYield, + uint256 amount + ) external returns (uint256); + function claimAllYield( + address contractAddress, + address recipientOfYield + ) external returns (uint256); + + // claim gas + function claimAllGas( + address contractAddress, + address recipientOfGas + ) external returns (uint256); + function claimGasAtMinClaimRate( + address contractAddress, + address recipientOfGas, + uint256 minClaimRateBips + ) external returns (uint256); + function claimMaxGas( + address contractAddress, + address recipientOfGas + ) external returns (uint256); + function claimGas( + address contractAddress, + address recipientOfGas, + uint256 gasToClaim, + uint256 gasSecondsToConsume + ) external returns (uint256); + + // read functions + function readClaimableYield(address contractAddress) external view returns (uint256); + function readYieldConfiguration(address contractAddress) external view returns (uint8); + function readGasParams( + address contractAddress + ) + external + view + returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode); +} diff --git a/contracts/crowdfund/InitialETHCrowdfund.sol b/contracts/crowdfund/InitialETHCrowdfund.sol index 3e5eca13..1bee48b8 100644 --- a/contracts/crowdfund/InitialETHCrowdfund.sol +++ b/contracts/crowdfund/InitialETHCrowdfund.sol @@ -112,7 +112,7 @@ contract InitialETHCrowdfund is ETHCrowdfundBase { ETHPartyOptions memory partyOpts, MetadataProvider customMetadataProvider, bytes memory customMetadata - ) external payable onlyInitialize { + ) public payable virtual onlyInitialize { // Create party the initial crowdfund will be for. Party party_ = _createParty(partyOpts, customMetadataProvider, customMetadata); diff --git a/contracts/party/Party.sol b/contracts/party/Party.sol index 81d3fd59..ab02df22 100644 --- a/contracts/party/Party.sol +++ b/contracts/party/Party.sol @@ -35,7 +35,7 @@ contract Party is PartyGovernanceNFT { /// @notice Initializer to be called prior to using the contract. /// @param initData Options used to initialize the party governance. - function initialize(PartyInitData memory initData) external onlyInitialize { + function initialize(PartyInitData memory initData) public virtual onlyInitialize { PartyGovernanceNFT._initialize( initData.options.name, initData.options.symbol, diff --git a/deploy/Blast.s.sol b/deploy/Blast.s.sol new file mode 100644 index 00000000..0b8c2f41 --- /dev/null +++ b/deploy/Blast.s.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8; + +import "./blast/DeployBlast.s.sol"; +import "./LibDeployConstants.sol"; + +contract BlastDeploy is DeployScriptBlast { + function _run() internal override { + console.log("Starting blast deploy script."); + + deploy(LibDeployConstants.blast(this.getDeployer())); + + console.log("Ending blast deploy script."); + } +} diff --git a/deploy/BlastSepolia.s.sol b/deploy/BlastSepolia.s.sol new file mode 100644 index 00000000..fb413e2a --- /dev/null +++ b/deploy/BlastSepolia.s.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8; + +import "./blast/DeployBlast.s.sol"; +import "./LibDeployConstants.sol"; + +contract BlastSepoliaDeploy is DeployScriptBlast { + function _run() internal override { + console.log("Starting blast sepolia deploy script."); + + deploy(LibDeployConstants.blastSepolia(this.getDeployer())); + + console.log("Ending blast sepolia deploy script."); + } +} diff --git a/deploy/LibDeployConstants.sol b/deploy/LibDeployConstants.sol index 5c806fb8..8cfe9fa3 100644 --- a/deploy/LibDeployConstants.sol +++ b/deploy/LibDeployConstants.sol @@ -100,6 +100,74 @@ library LibDeployConstants { return deployConstants; } + function blast() internal pure returns (DeployConstants memory) { + address[] memory allowedERC20SwapOperatorTargets = new address[](0); + + DeployConstants memory deployConstants = DeployConstants({ + seaportExchangeAddress: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, + osZoraAuctionDuration: 1 days, + osZoraAuctionTimeout: 1 days, + osMinOrderDuration: 1 hours, + osMaxOrderDuration: 4 weeks, + zoraMinAuctionDuration: 1 days, + zoraMaxAuctionDuration: 4 weeks, + zoraMaxAuctionTimeout: 2 weeks, + minCancelDelay: 6 weeks, + maxCancelDelay: 12 weeks, + distributorEmergencyActionAllowedDuration: 365 days, + partyDaoMultisig: // Must fix. Left compile failing here on purpose + allowedERC20SwapOperatorTargets: allowedERC20SwapOperatorTargets, + osZone: 0x0000000000000000000000000000000000000000, + osConduitKey: 0xf984c55ca75735630c1c27d3d06969c1aa6af1df86d22ddc0e3a978ad6138e9f, + osConduitController: 0x00000000F9490004C11Cef243f5400493c00Ad63, + fractionalVaultFactory: 0x0000000000000000000000000000000000000000, + nounsAuctionHouse: 0x0000000000000000000000000000000000000000, + zoraReserveAuctionCoreEth: 0x0000000000000000000000000000000000000000, + networkName: "blast", + deployedNounsMarketWrapper: 0x0000000000000000000000000000000000000000, + contributionRouterInitialFee: 0.00055 ether, + tokenDistributorV1: address(0), + tokenDistributorV2: address(0), + baseExternalURL: "https://blast.party.app/party/" + }); + + return deployConstants; + } + + function blastSepolia(address multisig) internal pure returns (DeployConstants memory) { + address[] memory allowedERC20SwapOperatorTargets = new address[](0); + + DeployConstants memory deployConstants = DeployConstants({ + seaportExchangeAddress: 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC, + osZoraAuctionDuration: 2 minutes, + osZoraAuctionTimeout: 2 minutes, + osMinOrderDuration: 2 minutes, + osMaxOrderDuration: 14 days, + zoraMinAuctionDuration: 2 minutes, + zoraMaxAuctionDuration: 10 days, + zoraMaxAuctionTimeout: 7 days, + minCancelDelay: 5 minutes, + maxCancelDelay: 1 days, + distributorEmergencyActionAllowedDuration: 365 days, + partyDaoMultisig: multisig, + allowedERC20SwapOperatorTargets: allowedERC20SwapOperatorTargets, + osZone: 0x0000000000000000000000000000000000000000, + osConduitKey: 0xf984c55ca75735630c1c27d3d06969c1aa6af1df86d22ddc0e3a978ad6138e9f, + osConduitController: 0x00000000F9490004C11Cef243f5400493c00Ad63, + fractionalVaultFactory: 0x0000000000000000000000000000000000000000, + nounsAuctionHouse: 0x0000000000000000000000000000000000000000, + zoraReserveAuctionCoreEth: 0x0000000000000000000000000000000000000000, + networkName: "blast-sepolia", + deployedNounsMarketWrapper: 0x0000000000000000000000000000000000000000, + contributionRouterInitialFee: 0.00055 ether, + tokenDistributorV1: 0x0000000000000000000000000000000000000000, + tokenDistributorV2: 0x0000000000000000000000000000000000000000, + baseExternalURL: "https://party.app/party/" + }); + + return deployConstants; + } + function goerli(address multisig) internal pure returns (DeployConstants memory) { address[] memory allowedERC20SwapOperatorTargets = new address[](1); allowedERC20SwapOperatorTargets[0] = 0xF91bB752490473B8342a3E964E855b9f9a2A668e; // 0x Swap Aggregator diff --git a/deploy/blast/DeployBlast.s.sol b/deploy/blast/DeployBlast.s.sol new file mode 100644 index 00000000..ad51dc75 --- /dev/null +++ b/deploy/blast/DeployBlast.s.sol @@ -0,0 +1,808 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8; + +import "forge-std/Script.sol"; + +import "contracts/crowdfund/AuctionCrowdfund.sol"; +import "contracts/crowdfund/BuyCrowdfund.sol"; +import "contracts/crowdfund/CollectionBuyCrowdfund.sol"; +import "contracts/crowdfund/CollectionBatchBuyCrowdfund.sol"; +import "contracts/operators/CollectionBatchBuyOperator.sol"; +import "contracts/operators/ERC20SwapOperator.sol"; +import { InitialETHCrowdfundBlast } from "contracts/blast/InitialETHCrowdfundBlast.sol"; +import { CrowdfundFactoryBlast } from "contracts/blast/CrowdfundFactoryBlast.sol"; +import { TokenDistributorBlast } from "contracts/blast/TokenDistributorBlast.sol"; +import "contracts/gatekeepers/AllowListGateKeeper.sol"; +import "contracts/gatekeepers/TokenGateKeeper.sol"; +import "contracts/gatekeepers/IGateKeeper.sol"; +import "contracts/globals/Globals.sol"; +import "contracts/globals/LibGlobals.sol"; +import { PartyBlast } from "contracts/blast/PartyBlast.sol"; +import "contracts/party/PartyFactory.sol"; +import { MetadataRegistryBlast } from "contracts/blast/MetadataRegistryBlast.sol"; +import { MetadataProviderBlast } from "contracts/blast/MetadataProviderBlast.sol"; +import "contracts/renderers/CrowdfundNFTRenderer.sol"; +import "contracts/renderers/PartyNFTRenderer.sol"; +import "contracts/renderers/fonts/PixeldroidConsoleFont.sol"; +import "contracts/proposals/ProposalExecutionEngine.sol"; +import "contracts/utils/PartyHelpers.sol"; +import "contracts/market-wrapper/NounsMarketWrapper.sol"; +import { AtomicManualPartyBlast } from "contracts/blast/AtomicManualPartyBlast.sol"; +import { ContributionRouterBlast } from "contracts/blast/ContributionRouterBlast.sol"; +import { AddPartyCardsAuthority } from "contracts/authorities/AddPartyCardsAuthority.sol"; +import { SellPartyCardsAuthority } from "contracts/authorities/SellPartyCardsAuthority.sol"; +import { SSTORE2MetadataProviderBlast } from "contracts/blast/SSTORE2MetadataProviderBlast.sol"; +import { BasicMetadataProviderBlast } from "contracts/blast/BasicMetadataProviderBlast.sol"; +import { OffChainSignatureValidator } from "contracts/signature-validators/OffChainSignatureValidator.sol"; +import { BondingCurveAuthorityBlast } from "contracts/blast/BondingCurveAuthorityBlast.sol"; +import { MockZoraReserveAuctionCoreEth } from "test/proposals/MockZoraReserveAuctionCoreEth.sol"; +import "../LibDeployConstants.sol"; + +abstract contract DeployBlast { + enum DeployerRole { + Default, + PartyFactory, + CrowdfundFactory, + TokenDistributor + } + + struct AddressMapping { + string key; + address value; + } + + mapping(address => uint256) private _deployerGasBefore; + mapping(address => uint256) private _deployerGasUsage; + + // temporary variables to store deployed contract addresses + Globals public globals; + AuctionCrowdfund public auctionCrowdfund; + InitialETHCrowdfundBlast public initialETHCrowdfundBlast; + CrowdfundFactoryBlast public crowdfundFactoryBlast; + PartyBlast public partyBlast; + PartyFactory public partyFactory; + ProposalExecutionEngine public proposalExecutionEngine; + TokenDistributorBlast public tokenDistributorBlast; + MetadataRegistryBlast public metadataRegistryBlast; + BasicMetadataProviderBlast public basicMetadataProviderBlast; + SSTORE2MetadataProviderBlast public sstore2MetadataProviderBlast; + RendererStorage public rendererStorage; + CrowdfundNFTRenderer public crowdfundNFTRenderer; + PartyNFTRenderer public partyNFTRenderer; + CollectionBatchBuyOperator public collectionBatchBuyOperator; + ERC20SwapOperator public swapOperator; + PartyHelpers public partyHelpers; + IGateKeeper public allowListGateKeeper; + IGateKeeper public tokenGateKeeper; + NounsMarketWrapper public nounsMarketWrapper; + PixeldroidConsoleFont public pixeldroidConsoleFont; + AtomicManualPartyBlast public atomicManualPartyBlast; + ContributionRouterBlast public contributionRouterBlast; + AddPartyCardsAuthority public addPartyCardsAuthority; + SellPartyCardsAuthority public sellPartyCardsAuthority; + OffChainSignatureValidator public offChainSignatureValidator; + BondingCurveAuthorityBlast public bondingCurveAuthorityBlast; + address constant BLAST = 0x4300000000000000000000000000000000000002; + + function deploy(LibDeployConstants.DeployConstants memory deployConstants) public virtual { + _switchDeployer(DeployerRole.Default); + + // DEPLOY_GLOBALS + console.log(""); + console.log("### Globals"); + console.log(" Deploying - Globals"); + globals = new Globals(this.getDeployer()); + console.log(" Deployed - Globals", address(globals)); + + // DEPLOY_TOKEN_DISTRIBUTOR + console.log(""); + console.log("### TokenDistributorBlast"); + console.log(" Deploying - TokenDistributorBlast"); + _switchDeployer(DeployerRole.TokenDistributor); + _trackDeployerGasBefore(); + tokenDistributorBlast = new TokenDistributorBlast( + globals, + uint40(block.timestamp) + deployConstants.distributorEmergencyActionAllowedDuration, + BLAST, + deployConstants.partyDaoMultisig + ); + _trackDeployerGasAfter(); + console.log(" Deployed - TokenDistributorBlast", address(tokenDistributorBlast)); + _switchDeployer(DeployerRole.Default); + + // DEPLOY_PROPOSAL_EXECUTION_ENGINE + console.log(""); + console.log("### ProposalExecutionEngine"); + console.log(" Deploying - ProposalExecutionEngine"); + if (deployConstants.zoraReserveAuctionCoreEth == address(0)) { + deployConstants.zoraReserveAuctionCoreEth = address( + new MockZoraReserveAuctionCoreEth() + ); + } + IReserveAuctionCoreEth zora = IReserveAuctionCoreEth( + deployConstants.zoraReserveAuctionCoreEth + ); + IFractionalV1VaultFactory fractionalVaultFactory = IFractionalV1VaultFactory( + deployConstants.fractionalVaultFactory + ); + _trackDeployerGasBefore(); + proposalExecutionEngine = new ProposalExecutionEngine( + globals, + zora, + fractionalVaultFactory + ); + _trackDeployerGasAfter(); + console.log(" Deployed - ProposalExecutionEngine", address(proposalExecutionEngine)); + + // DEPLOY_PARTY_IMPLEMENTATION + console.log(""); + console.log("### PartyBlast implementation"); + console.log(" Deploying - PartyBlast implementation"); + _trackDeployerGasBefore(); + partyBlast = new PartyBlast(globals, BLAST); + _trackDeployerGasAfter(); + console.log(" Deployed - PartyBlast implementation", address(partyBlast)); + + // DEPLOY_PARTY_FACTORY + console.log(""); + console.log("### PartyFactory"); + console.log(" Deploying - PartyFactory"); + _switchDeployer(DeployerRole.PartyFactory); + _trackDeployerGasBefore(); + partyFactory = new PartyFactory(globals); + _trackDeployerGasAfter(); + console.log(" Deployed - PartyFactory", address(partyFactory)); + _switchDeployer(DeployerRole.Default); + + // DEPLOY_INITIAL_ETH_CF_IMPLEMENTATION + console.log(""); + console.log("### InitialETHCrowdfundBlast crowdfund implementation"); + console.log(" Deploying - InitialETHCrowdfundBlast crowdfund implementation"); + _trackDeployerGasBefore(); + initialETHCrowdfundBlast = new InitialETHCrowdfundBlast(globals, BLAST); + _trackDeployerGasAfter(); + console.log( + " Deployed - InitialETHCrowdfundBlast crowdfund implementation", + address(initialETHCrowdfundBlast) + ); + + // DEPLOY_PARTY_CROWDFUND_FACTORY + console.log(""); + console.log("### CrowdfundFactoryBlast"); + console.log(" Deploying - CrowdfundFactoryBlast"); + _switchDeployer(DeployerRole.CrowdfundFactory); + _trackDeployerGasBefore(); + crowdfundFactoryBlast = new CrowdfundFactoryBlast(BLAST, deployConstants.partyDaoMultisig); + _trackDeployerGasAfter(); + console.log(" Deployed - CrowdfundFactoryBlast", address(crowdfundFactoryBlast)); + _switchDeployer(DeployerRole.Default); + + // DEPLOY_METADATA_REGISTRY + address[] memory registrars = new address[](2); + registrars[0] = address(partyFactory); + registrars[1] = address(deployConstants.partyDaoMultisig); + + console.log(""); + console.log("### MetadataRegistryBlast"); + console.log(" Deploying - MetadataRegistryBlast"); + _trackDeployerGasBefore(); + metadataRegistryBlast = new MetadataRegistryBlast( + globals, + registrars, + BLAST, + deployConstants.partyDaoMultisig + ); + _trackDeployerGasAfter(); + console.log(" Deployed - MetadataRegistryBlast", address(metadataRegistryBlast)); + + // DEPLOY_BASIC_METADATA_PROVIDER + console.log(""); + console.log("### BasicMetadataProviderBlast"); + console.log(" Deploying - BasicMetadataProviderBlast"); + _trackDeployerGasBefore(); + basicMetadataProviderBlast = new BasicMetadataProviderBlast( + globals, + BLAST, + deployConstants.partyDaoMultisig + ); + _trackDeployerGasAfter(); + console.log(" Deployed - BasicMetadataProviderBlast", address(basicMetadataProviderBlast)); + + // DEPLOY_SSTORE2_METADATA_PROVIDER + console.log(""); + console.log("### SSTORE2MetadataProviderBlast"); + console.log(" Deploying - SSTORE2MetadataProviderBlast"); + _trackDeployerGasBefore(); + sstore2MetadataProviderBlast = new SSTORE2MetadataProviderBlast( + globals, + BLAST, + deployConstants.partyDaoMultisig + ); + _trackDeployerGasAfter(); + console.log( + " Deployed - SSTORE2MetadataProviderBlast", + address(sstore2MetadataProviderBlast) + ); + + // DEPLOY_RENDERER_STORAGE + console.log(""); + console.log("### RendererStorage"); + console.log(" Deploying - RendererStorage"); + _trackDeployerGasBefore(); + rendererStorage = new RendererStorage(this.getDeployer()); + _trackDeployerGasAfter(); + console.log(" Deployed - RendererStorage", address(rendererStorage)); + + // CREATE_CUSTOMIZATION_OPTIONS + { + console.log("### Creating customization presets"); + uint256 versionId = 1; + uint256 numOfColors = uint8(type(Color).max) + 1; + bytes[] memory multicallData = new bytes[](numOfColors * 2); + // Create customization options for all colors w/ both modes (light and dark). + for (uint256 i; i < numOfColors; ++i) { + multicallData[i * 2] = abi.encodeCall( + rendererStorage.createCustomizationPreset, + ( + // Preset ID 0 is reserved. It is used to indicates to party instances + // to use the same customization preset as the crowdfund. + i + 1, + abi.encode(versionId, false, Color(i)) + ) + ); + multicallData[i * 2 + 1] = abi.encodeCall( + rendererStorage.createCustomizationPreset, + (i + 1 + numOfColors, abi.encode(versionId, true, Color(i))) + ); + } + _trackDeployerGasBefore(); + rendererStorage.multicall(multicallData); + _trackDeployerGasAfter(); + } + + // DEPLOY_FONT + console.log(""); + console.log("### PixeldroidConsoleFont"); + console.log(" Deploying - PixeldroidConsoleFont"); + _trackDeployerGasBefore(); + pixeldroidConsoleFont = new PixeldroidConsoleFont(); + _trackDeployerGasAfter(); + console.log(" Deployed - PixeldroidConsoleFont", address(pixeldroidConsoleFont)); + + // DEPLOY_CROWDFUND_NFT_RENDERER + console.log(""); + console.log("### CrowdfundNFTRenderer"); + console.log(" Deploying - CrowdfundNFTRenderer"); + _trackDeployerGasBefore(); + crowdfundNFTRenderer = new CrowdfundNFTRenderer( + globals, + rendererStorage, + IFont(address(pixeldroidConsoleFont)) + ); + _trackDeployerGasAfter(); + console.log(" Deployed - CrowdfundNFTRenderer", address(crowdfundNFTRenderer)); + + // DEPLOY_PARTY_NFT_RENDERER + console.log(""); + console.log("### PartyNFTRenderer"); + console.log(" Deploying - PartyNFTRenderer"); + _trackDeployerGasBefore(); + partyNFTRenderer = new PartyNFTRenderer( + globals, + rendererStorage, + IFont(address(pixeldroidConsoleFont)), + deployConstants.tokenDistributorV1, + deployConstants.tokenDistributorV2, + deployConstants.baseExternalURL + ); + _trackDeployerGasAfter(); + console.log(" Deployed - PartyNFTRenderer", address(partyNFTRenderer)); + + // DEPLOY_ADD_PARTY_CARDS_AUTHORITY + console.log(""); + console.log("### AddPartyCardsAuthority"); + console.log(" Deploying - AddPartyCardsAuthority"); + _trackDeployerGasBefore(); + addPartyCardsAuthority = new AddPartyCardsAuthority(); + _trackDeployerGasAfter(); + console.log(" Deployed - AddPartyCardsAuthority", address(addPartyCardsAuthority)); + + // DEPLOY_SELL_PARTY_CARDS_AUTHORITY + console.log(""); + console.log("### SellPartyCardsAuthority"); + console.log(" Deploying - SellPartyCardsAuthority"); + _trackDeployerGasBefore(); + sellPartyCardsAuthority = new SellPartyCardsAuthority(); + _trackDeployerGasAfter(); + console.log(" Deployed - SellPartyCardsAuthority", address(sellPartyCardsAuthority)); + + // Deploy_BONDING_CURVE_AUTHORITY + console.log(""); + console.log("### BondingCurveAuthorityBlast"); + console.log(" Deploying - BondingCurveAuthorityBlast"); + _trackDeployerGasBefore(); + bondingCurveAuthorityBlast = new BondingCurveAuthorityBlast( + payable(deployConstants.partyDaoMultisig), + 250, + 1000, + 250, + BLAST, + deployConstants.partyDaoMultisig + ); + _trackDeployerGasAfter(); + console.log(" Deployed - BondingCurveAuthorityBlast", address(bondingCurveAuthorityBlast)); + + // DEPLOY_BATCH_BUY_OPERATOR + console.log(""); + console.log("### CollectionBatchBuyOperator"); + console.log(" Deploying - CollectionBatchBuyOperator"); + _trackDeployerGasBefore(); + collectionBatchBuyOperator = new CollectionBatchBuyOperator(); + _trackDeployerGasAfter(); + console.log(" Deployed - CollectionBatchBuyOperator", address(collectionBatchBuyOperator)); + + // DEPLOY_ERC20_SWAP_OPERATOR + console.log(""); + console.log("### ERC20SwapOperator"); + console.log(" Deploying - ERC20SwapOperator"); + _trackDeployerGasBefore(); + swapOperator = new ERC20SwapOperator( + globals, + deployConstants.allowedERC20SwapOperatorTargets + ); + _trackDeployerGasAfter(); + console.log(" Deployed - ERC20SwapOperator", address(swapOperator)); + + // DEPLOY_PARTY_HELPERS + if (!isTest()) { + console.log(""); + console.log("### PartyHelpers"); + console.log(" Deploying - PartyHelpers"); + _trackDeployerGasBefore(); + partyHelpers = new PartyHelpers(); + _trackDeployerGasAfter(); + console.log(" Deployed - PartyHelpers", address(partyHelpers)); + } + + // Deploy CONTRIBUTION_ROUTER + console.log(""); + console.log("### ContributionRouterBlast"); + console.log(" Deploying - ContributionRouterBlast"); + _trackDeployerGasBefore(); + contributionRouterBlast = new ContributionRouterBlast( + deployConstants.partyDaoMultisig, + deployConstants.contributionRouterInitialFee, + BLAST, + deployConstants.partyDaoMultisig + ); + _trackDeployerGasAfter(); + console.log(" Deployed - ContributionRouterBlast", address(contributionRouterBlast)); + + // Deploy OFF_CHAIN_SIGNATURE_VALIDATOR + console.log(""); + console.log("### OffChainSignatureValidator"); + console.log(" Deploying - OffChainSignatureValidator"); + _trackDeployerGasBefore(); + offChainSignatureValidator = new OffChainSignatureValidator(); + _trackDeployerGasAfter(); + console.log(" Deployed - OffChainSignatureValidator", address(offChainSignatureValidator)); + + // DEPLOY_GATE_KEEPRS + console.log(""); + console.log("### GateKeepers"); + console.log(" Deploying - AllowListGateKeeper"); + _trackDeployerGasBefore(); + allowListGateKeeper = new AllowListGateKeeper(address(contributionRouterBlast)); + _trackDeployerGasAfter(); + console.log(" Deployed - AllowListGateKeeper", address(allowListGateKeeper)); + + console.log(""); + console.log(" Deploying - TokenGateKeeper"); + _trackDeployerGasBefore(); + tokenGateKeeper = new TokenGateKeeper(address(contributionRouterBlast)); + _trackDeployerGasAfter(); + console.log(" Deployed - TokenGateKeeper", address(tokenGateKeeper)); + + // DEPLOY_MARKET_WRAPPERS + console.log(""); + console.log("### MarketWrappers"); + if (address(deployConstants.deployedNounsMarketWrapper) == address(0)) { + console.log(" Deploying - NounsMarketWrapper"); + _trackDeployerGasBefore(); + nounsMarketWrapper = new NounsMarketWrapper(deployConstants.nounsAuctionHouse); + _trackDeployerGasAfter(); + console.log(" Deployed - NounsMarketWrapper", address(nounsMarketWrapper)); + } else { + nounsMarketWrapper = NounsMarketWrapper(deployConstants.deployedNounsMarketWrapper); + } + + console.log(""); + console.log(" Deploying - AtomicManualPartyBlast"); + _trackDeployerGasBefore(); + atomicManualPartyBlast = new AtomicManualPartyBlast( + partyFactory, + BLAST, + deployConstants.partyDaoMultisig + ); + _trackDeployerGasAfter(); + console.log(" Deployed - AtomicManualPartyBlast", address(atomicManualPartyBlast)); + + // Set Global values and transfer ownership + { + console.log("### Configure Globals"); + bytes[] memory multicallData = new bytes[](999); + uint256 n = 0; + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_DAO_WALLET, deployConstants.partyDaoMultisig) + ); + multicallData[n++] = abi.encodeCall( + globals.setBytes32, + (LibGlobals.GLOBAL_OPENSEA_CONDUIT_KEY, deployConstants.osConduitKey) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_OPENSEA_ZONE, deployConstants.osZone) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_TOKEN_DISTRIBUTOR, address(tokenDistributorBlast)) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + (LibGlobals.GLOBAL_OS_ZORA_AUCTION_DURATION, deployConstants.osZoraAuctionDuration) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + (LibGlobals.GLOBAL_OS_ZORA_AUCTION_TIMEOUT, deployConstants.osZoraAuctionTimeout) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + (LibGlobals.GLOBAL_OS_MIN_ORDER_DURATION, deployConstants.osMinOrderDuration) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + (LibGlobals.GLOBAL_OS_MAX_ORDER_DURATION, deployConstants.osMaxOrderDuration) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + ( + LibGlobals.GLOBAL_ZORA_MIN_AUCTION_DURATION, + deployConstants.zoraMinAuctionDuration + ) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + ( + LibGlobals.GLOBAL_ZORA_MAX_AUCTION_DURATION, + deployConstants.zoraMaxAuctionDuration + ) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + (LibGlobals.GLOBAL_ZORA_MAX_AUCTION_TIMEOUT, deployConstants.zoraMaxAuctionTimeout) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + (LibGlobals.GLOBAL_PROPOSAL_MIN_CANCEL_DURATION, deployConstants.minCancelDelay) + ); + multicallData[n++] = abi.encodeCall( + globals.setUint256, + (LibGlobals.GLOBAL_PROPOSAL_MAX_CANCEL_DURATION, deployConstants.maxCancelDelay) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_SEAPORT, deployConstants.seaportExchangeAddress) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_CONDUIT_CONTROLLER, deployConstants.osConduitController) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_PROPOSAL_ENGINE_IMPL, address(proposalExecutionEngine)) + ); + + // The Globals commented out below were depreciated in 1.2; factories + // can now choose the implementation address to deploy and no longer + // deploy the latest implementation. + // + // See https://github.com/PartyDAO/party-migrations for + // implementation addresses by release. + + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_PARTY_IMPL, address(party)) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_CROWDFUND_FACTORY, address(crowdfundFactory)) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_PARTY_FACTORY, address(partyFactory)) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_AUCTION_CF_IMPL, address(auctionCrowdfund)) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_BUY_CF_IMPL, address(buyCrowdfund)) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_COLLECTION_BUY_CF_IMPL, address(collectionBuyCrowdfund)) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // ( + // LibGlobals.GLOBAL_COLLECTION_BATCH_BUY_CF_IMPL, + // address(collectionBatchBuyCrowdfund) + // ) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_INITIAL_ETH_CF_IMPL, address(initialETHCrowdfund)) + // ); + // multicallData[n++] = abi.encodeCall( + // globals.setAddress, + // (LibGlobals.GLOBAL_ROLLING_AUCTION_CF_IMPL, address(rollingAuctionCrowdfund)) + // ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_RENDERER_STORAGE, address(rendererStorage)) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_CF_NFT_RENDER_IMPL, address(crowdfundNFTRenderer)) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_GOVERNANCE_NFT_RENDER_IMPL, address(partyNFTRenderer)) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + (LibGlobals.GLOBAL_METADATA_REGISTRY, address(metadataRegistryBlast)) + ); + multicallData[n++] = abi.encodeCall( + globals.setAddress, + ( + LibGlobals.GLOBAL_OFF_CHAIN_SIGNATURE_VALIDATOR, + address(offChainSignatureValidator) + ) + ); + // transfer ownership of Globals to multisig + if (this.getDeployer() != deployConstants.partyDaoMultisig) { + multicallData[n++] = abi.encodeCall( + globals.transferMultiSig, + (deployConstants.partyDaoMultisig) + ); + } + assembly { + mstore(multicallData, n) + } + _trackDeployerGasBefore(); + globals.multicall(multicallData); + _trackDeployerGasAfter(); + } + + // transfer renderer storage ownership to multisig + if (this.getDeployer() != deployConstants.partyDaoMultisig) { + console.log(" Transferring RendererStorage ownership to multisig"); + _trackDeployerGasBefore(); + rendererStorage.transferOwnership(deployConstants.partyDaoMultisig); + _trackDeployerGasAfter(); + } + } + + function getDeployer() external view returns (address) { + return msg.sender; + } + + function isTest() internal view returns (bool) { + return address(this) == this.getDeployer(); + } + + function _getDeployerGasUsage(address deployer) internal view returns (uint256) { + return _deployerGasUsage[deployer]; + } + + function _trackDeployerGasBefore() private { + address deployer = this.getDeployer(); + _deployerGasBefore[deployer] = gasleft(); + } + + function _trackDeployerGasAfter() private { + address deployer = this.getDeployer(); + uint256 usage = _deployerGasBefore[deployer] - gasleft(); + _deployerGasUsage[deployer] += usage; + } + + function _switchDeployer(DeployerRole role) internal virtual; +} + +contract DeployScriptBlast is Script, DeployBlast { + mapping(DeployerRole => address) internal _deployerByRole; + address[] private _deployersUsed; + + function run() external { + vm.startBroadcast(); + + _run(); + + { + uint256 n = _deployersUsed.length; + console.log(""); + for (uint256 i; i < n; ++i) { + address deployer = _deployersUsed[i]; + uint256 gasUsed = _getDeployerGasUsage(deployer); + console.log("deployer:", deployer); + console.log("cost:", gasUsed * tx.gasprice); + console.log("gas:", gasUsed); + if (i + 1 < n) { + console.log(""); + } + } + } + } + + function _run() internal virtual {} + + function _switchDeployer(DeployerRole role) internal override { + vm.stopBroadcast(); + { + address deployer_ = _deployerByRole[role]; + if (deployer_ != address(0)) { + vm.startBroadcast(deployer_); + } else { + vm.startBroadcast(); + } + } + address deployer = this.getDeployer(); + console.log("Switched deployer to", deployer); + for (uint256 i; i < _deployersUsed.length; ++i) { + if (_deployersUsed[i] == deployer) { + return; + } + } + _deployersUsed.push(deployer); + if (vm.envUint("DRY_RUN") == 1) { + vm.deal(deployer, 100e18); + } + } + + function deploy(LibDeployConstants.DeployConstants memory deployConstants) public override { + DeployBlast.deploy(deployConstants); + vm.stopBroadcast(); + + AddressMapping[] memory addressMapping = new AddressMapping[](25); + addressMapping[0] = AddressMapping("Globals", address(globals)); + addressMapping[1] = AddressMapping("TokenDistributorBlast", address(tokenDistributorBlast)); + addressMapping[2] = AddressMapping( + "ProposalExecutionEngine", + address(proposalExecutionEngine) + ); + addressMapping[3] = AddressMapping("PartyBlast", address(partyBlast)); + addressMapping[4] = AddressMapping("PartyFactory", address(partyFactory)); + addressMapping[10] = AddressMapping( + "InitialETHCrowdfundBlast", + address(initialETHCrowdfundBlast) + ); + addressMapping[11] = AddressMapping( + "CollectionBatchBuyOperator", + address(collectionBatchBuyOperator) + ); + addressMapping[12] = AddressMapping("ERC20SwapOperator", address(swapOperator)); + addressMapping[13] = AddressMapping( + "CrowdfundFactoryBlast", + address(crowdfundFactoryBlast) + ); + addressMapping[14] = AddressMapping( + "MetadataRegistryBlast", + address(metadataRegistryBlast) + ); + addressMapping[15] = AddressMapping( + "BasicMetadataProviderBlast", + address(basicMetadataProviderBlast) + ); + addressMapping[16] = AddressMapping( + "SSTORE2MetadataProviderBlast", + address(sstore2MetadataProviderBlast) + ); + addressMapping[17] = AddressMapping("CrowdfundNFTRenderer", address(crowdfundNFTRenderer)); + addressMapping[18] = AddressMapping("PartyNFTRenderer", address(partyNFTRenderer)); + addressMapping[19] = AddressMapping("PartyHelpers", address(partyHelpers)); + addressMapping[20] = AddressMapping("AllowListGateKeeper", address(allowListGateKeeper)); + addressMapping[21] = AddressMapping("TokenGateKeeper", address(tokenGateKeeper)); + addressMapping[22] = AddressMapping("RendererStorage", address(rendererStorage)); + addressMapping[23] = AddressMapping( + "PixeldroidConsoleFont", + address(pixeldroidConsoleFont) + ); + addressMapping[24] = AddressMapping( + "AtomicManualPartyBlast", + address(atomicManualPartyBlast) + ); + addressMapping[9] = AddressMapping( + "ContributionRouterBlast", + address(contributionRouterBlast) + ); + addressMapping[8] = AddressMapping( + "AddPartyCardsAuthority", + address(addPartyCardsAuthority) + ); + addressMapping[7] = AddressMapping( + "BondingCurveAuthorityBlast", + address(bondingCurveAuthorityBlast) + ); + addressMapping[6] = AddressMapping( + "SellPartyCardsAuthority", + address(sellPartyCardsAuthority) + ); + addressMapping[5] = AddressMapping( + "OffChainSignatureValidator", + address(offChainSignatureValidator) + ); + + console.log(""); + console.log("### Deployed addresses"); + string memory jsonRes = generateJSONString(addressMapping); + console.log(jsonRes); + + writeAddressesToFile(deployConstants.networkName, jsonRes); + writeAbisToFiles(); + console.log(""); + console.log("Ending deploy script."); + } + + function generateJSONString( + AddressMapping[] memory parts + ) private pure returns (string memory) { + string memory vals = ""; + for (uint256 i; i < parts.length; ++i) { + string memory newValue = string.concat( + '"', + parts[i].key, + '": "', + Strings.toHexString(parts[i].value), + '"' + ); + if (i != parts.length - 1) { + newValue = string.concat(newValue, ","); + } + vals = string.concat(vals, newValue); + } + return string.concat("{", vals, "}"); + } + + function writeAbisToFiles() private { + string[] memory ffiCmd = new string[](2); + ffiCmd[0] = "node"; + ffiCmd[1] = "./js/output-abis.js"; + bytes memory ffiResp = vm.ffi(ffiCmd); + + bool wroteSuccessfully = keccak256(ffiResp) == + keccak256(hex"0000000000000000000000000000000000000001"); + if (!wroteSuccessfully) { + revert("Could not write ABIs to file"); + } + console.log("Successfully wrote ABIS to files"); + } + + function writeAddressesToFile(string memory networkName, string memory jsonRes) private { + string[] memory ffiCmd = new string[](4); + ffiCmd[0] = "node"; + ffiCmd[1] = "./js/save-json.js"; + ffiCmd[2] = networkName; + ffiCmd[3] = jsonRes; + bytes memory ffiResp = vm.ffi(ffiCmd); + + bool wroteSuccessfully = keccak256(ffiResp) == + keccak256(hex"0000000000000000000000000000000000000001"); + if (!wroteSuccessfully) { + revert("Could not write to file"); + } + console.log("Successfully wrote to file"); + } +} diff --git a/lib/forge-std b/lib/forge-std index 2f43c7e6..2f6762e4 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 2f43c7e69b820910e9d4f3b8cc8d3b4e6382786e +Subproject commit 2f6762e4f73f3d835457c220b5f62dfeeb6f6341 diff --git a/lib/party-addresses b/lib/party-addresses index 9e3ee3c4..71b9ead0 160000 --- a/lib/party-addresses +++ b/lib/party-addresses @@ -1 +1 @@ -Subproject commit 9e3ee3c4fef00420388e37bbb98167cc6d4fd497 +Subproject commit 71b9ead03292d6b06adf0f2f587619c089c14532 diff --git a/package.json b/package.json index 1629bae6..b81f0c12 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,10 @@ "deploy:base-goerli:dry": "DRY_RUN=1 forge script ./deploy/BaseGoerli.s.sol -vvv --rpc-url $BASE_GOERLI_RPC_URL --via-ir --skip test --optimize --optimizer-runs 50 --ffi", "deploy:zora:dry": "DRY_RUN=1 forge script ./deploy/Zora.s.sol -vvv --rpc-url $ZORA_RPC_URL --via-ir --skip test --optimize --optimizer-runs 50 --ffi --priority-gas-price 1", "deploy:zora": "DRY_RUN=0 forge script ./deploy/Zora.s.sol -vvv --rpc-url $ZORA_RPC_URL --broadcast --via-ir --skip test --optimize --optimizer-runs 50 --ffi --slow --priority-gas-price 1", + "deploy:blast-sepolia": "DRY_RUN=0 forge script ./deploy/BlastSepolia.s.sol -vvv --rpc-url $BLAST_SEPOLIA_RPC_URL --via-ir --broadcast --etherscan-api-key $ETHERSCAN_API_KEY --skip test --optimize --optimizer-runs 0 --ffi --slow", + "deploy:blast-sepolia:dry": "DRY_RUN=1 forge script ./deploy/BlastSepolia.s.sol -vvv --rpc-url $BLAST_SEPOLIA_RPC_URL --via-ir --skip test --optimize --optimizer-runs 0 --ffi", + "deploy:blast": "DRY_RUN=0 forge script ./deploy/Blast.s.sol -vvv --rpc-url $BLAST_RPC_URL --via-ir --broadcast --etherscan-api-key $ETHERSCAN_API_KEY --skip test --optimize --optimizer-runs 0 --ffi --slow", + "deploy:blast:dry": "DRY_RUN=1 forge script ./deploy/Blast.s.sol -vvv --rpc-url $BLAST_RPC_URL --via-ir --skip test --optimize --optimizer-runs 0 --ffi", "decode-revert": "node js/decode-revert.js", "layout": "node js/gen-storage-layout.js", "coverage": "COVERAGE=true forge coverage --report lcov" diff --git a/utils/deploy.ts b/utils/deploy.ts index 481289e6..fd0ffe25 100644 --- a/utils/deploy.ts +++ b/utils/deploy.ts @@ -367,6 +367,8 @@ async function main() { "zora", "sepolia", "base-sepolia", + "blast-sepolia", + "blast", ]; if (!chain) { diff --git a/utils/output-abis.ts b/utils/output-abis.ts index 296aaf09..dd4ad2b4 100644 --- a/utils/output-abis.ts +++ b/utils/output-abis.ts @@ -33,6 +33,16 @@ const RELEVANT_ABIS = [ "OffChainSignatureValidator", "BondingCurveAuthority", "EZPartyBuilder", + "TokenDistributorBlast", + "PartyBlast", + "InitialETHCrowdfundBlast", + "CrowdfundFactoryBlast", + "MetadataRegistryBlast", + "BasicMetadataProviderBlast", + "SSTORE2MetadataProviderBlast", + "AtomicManualPartyBlast", + "ContributionRouterBlast", + "BondingCurveAuthorityBlast", ]; // AFileName -> a_file_name diff --git a/utils/verify.ts b/utils/verify.ts index 108e8c2e..9ceb7852 100644 --- a/utils/verify.ts +++ b/utils/verify.ts @@ -16,6 +16,10 @@ export const getBlockExplorerApiEndpoint = (chain: string) => { return "https://api.routescan.io/v2/network/mainnet/evm/7777777/etherscan/api"; } else if (chain === "base-sepolia") { return "https://api-sepolia.basescan.org/api"; + } else if (chain === "blast-sepolia") { + return "https://api-sepolia.blastscan.io/api"; + } else if (chain === "blast") { + return "https://api.blastscan.io/api"; } else { return `https://api-${chain}.etherscan.io/api`; } @@ -228,6 +232,10 @@ const getChainId = (chain: string) => { return 11155111; } else if (chain === "base-sepolia") { return 84532; + } else if (chain === "blast-sepolia") { + return 168587773; + } else if (chain === "blast") { + return 81457; } else { throw new Error(`Unknown chain ID for "${chain}". Please add to getChainId() function.`); }