<!-- {%hackmd theme-dark %} --> # Juicebox + Collective Ownership of Nounish Token This spec extends the [initial proposal](https://hackmd.io/vYrWAz_xRKq6mNaIvv3EvQ) to discuss technical tradeoffs and implementation details. ### I) Initialization The following values will be used to set the initial state of the Juicebox project. ```solidity function launchProjectFor( address _owner, JBProjectMetadata calldata _projectMetadata, JBFundingCycleData calldata _data, JBFundingCycleMetadata calldata _metadata, uint256 _mustStartAtOrAfter, JBGroupedSplits[] calldata _groupedSplits, JBFundAccessConstraints[] calldata _fundAccessConstraints, IJBPaymentTerminal[] memory _terminals, string memory _memo ) external virtual override returns (uint256 projectId) { ... } ``` **_owner** ```solidity 0x0000000000000000000000000000000000000000 // TODO replace with governance contract ``` **_projectMetadata** ```solidity { // TODO replace with IPFS link content: "QmbH96jj8RTJgC9526RdsFs5dPvcsRfiRuD9797JXzcvbw", domain: 0 } ``` **_data** ```solidity { duration: 0, // new funding cycle can start at any time weight: 1000000000000000000000000, // 10^24 -> 10^6 project tokens for 1E contribution discountRate: 0, ballot: 0x0000000000000000000000000000000000000000 } ``` **_metadata** ```solidity { global: { allowSetTerminals: false, allowSetController: false, pauseTransfers: false }, reservedRate: 0, // all project tokens go to beneficiary redemptionRate: 0, // contributions aren't refundable until auction is complete ballotRedemptionRate: 0, pausePay: false, pauseDistributions: false, pauseRedeem: false, pauseBurn: false, allowMinting: false, allowTerminalMigration: false, allowControllerMigration: false, holdFees: false, preferClaimedTokenOverride: false, useTotalOverflowForRedemptions: false, useDataSourceForPay: true, useDataSourceForRedeem: true, dataSource: 0x0000000000000000000000000000000000000000, // TODO depending on implementation for funding and bidding, may need to replace with custom data source metadata: 0 } ``` **_mustStartAtOrAfter** ``` time.Now() or 1 ``` **_groupedSplits** ```solidity {} // TODO depending on implementation for bidding, may need to replace with split group for custom split allocator ``` **_fundAccessConstraints** ```solidity [] // no treasury funds go to project owner ``` **_terminals** See [Juicebox contract addresses](https://docs.juicebox.money/dev/resources/addresses/) on mainnet and Goerli. ```solidity [ // JBETHPaymentTerminal3_1 address on mainnet IJBPaymentTerminal("0xFA391De95Fcbcd3157268B91d8c7af083E607A5C") ] ``` **_memo** ```solidity "This is a group bid for a nounish token." ``` ### II) Bidding When users contribute funds to the project, new bids need to be created for the auction. #### Option A: [IJBSplitAllocator](https://docs.juicebox.money/dev/api/interfaces/ijbsplitallocator/) ```solidity { group: 1, splits: [ { preferClaimed: false, preferAddToBalance: false, percent: 1000000000, // 100%, out of 10^9 projectId: 0, beneficiary: 0x0000000000000000000000000000000000000000, lockedUntil: 0, // no lock time allocator: 0x6969696969696969696969696969696969696969 // TODO replace with allocator address } ] } ``` Grouped splits allow treasury payouts to be automatically forwarded to a set of predefined addresses. An allocator contract can be used to create a new bid for the auction. - in `launchProjectFor()`, set `_groupedSplits` to object in code snippet - in split allocator logic, forward treasury increases to custom auction bidder - require transaction fee because destination isn't a Juicebox project #### Option B: [IJBPayDelegate](https://docs.juicebox.money/dev/build/treasury-extensions/pay-delegate/) - in data source contract, return address of custom pay delegate in `payParams()` - in pay delegate logic, forward treasury increases to custom auction bidder - (called _after_ project tokens minted and distributed) If the project is outbid, the auction will return the funds back to the project's address. The above options only have access to new payment contributions rather than contributions + existing funds in the treasury, so they have to forward payouts to an auction bidder instead of creating a bid directly. The auction bidder should have optimizations to save on gas fees e.g. only make a bid if the treasury amount is more than the current highest bid or wait until the auction deadline is close before making a bid. It should also integrate with the project's governance model so that it can forward Nouns that are won to the correct address. ### III) Funding Cycle Parameters The initial payment and redemption parameters need to be changed once the auction is complete. #### Option A: New Funding Cycle Reconfigure the funding cycle with new parameters when the auction changes state. This can be done manually by users or automatically through program execution, but both introduce friction and complexity. - auction ongoing: - payments enabled - redemptions disabled (redemption rate = 0%) - auction won by project: - payments disabled - redemptions disabled (redemption rate = 0%) - auction won by someone else: - payments disabled - redemptions enabled (redemption rate = 100%) #### Option B: [IJBFundingCycleDataSource](https://docs.juicebox.money/dev/build/treasury-extensions/data-source/) Create a custom data source contract that implements `payParams()` and `redeemParams()`. Use conditional logic to control when users can contribute funds and redeem project tokens. - in `launchProjectFor()`, - set `_metadata` data source to deployed contract address - set `useDataSourceForPay` and `useDataSourceForRedeem` to true - `payParams(JBPayParamsData data)` - `return data.weight, data.memo, payDelegateAddress` - use default weight, default user message, and potentially new pay delegate - revert function if auction complete or invalid params - `redeemParams(JBRedeemParamsData data)` - `return data.reclaimAmount, data.memo, 0x0000000000000000000000000000000000000000` - use default reclaim amount, default user message, and default redemption delegate - revert function if auction ongoing, Noun won by the project, or invalid params - i.e. redemption rate is 0% in the above cases (since can't extract funds) and 100% in all other cases ### IV) Governance Once a project has won an auction, its members need to be able to create and vote on proposals in the parent DAO. The system should be unopinionated and allow for various governance models e.g. both option A or B can work. #### Option A: Safe + Snapshot - off-chain: can save on gas fees - can't set up programmatically for project and require ENS domain - [two strategies](https://docs.juicebox.money/user/resources/snapshot/#strategies): project token voting + project token delegation #### Option B: Governor Bravo - on-chain: have to pay gas fees - require ERC-20 deploy Use a cron job to submit votes to the parent DAO so that manual user action isn't needed. This can be _x_ minutes before the voting deadline. ### V) Next Steps - deploy Juicebox project to Goerli - validate with custom [Nouns Builder DAO](https://testnet.nouns.build/dao/0xe7ff4134De785E00305EC62D9dD52bc8b41b14e8/1?tab=about) on testnet