diff --git a/.env.contracts b/.env.contracts index e3a458c6..a1a45072 100755 --- a/.env.contracts +++ b/.env.contracts @@ -1,17 +1,15 @@ -OPENQ_PROXY_ADDRESS=0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 -OPENQ_IMPLEMENTATION_ADDRESS=0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 -CLAIM_MANAGER_PROXY_ADDRESS=0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6 -CLAIM_MANAGER_IMPLEMENTATION_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 -DEPOSIT_MANAGER_PROXY_ADDRESS=0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e -DEPOSIT_MANAGER_IMPLEMENTATION_ADDRESS=0x610178dA211FEF7D417bC0e6FeD39F05609AD788 -OPENQ_BOUNTY_FACTORY_ADDRESS=0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44 -ATOMIC_BOUNTY_BEACON_ADDRESS=0x3Aa5ebB10DC797CAC828524e59A333d0A371443c -ONGOING_BOUNTY_BEACON_ADDRESS=0xc6e7DF5E7b4f2A278906862b61205850344D4e7d -TIERED_BOUNTY_BEACON_ADDRESS=0x59b670e9fA9D0A427751Af201D676719a970857b -TIERED_FIXED_BOUNTY_BEACON_ADDRESS=0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1 -OPENQ_TOKEN_WHITELIST_ADDRESS=0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 +OPENQ_PROXY_ADDRESS=0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E +OPENQ_IMPLEMENTATION_ADDRESS=0x67d269191c92Caf3cD7723F116c85e6E9bf55933 +CLAIM_MANAGER_PROXY_ADDRESS=0x9E545E3C0baAB3E08CdfD552C960A1050f373042 +CLAIM_MANAGER_IMPLEMENTATION_ADDRESS=0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB +DEPOSIT_MANAGER_PROXY_ADDRESS=0x851356ae760d987E095750cCeb3bC6014560891C +DEPOSIT_MANAGER_IMPLEMENTATION_ADDRESS=0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8 +OPENQ_BOUNTY_FACTORY_ADDRESS=0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf +ATOMIC_BOUNTY_BEACON_ADDRESS=0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf +TIERED_FIXED_BOUNTY_BEACON_ADDRESS=0x0E801D84Fa97b50751Dbf25036d067dCf18858bF +OPENQ_TOKEN_WHITELIST_ADDRESS=0x95401dc811bb5740090279Ba06cfA8fcF6113778 OPENQ_DEPLOY_BLOCK_NUMBER=1 -MOCK_LINK_TOKEN_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3 -MOCK_DAI_TOKEN_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 -MOCK_NFT_TOKEN_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -MOCK_DAI_BLACKLISTED_TOKEN_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 +MOCK_LINK_TOKEN_ADDRESS=0x4A679253410272dd5232B3Ff7cF5dbB88f295319 +MOCK_DAI_TOKEN_ADDRESS=0x09635F643e140090A9A8Dcd712eD6285858ceBef +MOCK_NFT_TOKEN_ADDRESS=0x7a2088a1bFc9d81c55368AE168C2C02570cB814F +MOCK_DAI_BLACKLISTED_TOKEN_ADDRESS=0xc5a5C42992dECbae36851359345FE25997F5C42d diff --git a/contracts/Bounty/Implementations/AtomicBountyV1.sol b/contracts/Bounty/Implementations/AtomicBountyV1.sol index ab535a6a..3ad1501a 100755 --- a/contracts/Bounty/Implementations/AtomicBountyV1.sol +++ b/contracts/Bounty/Implementations/AtomicBountyV1.sol @@ -46,7 +46,6 @@ contract AtomicBountyV1 is AtomicBountyStorageV1 { issuer = _issuer; organization = _organization; bountyCreatedTime = block.timestamp; - nftDepositLimit = 5; ( bool _hasFundingGoal, @@ -116,41 +115,6 @@ contract AtomicBountyV1 is AtomicBountyStorageV1 { closerData = _closerData; } - /// @notice Receives an NFT for this contract - /// @param _sender Sender of the NFT - /// @param _tokenAddress NFT token address - /// @param _tokenId NFT token id - /// @param _expiration How long before this deposit becomes refundable - /// @return bytes32 the deposit id - function receiveNft( - address _sender, - address _tokenAddress, - uint256 _tokenId, - uint256 _expiration, - bytes calldata - ) external onlyDepositManager nonReentrant returns (bytes32) { - require( - nftDeposits.length < nftDepositLimit, - Errors.NFT_DEPOSIT_LIMIT_REACHED - ); - require(_expiration > 0, Errors.EXPIRATION_NOT_GREATER_THAN_ZERO); - _receiveNft(_tokenAddress, _sender, _tokenId); - - bytes32 depositId = _generateDepositId(); - - funder[depositId] = _sender; - tokenAddress[depositId] = _tokenAddress; - depositTime[depositId] = block.timestamp; - tokenId[depositId] = _tokenId; - expiration[depositId] = _expiration; - isNFT[depositId] = true; - - deposits.push(depositId); - nftDeposits.push(depositId); - - return depositId; - } - /// @notice Whether or not invoice has been completed /// @param _data ABI encoded data /// @dev see IBountyCore.setInvoiceComplete.(_data) for _data ABI encoding schema diff --git a/contracts/Bounty/Implementations/BountyCore.sol b/contracts/Bounty/Implementations/BountyCore.sol index 7de0deb9..c87a6bf2 100755 --- a/contracts/Bounty/Implementations/BountyCore.sol +++ b/contracts/Bounty/Implementations/BountyCore.sol @@ -49,7 +49,6 @@ abstract contract BountyCore is BountyStorageCore { volume[depositId] = volumeReceived; depositTime[depositId] = block.timestamp; expiration[depositId] = _expiration; - isNFT[depositId] = false; deposits.push(depositId); tokenAddresses.add(_tokenAddress); @@ -57,10 +56,10 @@ abstract contract BountyCore is BountyStorageCore { return (depositId, volumeReceived); } - /// @notice Transfers volume of deposit or NFT of deposit from bounty to funder + /// @notice Transfers volume of deposit from bounty to funder /// @param _depositId The deposit to refund /// @param _funder The initial funder of the deposit - /// @param _volume The volume to be refunded (only relevant if deposit is not an NFT, otherwise is zero) + /// @param _volume The volume to be refunded function refundDeposit( bytes32 _depositId, address _funder, @@ -77,12 +76,6 @@ abstract contract BountyCore is BountyStorageCore { if (tokenAddress[_depositId] == address(0)) { _transferProtocolToken(funder[_depositId], _volume); - } else if (isNFT[_depositId]) { - _transferNft( - tokenAddress[_depositId], - funder[_depositId], - tokenId[_depositId] - ); } else { _transferERC20( tokenAddress[_depositId], @@ -119,22 +112,6 @@ abstract contract BountyCore is BountyStorageCore { return expiration[_depositId]; } - /// @notice Transfers NFT from bounty address to _payoutAddress - /// @param _payoutAddress The destination address for the NFT - /// @param _depositId The payout address of the bounty - function claimNft(address _payoutAddress, bytes32 _depositId) - external - virtual - onlyClaimManager - nonReentrant - { - _transferNft( - tokenAddress[_depositId], - _payoutAddress, - tokenId[_depositId] - ); - } - /// @notice Sets the funding goal /// @param _fundingToken Token address for funding goal /// @param _fundingGoal Token volume for funding goal @@ -237,32 +214,6 @@ abstract contract BountyCore is BountyStorageCore { payable(_payoutAddress).sendValue(_volume); } - /// @notice Receives NFT of _tokenId on _tokenAddress from _funder to bounty address - /// @param _tokenAddress The ERC721 token address - /// @param _sender The sender of the NFT - /// @param _tokenId The tokenId - function _receiveNft( - address _tokenAddress, - address _sender, - uint256 _tokenId - ) internal virtual { - IERC721Upgradeable nft = IERC721Upgradeable(_tokenAddress); - nft.safeTransferFrom(_sender, address(this), _tokenId); - } - - /// @notice Transfers NFT of _tokenId on _tokenAddress from bounty address to _payoutAddress - /// @param _tokenAddress The ERC721 token address - /// @param _payoutAddress The sender of the NFT - /// @param _tokenId The tokenId - function _transferNft( - address _tokenAddress, - address _payoutAddress, - uint256 _tokenId - ) internal virtual { - IERC721Upgradeable nft = IERC721Upgradeable(_tokenAddress); - nft.safeTransferFrom(address(this), _payoutAddress, _tokenId); - } - /// @notice Generates a unique deposit ID from bountyId and the current length of deposits function _generateDepositId() internal view virtual returns (bytes32) { return keccak256(abi.encode(bountyId, deposits.length)); @@ -298,18 +249,12 @@ abstract contract BountyCore is BountyStorageCore { return token.balanceOf(address(this)); } - /// @notice Returns an array of all deposits (ERC20, protocol token, and NFT) for this bounty - /// @return deposits The array of deposits including ERC20, protocol token, and NFT + /// @notice Returns an array of all deposits for this bounty + /// @return deposits The array of deposits including ERC20 and protocol token function getDeposits() external view virtual returns (bytes32[] memory) { return deposits; } - /// @notice Returns an array of ONLY NFT deposits for this bounty - /// @return nftDeposits The array of NFT deposits - function getNftDeposits() external view virtual returns (bytes32[] memory) { - return nftDeposits; - } - /// @notice Returns an array of all ERC20 token addresses which have funded this bounty /// @return tokenAddresses An array of all ERC20 token addresses which have funded this bounty function getTokenAddresses() diff --git a/contracts/Bounty/Implementations/OngoingBountyV1.sol b/contracts/Bounty/Implementations/OngoingBountyV1.sol deleted file mode 100755 index d78f566f..00000000 --- a/contracts/Bounty/Implementations/OngoingBountyV1.sol +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - -import '../Storage/OngoingBountyStorage.sol'; - -/// @title OngoingBountyV1 -/// @author FlacoJones -/// @notice Bounty implementation for multiple contributor, multiple + fixed payout scenarios (e.g. 500 USDC for every submission) -/// @dev OngoingBountyV1 -> OngoingBountyStorageV1 -> BountyCore -> BountyStorageCore -> Core Dependencies (OZ + Custom) -/// @dev Do not add any new storage variables here. Put them in a TieredPercentageBountyStorageV# and release new implementation -contract OngoingBountyV1 is OngoingBountyStorageV1 { - using SafeERC20Upgradeable for IERC20Upgradeable; - using AddressUpgradeable for address payable; - using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; - - constructor() {} - - /// @notice Initializes a bounty proxy with initial state - /// @param _bountyId The unique bounty identifier - /// @param _issuer The sender of the mint bounty transaction - /// @param _organization The organization associated with the bounty - /// @param _openQ The OpenQProxy address - /// @param _claimManager The Claim Manager proxy address - /// @param _depositManager The Deposit Manager proxy address - /// @param _operation The ABI encoded data determining the type of bounty being initialized and associated data - /// @dev see IBountyCore.initialize.(_operation) for _operation ABI encoding schema for ONGOING - function initialize( - string memory _bountyId, - address _issuer, - string memory _organization, - address _openQ, - address _claimManager, - address _depositManager, - OpenQDefinitions.InitOperation memory _operation - ) external initializer { - require(bytes(_bountyId).length != 0, Errors.NO_EMPTY_BOUNTY_ID); - require(bytes(_organization).length != 0, Errors.NO_EMPTY_ORGANIZATION); - - __ReentrancyGuard_init(); - - __OnlyOpenQ_init(_openQ); - __ClaimManagerOwnable_init(_claimManager); - __DepositManagerOwnable_init(_depositManager); - - bountyId = _bountyId; - issuer = _issuer; - organization = _organization; - bountyCreatedTime = block.timestamp; - nftDepositLimit = 5; - - ( - address _payoutTokenAddress, - uint256 _payoutVolume, - bool _hasFundingGoal, - address _fundingToken, - uint256 _fundingGoal, - bool _invoiceRequired, - bool _kycRequired, - bool _supportingDocumentsRequired, - string memory _issuerExternalUserId, - , - - ) = abi.decode( - _operation.data, - ( - address, - uint256, - bool, - address, - uint256, - bool, - bool, - bool, - string, - string, - string - ) - ); - - bountyType = OpenQDefinitions.ONGOING; - payoutTokenAddress = _payoutTokenAddress; - payoutVolume = _payoutVolume; - hasFundingGoal = _hasFundingGoal; - fundingToken = _fundingToken; - fundingGoal = _fundingGoal; - invoiceRequired = _invoiceRequired; - kycRequired = _kycRequired; - supportingDocumentsRequired = _supportingDocumentsRequired; - issuerExternalUserId = _issuerExternalUserId; - } - - /// @notice Transfers a payout amount of an ongoing bounty to claimant for claimant asset - /// @param _payoutAddress The destination address for the funds - /// @param _closerData ABI-encoded data of the claimant and claimant asset - /// @dev see IBountyCore.claimOngoingPayout.(_closerData) for _closerData ABI encoding schema - function claimOngoingPayout( - address _payoutAddress, - bytes calldata _closerData - ) external onlyClaimManager nonReentrant returns (address, uint256) { - (, string memory claimant, , string memory claimantAsset) = abi.decode( - _closerData, - (address, string, address, string) - ); - - bytes32 _claimId = generateClaimId(claimant, claimantAsset); - - claimId[_claimId] = true; - claimIds.push(_claimId); - - _transferToken(payoutTokenAddress, payoutVolume, _payoutAddress); - return (payoutTokenAddress, payoutVolume); - } - - /// @notice Similar to close() for single priced bounties. Stops all withdrawls. - /// @param _closer Address of the closer - function closeOngoing(address _closer) external onlyOpenQ { - require( - status == OpenQDefinitions.OPEN, - Errors.CONTRACT_ALREADY_CLOSED - ); - require(_closer == issuer, Errors.CALLER_NOT_ISSUER); - - status = OpenQDefinitions.CLOSED; - bountyClosedTime = block.timestamp; - } - - /// @notice Receives an NFT for this contract - /// @param _sender Sender of the NFT - /// @param _tokenAddress NFT token address - /// @param _tokenId NFT token id - /// @param _expiration How long before this deposit becomes refundable - /// @return bytes32 the deposit id - function receiveNft( - address _sender, - address _tokenAddress, - uint256 _tokenId, - uint256 _expiration, - bytes calldata - ) external onlyDepositManager nonReentrant returns (bytes32) { - require( - nftDeposits.length < nftDepositLimit, - Errors.NFT_DEPOSIT_LIMIT_REACHED - ); - require(_expiration > 0, Errors.EXPIRATION_NOT_GREATER_THAN_ZERO); - _receiveNft(_tokenAddress, _sender, _tokenId); - - bytes32 depositId = _generateDepositId(); - - funder[depositId] = _sender; - tokenAddress[depositId] = _tokenAddress; - depositTime[depositId] = block.timestamp; - tokenId[depositId] = _tokenId; - expiration[depositId] = _expiration; - isNFT[depositId] = true; - - deposits.push(depositId); - nftDeposits.push(depositId); - - return depositId; - } - - /// @notice Sets the payout for an ongoing bounty - /// @param _payoutTokenAddress Sets payout token address - /// @param _payoutVolume Sets payout token volume - function setPayout(address _payoutTokenAddress, uint256 _payoutVolume) - external - onlyOpenQ - { - payoutTokenAddress = _payoutTokenAddress; - payoutVolume = _payoutVolume; - } - - /// @notice Whether or not invoice has been completed - /// @param _data ABI encoded data - /// @dev see IBountyCore.setInvoiceComplete.(_data) for _data ABI encoding schema - function setInvoiceComplete(bytes calldata _data) external onlyOpenQ { - (bytes32 _claimId, bool _invoiceComplete) = abi.decode( - _data, - (bytes32, bool) - ); - invoiceComplete[_claimId] = _invoiceComplete; - invoiceCompleteClaimIds.push(_claimId); - } - - /// @notice Whether or not supporting documents have been completed - /// @param _data ABI encoded data - /// @dev see IBountyCore.setSupportingDocumentsComplete.(_data) for _data ABI encoding schema - function setSupportingDocumentsComplete(bytes calldata _data) - external - onlyOpenQ - { - (bytes32 _claimId, bool _supportingDocumentsComplete) = abi.decode( - _data, - (bytes32, bool) - ); - supportingDocumentsComplete[_claimId] = _supportingDocumentsComplete; - supportingDocumentsCompleteClaimIds.push(_claimId); - } - - /// @notice Returns the claimIds which have completed supporting documents - /// @return Documents the return variables of a contract’s function state variable - /// @dev We return from all IBountyCore.getSupportingDocumentsComplete() as bytes to accomodate different return types - /// @dev _data (bytes32[]) - /// @dev _data (supportingDocumentsCompleteClaimIds) - function getSupportingDocumentsComplete() - external - view - returns (bytes memory) - { - return abi.encode(supportingDocumentsCompleteClaimIds); - } - - /// @notice Returns the claimIds which have completed supporting documents - /// @return Documents the return variables of a contract’s function state variable - /// @dev We return from all IBountyCore.getInvoiceComplete() as bytes to accomodate different return types - /// @dev _data (bytes32[]) - /// @dev _data (invoiceCompleteClaimIds) - function getInvoiceComplete() external view returns (bytes memory) { - return abi.encode(invoiceCompleteClaimIds); - } - - /// @notice Returns all claimIds - /// @return Array of bytes32 claim ids - function getClaimIds() public view returns (bytes32[] memory) { - return claimIds; - } - - /// @notice Generates a unique claimant ID from user and asset - function generateClaimId( - string memory claimant, - string memory claimantAsset - ) public pure virtual returns (bytes32) { - return keccak256(abi.encode(claimant, claimantAsset)); - } - - /// @notice receive() method to accept protocol tokens - receive() external payable { - revert( - 'Cannot send Ether directly to boutny contract. Please use the BountyV1.receiveFunds() method.' - ); - } -} diff --git a/contracts/Bounty/Implementations/TieredBountyCore.sol b/contracts/Bounty/Implementations/TieredBountyCore.sol index 80295904..78158775 100755 --- a/contracts/Bounty/Implementations/TieredBountyCore.sol +++ b/contracts/Bounty/Implementations/TieredBountyCore.sol @@ -8,45 +8,6 @@ import '../Storage/TieredBountyStorageCore.sol'; /// @notice Shared methods common to all tiered bounty types (tier meaning multiple payout levels, e.g. 1st, 2nd, 3rd) /// @dev TieredBountyCore -> TieredBountyStorageCore -> BountyCore -> BountyStorageCore -> Core Dependencies (OZ + Custom) abstract contract TieredBountyCore is TieredBountyStorageCore { - /// @notice Receives an NFT for this contract - /// @param _sender Sender of the NFT - /// @param _tokenAddress NFT token address - /// @param _tokenId NFT token id - /// @param _expiration How long before this deposit becomes refundable - /// @param _data ABI encoded data (unused in this case) - /// @return bytes32 the deposit id - function receiveNft( - address _sender, - address _tokenAddress, - uint256 _tokenId, - uint256 _expiration, - bytes calldata _data - ) external override onlyDepositManager nonReentrant returns (bytes32) { - require( - nftDeposits.length < nftDepositLimit, - Errors.NFT_DEPOSIT_LIMIT_REACHED - ); - require(_expiration > 0, Errors.EXPIRATION_NOT_GREATER_THAN_ZERO); - _receiveNft(_tokenAddress, _sender, _tokenId); - - bytes32 depositId = _generateDepositId(); - - funder[depositId] = _sender; - tokenAddress[depositId] = _tokenAddress; - depositTime[depositId] = block.timestamp; - tokenId[depositId] = _tokenId; - expiration[depositId] = _expiration; - isNFT[depositId] = true; - - uint256 _tier = abi.decode(_data, (uint256)); - tier[depositId] = _tier; - - deposits.push(depositId); - nftDeposits.push(depositId); - - return depositId; - } - /// @notice Sets tierClaimed to true for the given tier /// @param _tier The tier being claimed function setTierClaimed(uint256 _tier) external onlyClaimManager { diff --git a/contracts/Bounty/Implementations/TieredFixedBountyV1.sol b/contracts/Bounty/Implementations/TieredFixedBountyV1.sol index fd40573c..f35312ab 100755 --- a/contracts/Bounty/Implementations/TieredFixedBountyV1.sol +++ b/contracts/Bounty/Implementations/TieredFixedBountyV1.sol @@ -23,7 +23,7 @@ contract TieredFixedBountyV1 is TieredFixedBountyStorageV1 { /// @param _claimManager The Claim Manager proxy address /// @param _depositManager The Deposit Manager proxy address /// @param _operation The ABI encoded data determining the type of bounty being initialized and associated data - /// @dev see IBountyCore.initialize.(_operation) for _operation ABI encoding schema for TIERED FIXED + /// @dev see IBountyCore.initialize.(_operation) for _operation ABI encoding schema for TIERED FIXED function initialize( string memory _bountyId, address _issuer, @@ -46,7 +46,6 @@ contract TieredFixedBountyV1 is TieredFixedBountyStorageV1 { issuer = _issuer; organization = _organization; bountyCreatedTime = block.timestamp; - nftDepositLimit = 5; ( uint256[] memory _payoutSchedule, @@ -59,21 +58,12 @@ contract TieredFixedBountyV1 is TieredFixedBountyStorageV1 { ) = abi.decode( _operation.data, - ( - uint256[], - address, - bool, - bool, - bool, - string, - string, - string - ) + (uint256[], address, bool, bool, bool, string, string, string) ); bountyType = OpenQDefinitions.TIERED_FIXED; payoutSchedule = _payoutSchedule; - payoutTokenAddress = _payoutTokenAddress; + payoutTokenAddress = _payoutTokenAddress; invoiceRequired = _invoiceRequired; kycRequired = _kycRequired; supportingDocumentsRequired = _supportingDocumentsRequired; diff --git a/contracts/Bounty/Implementations/TieredPercentageBountyV1.sol b/contracts/Bounty/Implementations/TieredPercentageBountyV1.sol deleted file mode 100755 index 5296740e..00000000 --- a/contracts/Bounty/Implementations/TieredPercentageBountyV1.sol +++ /dev/null @@ -1,187 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - -import '../Storage/TieredPercentageBountyStorage.sol'; - -/// @title TieredPercentageBountyV1 -/// @notice TieredPercentageBountyV1 is a bounty implementation contract for single contributor, single payout circumstances -/// @dev TieredPercentageBountyV1 -> TieredPercentageBountyStorageV1 -> TieredBountyCore -> TieredBountyStorageCore -> (BountyCore -> BountyStorageCore) -> (Third Party Deps + Custom ) -/// @dev Do not add any new storage variables here. Put them in a TieredPercentageBountyStorageV# and release new implementation -contract TieredPercentageBountyV1 is TieredPercentageBountyStorageV1 { - using SafeERC20Upgradeable for IERC20Upgradeable; - using AddressUpgradeable for address payable; - using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; - - constructor() {} - - /// @notice Initializes a bounty proxy with initial state - /// @param _bountyId The unique bounty identifier - /// @param _issuer The sender of the mint bounty transaction - /// @param _organization The organization associated with the bounty - /// @param _openQ The OpenQProxy address - /// @param _claimManager The Claim Manager proxy address - /// @param _depositManager The Deposit Manager proxy address - /// @param _operation The ABI encoded data determining the type of bounty being initialized and associated data - /// @dev see IBountyCore.initialize.(_operation) for _operation ABI encoding schema for TIERED PERCENTAGE - function initialize( - string memory _bountyId, - address _issuer, - string memory _organization, - address _openQ, - address _claimManager, - address _depositManager, - OpenQDefinitions.InitOperation memory _operation - ) external initializer { - require(bytes(_bountyId).length != 0, Errors.NO_EMPTY_BOUNTY_ID); - require(bytes(_organization).length != 0, Errors.NO_EMPTY_ORGANIZATION); - - __ReentrancyGuard_init(); - - __OnlyOpenQ_init(_openQ); - __ClaimManagerOwnable_init(_claimManager); - __DepositManagerOwnable_init(_depositManager); - - bountyId = _bountyId; - issuer = _issuer; - organization = _organization; - bountyCreatedTime = block.timestamp; - nftDepositLimit = 5; - - ( - uint256[] memory _payoutSchedule, - bool _hasFundingGoal, - address _fundingToken, - uint256 _fundingGoal, - bool _invoiceRequired, - bool _kycRequired, - bool _supportingDocumentsRequired, - string memory _issuerExternalUserId, - , - - ) = abi.decode( - _operation.data, - ( - uint256[], - bool, - address, - uint256, - bool, - bool, - bool, - string, - string, - string - ) - ); - - uint256 sum; - for (uint256 i = 0; i < _payoutSchedule.length; i++) { - sum += _payoutSchedule[i]; - } - require(sum == 100, Errors.PAYOUT_SCHEDULE_MUST_ADD_TO_100); - payoutSchedule = _payoutSchedule; - - bountyType = OpenQDefinitions.TIERED_PERCENTAGE; - hasFundingGoal = _hasFundingGoal; - fundingToken = _fundingToken; - fundingGoal = _fundingGoal; - invoiceRequired = _invoiceRequired; - kycRequired = _kycRequired; - supportingDocumentsRequired = _supportingDocumentsRequired; - issuerExternalUserId = _issuerExternalUserId; - - // Initialize metadata arrays to same number of tiers - tierWinners = new string[](_payoutSchedule.length); - invoiceComplete = new bool[](_payoutSchedule.length); - supportingDocumentsComplete = new bool[](_payoutSchedule.length); - } - - /// @notice Transfers the tiered percentage of the token balance of _tokenAddress from bounty to _payoutAddress - /// @param _payoutAddress The destination address for the fund - /// @param _tier The ordinal of the claimant (e.g. 1st place, 2nd place) - /// @param _tokenAddress The token address being claimed - /// @return Volume of claimed token payout - function claimTiered( - address _payoutAddress, - uint256 _tier, - address _tokenAddress - ) external onlyClaimManager nonReentrant returns (uint256) { - require( - bountyType == OpenQDefinitions.TIERED_PERCENTAGE, - Errors.NOT_A_TIERED_BOUNTY - ); - require(!tierClaimed[_tier], Errors.TIER_ALREADY_CLAIMED); - - uint256 claimedBalance = (payoutSchedule[_tier] * - fundingTotals[_tokenAddress]) / 100; - - _transferToken(_tokenAddress, claimedBalance, _payoutAddress); - return claimedBalance; - } - - /// @notice Similar to close() for single priced bounties. closeCompetition() freezes the current funds for the competition. - function closeCompetition() external onlyClaimManager { - require( - status == OpenQDefinitions.OPEN, - Errors.CONTRACT_ALREADY_CLOSED - ); - - status = OpenQDefinitions.CLOSED; - bountyClosedTime = block.timestamp; - - for (uint256 i = 0; i < getTokenAddresses().length; i++) { - address _tokenAddress = getTokenAddresses()[i]; - fundingTotals[_tokenAddress] = getTokenBalance(_tokenAddress); - } - } - - /// @notice Sets the payout schedule - /// @notice There is no tokenAddress needed here - payouts on percentage tiered bounties is a percentage of whatever is deposited on the contract - /// @param _payoutSchedule An array of payout volumes for each tier - function setPayoutSchedule(uint256[] calldata _payoutSchedule) - external - onlyOpenQ - { - require( - bountyType == OpenQDefinitions.TIERED_PERCENTAGE, - Errors.NOT_A_TIERED_BOUNTY - ); - uint256 sum; - for (uint256 i = 0; i < _payoutSchedule.length; i++) { - sum += _payoutSchedule[i]; - } - require(sum == 100, Errors.PAYOUT_SCHEDULE_MUST_ADD_TO_100); - - payoutSchedule = _payoutSchedule; - - // Resize metadata arrays and copy current members to new array - // NOTE: If resizing to fewer tiers than previously, the final indexes will be removed - string[] memory newTierWinners = new string[](payoutSchedule.length); - bool[] memory newInvoiceComplete = new bool[](payoutSchedule.length); - bool[] memory newSupportingDocumentsCompleted = new bool[]( - payoutSchedule.length - ); - - for (uint256 i = 0; i < tierWinners.length; i++) { - newTierWinners[i] = tierWinners[i]; - } - tierWinners = newTierWinners; - - for (uint256 i = 0; i < invoiceComplete.length; i++) { - newInvoiceComplete[i] = invoiceComplete[i]; - } - invoiceComplete = newInvoiceComplete; - - for (uint256 i = 0; i < supportingDocumentsComplete.length; i++) { - newSupportingDocumentsCompleted[i] = supportingDocumentsComplete[i]; - } - supportingDocumentsComplete = newSupportingDocumentsCompleted; - } - - /// @notice receive() method to accept protocol tokens - receive() external payable { - revert( - 'Cannot send Ether directly to boutny contract. Please use the BountyV1.receiveFunds() method.' - ); - } -} diff --git a/contracts/Bounty/Interfaces/IBounty.sol b/contracts/Bounty/Interfaces/IBounty.sol index 47976e62..23c6f47d 100755 --- a/contracts/Bounty/Interfaces/IBounty.sol +++ b/contracts/Bounty/Interfaces/IBounty.sol @@ -2,18 +2,11 @@ pragma solidity 0.8.17; import './IAtomicBounty.sol'; -import './IOngoingBounty.sol'; -import './ITieredPercentageBounty.sol'; import './ITieredFixedBounty.sol'; /// @title IBounty /// @author FlacoJones /// @notice Interface aggregating all bounty type interfaces for use in OpenQ, ClaimManager and DepositManager -interface IBounty is - IAtomicBounty, - IOngoingBounty, - ITieredPercentageBounty, - ITieredFixedBounty -{ +interface IBounty is IAtomicBounty, ITieredFixedBounty { } diff --git a/contracts/Bounty/Interfaces/IBountyCore.sol b/contracts/Bounty/Interfaces/IBountyCore.sol index f5ec6a76..88b33a89 100755 --- a/contracts/Bounty/Interfaces/IBountyCore.sol +++ b/contracts/Bounty/Interfaces/IBountyCore.sol @@ -51,28 +51,10 @@ interface IBountyCore { uint256 _expiration ) external payable returns (bytes32, uint256); - /// @notice Receives an NFT for this contract - /// @param _sender Sender of the NFT - /// @param _tokenAddress NFT token address - /// @param _tokenId NFT token id - /// @param _expiration How long before this deposit becomes refundable - /// @param _data ABI encoded data (unused in this case) - /// @return bytes32 the deposit id - /// @dev _data (TIERED): (uint256):(tier) - /// @dev _data (ATOMIC): empty bytes array - /// @dev _data (ONGOING): empty bytes array - function receiveNft( - address _sender, - address _tokenAddress, - uint256 _tokenId, - uint256 _expiration, - bytes calldata _data - ) external returns (bytes32); - - /// @notice Transfers volume of deposit or NFT of deposit from bounty to funder + /// @notice Transfers volume of deposit from bounty to funder /// @param _depositId The deposit to refund /// @param _funder The initial funder of the deposit - /// @param _volume The volume to be refunded (only relevant if deposit is not an NFT, otherwise is zero) + /// @param _volume The volume to be refunded function refundDeposit( bytes32 _depositId, address _funder, @@ -89,11 +71,6 @@ interface IBountyCore { address _funder ) external returns (uint256); - /// @notice Transfers NFT from bounty address to _payoutAddress - /// @param _payoutAddress The destination address for the NFT - /// @param _depositId The payout address of the bounty - function claimNft(address _payoutAddress, bytes32 _depositId) external; - /// @notice Sets the funding goal /// @param _fundingToken Token address for funding goal /// @param _fundingGoal Token volume for funding goal @@ -149,10 +126,6 @@ interface IBountyCore { /// @return tokenAddresses An array of all ERC20 token addresses which have funded this bounty function getTokenAddresses() external view returns (address[] memory); - /// @notice Returns an array of ONLY NFT deposits for this bounty - /// @return nftDeposits The array of NFT deposits - function getNftDeposits() external view returns (bytes32[] memory); - /// @notice Returns the amount of locked tokens (of a specific token) on a bounty address, only available for claims but not for refunds /// @param _depositId The depositId that determines which token is being looked at /// @return uint256 @@ -177,8 +150,6 @@ interface IBountyCore { function status() external view returns (uint256); - function nftDepositLimit() external view returns (uint256); - function funder(bytes32) external view returns (address); function tokenAddress(bytes32) external view returns (address); @@ -195,12 +166,8 @@ interface IBountyCore { function expiration(bytes32) external view returns (uint256); - function isNFT(bytes32) external view returns (bool); - function deposits(uint256) external view returns (bytes32); - function nftDeposits(uint256) external view returns (bytes32); - function closerData() external view returns (bytes memory); function bountyType() external view returns (uint256); diff --git a/contracts/Bounty/Interfaces/IOngoingBounty.sol b/contracts/Bounty/Interfaces/IOngoingBounty.sol deleted file mode 100755 index 1915592a..00000000 --- a/contracts/Bounty/Interfaces/IOngoingBounty.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - -import './IBountyCore.sol'; - -/// @title IOngoingBounty -/// @author FlacoJones -/// @notice Interface defining OngoingBounty specific methods -interface IOngoingBounty is IBountyCore { - /// @notice Sets the payout for an ongoing bounty - /// @param _payoutTokenAddress Sets payout token address - /// @param _payoutVolume Sets payout token volume - function setPayout(address _payoutTokenAddress, uint256 _payoutVolume) - external; - - /// @notice Transfers a payout amount of an ongoing bounty to claimant for claimant asset - /// @param _payoutAddress The destination address for the funds - /// @param _closerData ABI-encoded data of the claimant and claimant asset - /// @dev _closerData (address,string,address,string,uint256) - /// @dev _closerData (bountyAddress, externalUserId, closer, claimantAsset, tier) - function claimOngoingPayout( - address _payoutAddress, - bytes calldata _closerData - ) external returns (address, uint256); - - /// @notice Similar to close() for single priced bounties. Stops all withdrawls. - /// @param _closer Address of the closer - function closeOngoing(address _closer) external; - - function getClaimIds() external returns (bytes32[] memory); - - function generateClaimId( - string memory claimant, - string memory claimantAsset - ) external pure returns (bytes32); - - function invoiceComplete(bytes32) external view returns (bool); - - function supportingDocumentsComplete(bytes32) external view returns (bool); - - function supportingDocumentsCompleteClaimIds() - external - view - returns (bytes32[] memory); - - function invoiceCompleteClaimIds() external view returns (bytes32[] memory); - - // PUBLIC GETTERS - function payoutTokenAddress() external view returns (address); - - function payoutVolume() external view returns (uint256); - - function claimId(bytes32) external view returns (bool); -} diff --git a/contracts/Bounty/Interfaces/ITieredFixedBounty.sol b/contracts/Bounty/Interfaces/ITieredFixedBounty.sol index b305bbe3..938bb894 100755 --- a/contracts/Bounty/Interfaces/ITieredFixedBounty.sol +++ b/contracts/Bounty/Interfaces/ITieredFixedBounty.sol @@ -22,4 +22,6 @@ interface ITieredFixedBounty is IBountyCore, ITieredBounty { function claimTieredFixed(address _payoutAddress, uint256 _tier) external returns (uint256); + + function payoutTokenAddress() external returns (address); } diff --git a/contracts/Bounty/Interfaces/ITieredPercentageBounty.sol b/contracts/Bounty/Interfaces/ITieredPercentageBounty.sol deleted file mode 100755 index 765bf3df..00000000 --- a/contracts/Bounty/Interfaces/ITieredPercentageBounty.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - -import './IBountyCore.sol'; -import './ITieredBounty.sol'; - -/// @title ITieredPercentageBounty -/// @author FlacoJones -/// @notice Interface defining TieredPercentageBounty specific methods -interface ITieredPercentageBounty is IBountyCore, ITieredBounty { - /// @notice Sets the payout schedule - /// @notice There is no tokenAddress needed here - payouts on percentage tiered bounties is a percentage of whatever is deposited on the contract - /// @param _payoutSchedule An array of payout volumes for each tier - function setPayoutSchedule(uint256[] calldata _payoutSchedule) external; - - /// @notice Transfers the tiered percentage of the token balance of _tokenAddress from bounty to _payoutAddress - /// @param _payoutAddress The destination address for the fund - /// @param _tier The ordinal of the claimant (e.g. 1st place, 2nd place) - /// @param _tokenAddress The token address being claimed - /// @return Volume of claimed token payout - function claimTiered( - address _payoutAddress, - uint256 _tier, - address _tokenAddress - ) external returns (uint256); -} diff --git a/contracts/Bounty/Storage/BountyStorageCore.sol b/contracts/Bounty/Storage/BountyStorageCore.sol index a2b4505e..0d05d3cd 100755 --- a/contracts/Bounty/Storage/BountyStorageCore.sol +++ b/contracts/Bounty/Storage/BountyStorageCore.sol @@ -2,12 +2,10 @@ pragma solidity 0.8.17; import '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol'; import '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol'; import '@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol'; import '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol'; -import '@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol'; import '../../OnlyOpenQ/OnlyOpenQ.sol'; import '../../ClaimManager/ClaimManagerOwnable.sol'; @@ -24,7 +22,6 @@ import '../Interfaces/IBountyCore.sol'; abstract contract BountyStorageCore is IBountyCore, ReentrancyGuardUpgradeable, - ERC721HolderUpgradeable, OnlyOpenQ, ClaimManagerOwnable, DepositManagerOwnable @@ -37,7 +34,6 @@ abstract contract BountyStorageCore is string public organization; address public closer; uint256 public status; - uint256 public nftDepositLimit; /// @notice Deconstructed deposit struct mapping(bytes32 => address) public funder; @@ -48,11 +44,9 @@ abstract contract BountyStorageCore is mapping(bytes32 => address) public payoutAddress; mapping(bytes32 => uint256) public tokenId; mapping(bytes32 => uint256) public expiration; - mapping(bytes32 => bool) public isNFT; /// @notice Array of depositIds bytes32[] public deposits; - bytes32[] public nftDeposits; /// @notice Set of unique token address EnumerableSetUpgradeable.AddressSet internal tokenAddresses; diff --git a/contracts/Bounty/Storage/OngoingBountyStorage.sol b/contracts/Bounty/Storage/OngoingBountyStorage.sol deleted file mode 100755 index 715f5535..00000000 --- a/contracts/Bounty/Storage/OngoingBountyStorage.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - -import '../Implementations/BountyCore.sol'; - -/// @title OngoingBountyStorageV1 -/// @author FlacoJones -/// @notice Backwards compatible, append-only chain of storage contracts inherited by all OngoingBountyStorage implementations -/// @dev Add new variables for upgrades in a new, derived abstract contract that inherits from the previous storage contract version (see: https://forum.openzeppelin.com/t/to-inherit-version1-to-version2-or-to-copy-code-inheritance-order-from-version1-to-version2/28069) -abstract contract OngoingBountyStorageV1 is BountyCore { - uint256 public constant VERSION_1 = 1; - - /// @notice Ongoing Bounties pay out the same amount set by the minter for each submission. - /// @dev Only closed once minter explicitly closes - address public payoutTokenAddress; - uint256 public payoutVolume; - - /// @dev keccak256 hash of the claimant ID (GitHub ID) with the claimant asset ID (GitHub PR ID) - mapping(bytes32 => bool) public claimId; - - // Keys of claims, can be used off-chain as an iterable to determine completed payouts - bytes32[] public claimIds; - - // Quick lookup - mapping(bytes32 => bool) public invoiceComplete; - - mapping(bytes32 => bool) public supportingDocumentsComplete; - - bytes32[] public supportingDocumentsCompleteClaimIds; - - bytes32[] public invoiceCompleteClaimIds; -} diff --git a/contracts/Bounty/Storage/TieredPercentageBountyStorage.sol b/contracts/Bounty/Storage/TieredPercentageBountyStorage.sol deleted file mode 100755 index 1fab1836..00000000 --- a/contracts/Bounty/Storage/TieredPercentageBountyStorage.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - -import '../Implementations/TieredBountyCore.sol'; - -/// @title TieredPercentageBountyStorageV1 -/// @author FlacoJones -/// @notice Backwards compatible, append-only chain of storage contracts inherited by all TieredPercentageBountyStorage implementations -/// @dev Add new variables for upgrades in a new, derived abstract contract that inherits from the previous storage contract version (see: https://forum.openzeppelin.com/t/to-inherit-version1-to-version2-or-to-copy-code-inheritance-order-from-version1-to-version2/28069) -abstract contract TieredPercentageBountyStorageV1 is TieredBountyCore { - -} diff --git a/contracts/BountyFactory/BountyFactory.sol b/contracts/BountyFactory/BountyFactory.sol index be010fc4..574033a7 100755 --- a/contracts/BountyFactory/BountyFactory.sol +++ b/contracts/BountyFactory/BountyFactory.sol @@ -13,28 +13,20 @@ import '../Library/Errors.sol'; contract BountyFactory is OnlyOpenQ { /// @notice The address of the UpgradeableBeacon holding the current bounty implementation address public immutable atomicBountyBeacon; - address public immutable ongoingBountyBeacon; - address public immutable tieredPercentageBountyBeacon; address public immutable tieredFixedBountyBeacon; /// @notice Deploys and initializes a new BeaconProxy with implementation pulled from the appropriate BountyBeacon /// @param _openQ The OpenQProxy address, used to initialize OnlyOpenQ /// @param _atomicBountyBeacon The UpgradeableBeacon "BountyBeacon" address for Atomic contracts - /// @param _ongoingBountyBeacon The UpgradeableBeacon "BountyBeacon" address for Ongoing contracts - /// @param _tieredPercentageBountyBeacon The UpgradeableBeacon "BountyBeacon" address for Tiered contracts /// @param _tieredFixedBountyBeacon The UpgradeableBeacon "BountyBeacon" address for Tiered Fixed contracts constructor( address _openQ, address _atomicBountyBeacon, - address _ongoingBountyBeacon, - address _tieredPercentageBountyBeacon, address _tieredFixedBountyBeacon ) { __OnlyOpenQ_init(_openQ); atomicBountyBeacon = _atomicBountyBeacon; - ongoingBountyBeacon = _ongoingBountyBeacon; - tieredPercentageBountyBeacon = _tieredPercentageBountyBeacon; tieredFixedBountyBeacon = _tieredFixedBountyBeacon; } @@ -60,10 +52,6 @@ contract BountyFactory is OnlyOpenQ { if (operationType == OpenQDefinitions.ATOMIC) { beaconProxy = atomicBountyBeacon; - } else if (operationType == OpenQDefinitions.ONGOING) { - beaconProxy = ongoingBountyBeacon; - } else if (operationType == OpenQDefinitions.TIERED_PERCENTAGE) { - beaconProxy = tieredPercentageBountyBeacon; } else if (operationType == OpenQDefinitions.TIERED_FIXED) { beaconProxy = tieredFixedBountyBeacon; } else { diff --git a/contracts/ClaimManager/Implementations/ClaimManagerV1.sol b/contracts/ClaimManager/Implementations/ClaimManagerV1.sol index ea8ef1cf..849cdd99 100755 --- a/contracts/ClaimManager/Implementations/ClaimManagerV1.sol +++ b/contracts/ClaimManager/Implementations/ClaimManagerV1.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.17; import '../Storage/ClaimManagerStorage.sol'; import '../../Bounty/Interfaces/IAtomicBounty.sol'; import '../../Bounty/Interfaces/ITieredBounty.sol'; -import '../../Bounty/Interfaces/IOngoingBounty.sol'; /// @title ClaimManagerV1 /// @author FlacoJones @@ -53,10 +52,6 @@ contract ClaimManagerV1 is ClaimManagerStorageV1 { _closerData, VERSION_1 ); - } else if (_bountyType == OpenQDefinitions.ONGOING) { - _claimOngoingBounty(bounty, _closer, _closerData); - } else if (_bountyType == OpenQDefinitions.TIERED_PERCENTAGE) { - _claimTieredPercentageBounty(bounty, _closer, _closerData); } else if (_bountyType == OpenQDefinitions.TIERED_FIXED) { _claimTieredFixedBounty(bounty, _closer, _closerData); } else { @@ -101,8 +96,6 @@ contract ClaimManagerV1 is ClaimManagerStorageV1 { if (bounty.bountyType() == OpenQDefinitions.TIERED_FIXED) { _claimTieredFixedBounty(bounty, msg.sender, _closerData); - } else if (bounty.bountyType() == OpenQDefinitions.TIERED_PERCENTAGE) { - _claimTieredPercentageBounty(bounty, msg.sender, _closerData); } else { revert(Errors.NOT_A_COMPETITION_CONTRACT); } @@ -146,129 +139,6 @@ contract ClaimManagerV1 is ClaimManagerStorageV1 { VERSION_1 ); } - - for (uint256 i = 0; i < _bounty.getNftDeposits().length; i++) { - _bounty.claimNft(_closer, _bounty.nftDeposits(i)); - - emit NFTClaimed( - _bounty.bountyId(), - address(_bounty), - _bounty.organization(), - _closer, - block.timestamp, - _bounty.tokenAddress(_bounty.nftDeposits(i)), - _bounty.tokenId(_bounty.nftDeposits(i)), - _bounty.bountyType(), - _closerData, - VERSION_1 - ); - } - } - - /// @notice Claim method for OngoingBounty - /// @param _bounty The payout address of the bounty - /// @param _closer The payout address of the claimant - /// @param _closerData ABI Encoded data associated with this claim - /// @dev see IBountyCore.claimOngoingPayout.(_closerData) for _closerData ABI encoding schema - function _claimOngoingBounty( - IOngoingBounty _bounty, - address _closer, - bytes calldata _closerData - ) internal { - _eligibleToClaimOngoingBounty(_bounty, _closer, _closerData); - - (address tokenAddress, uint256 volume) = _bounty.claimOngoingPayout( - _closer, - _closerData - ); - - emit TokenBalanceClaimed( - _bounty.bountyId(), - address(_bounty), - _bounty.organization(), - _closer, - block.timestamp, - tokenAddress, - volume, - _bounty.bountyType(), - _closerData, - VERSION_1 - ); - } - - /// @notice Claim method for TieredPercentageBounty - /// @param _bounty The payout address of the bounty - /// @param _closer The payout address of the claimant - /// @param _closerData ABI Encoded data associated with this claim - function _claimTieredPercentageBounty( - IBounty _bounty, - address _closer, - bytes calldata _closerData - ) internal { - (, , , , uint256 _tier) = abi.decode( - _closerData, - (address, string, address, string, uint256) - ); - - _eligibleToClaimTier(_bounty, _tier, _closer); - - if (_bounty.status() == 0) { - _bounty.closeCompetition(); - - emit BountyClosed( - _bounty.bountyId(), - address(_bounty), - _bounty.organization(), - address(0), - block.timestamp, - _bounty.bountyType(), - new bytes(0), - VERSION_1 - ); - } - - for (uint256 i = 0; i < _bounty.getTokenAddresses().length; i++) { - uint256 volume = _bounty.claimTiered( - _closer, - _tier, - _bounty.getTokenAddresses()[i] - ); - - emit TokenBalanceClaimed( - _bounty.bountyId(), - address(_bounty), - _bounty.organization(), - _closer, - block.timestamp, - _bounty.getTokenAddresses()[i], - volume, - _bounty.bountyType(), - _closerData, - VERSION_1 - ); - } - - for (uint256 i = 0; i < _bounty.getNftDeposits().length; i++) { - bytes32 _depositId = _bounty.nftDeposits(i); - if (_bounty.tier(_depositId) == _tier) { - _bounty.claimNft(_closer, _depositId); - - emit NFTClaimed( - _bounty.bountyId(), - address(_bounty), - _bounty.organization(), - _closer, - block.timestamp, - _bounty.tokenAddress(_depositId), - _bounty.tokenId(_depositId), - _bounty.bountyType(), - _closerData, - VERSION_1 - ); - } - } - - _bounty.setTierClaimed(_tier); } /// @notice Claim method for TieredFixedBounty @@ -317,53 +187,9 @@ contract ClaimManagerV1 is ClaimManagerStorageV1 { VERSION_1 ); - for (uint256 i = 0; i < _bounty.getNftDeposits().length; i++) { - bytes32 _depositId = _bounty.nftDeposits(i); - if (_bounty.tier(_depositId) == _tier) { - _bounty.claimNft(_closer, _depositId); - - emit NFTClaimed( - _bounty.bountyId(), - address(_bounty), - _bounty.organization(), - _closer, - block.timestamp, - _bounty.tokenAddress(_depositId), - _bounty.tokenId(_depositId), - _bounty.bountyType(), - _closerData, - VERSION_1 - ); - } - } - _bounty.setTierClaimed(_tier); } - /// @notice Checks if bounty associated with _bountyId is open - /// @return bool True if _bountyId is associated with an open bounty - function bountyIsClaimable(address _bountyAddress) - public - view - returns (bool) - { - IBounty bounty = IBounty(payable(_bountyAddress)); - - uint256 status = bounty.status(); - uint256 _bountyType = bounty.bountyType(); - - if ( - _bountyType == OpenQDefinitions.ATOMIC || - _bountyType == OpenQDefinitions.ONGOING || - _bountyType == OpenQDefinitions.TIERED_PERCENTAGE || - _bountyType == OpenQDefinitions.TIERED_FIXED - ) { - return status == 0; - } else { - return status == 1; - } - } - /// @notice Override for UUPSUpgradeable._authorizeUpgrade(address newImplementation) to enforce onlyOwner upgrades function _authorizeUpgrade(address) internal override onlyOwner {} @@ -451,41 +277,4 @@ contract ClaimManagerV1 is ClaimManagerStorageV1 { require(hasKYC(_closer), Errors.ADDRESS_LACKS_KYC); } } - - /// @notice Runs all require statements to determine if the claimant can claim an ongoing bounty payout - function _eligibleToClaimOngoingBounty( - IOngoingBounty bounty, - address _closer, - bytes memory _closerData - ) internal view { - require( - bounty.status() == OpenQDefinitions.OPEN, - Errors.CONTRACT_IS_NOT_CLAIMABLE - ); - - (, string memory claimant, , string memory claimantAsset) = abi.decode( - _closerData, - (address, string, address, string) - ); - - bytes32 claimId = bounty.generateClaimId(claimant, claimantAsset); - - if (bounty.invoiceRequired()) { - require( - bounty.invoiceComplete(claimId), - Errors.INVOICE_NOT_COMPLETE - ); - } - - if (bounty.supportingDocumentsRequired()) { - require( - bounty.supportingDocumentsComplete(claimId), - Errors.SUPPORTING_DOCS_NOT_COMPLETE - ); - } - - if (bounty.kycRequired()) { - require(hasKYC(_closer), Errors.ADDRESS_LACKS_KYC); - } - } } diff --git a/contracts/DepositManager/Implementations/DepositManagerV1.sol b/contracts/DepositManager/Implementations/DepositManagerV1.sol index 21710a6d..83ae6024 100755 --- a/contracts/DepositManager/Implementations/DepositManagerV1.sol +++ b/contracts/DepositManager/Implementations/DepositManagerV1.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import '../Storage/DepositManagerStorage.sol'; +import 'hardhat/console.sol'; /// @title DepositManagerV1 /// @author FlacoJones @@ -42,14 +43,9 @@ contract DepositManagerV1 is DepositManagerStorageV1 { ) external payable onlyProxy { IBounty bounty = IBounty(payable(_bountyAddress)); - if (!isWhitelisted(_tokenAddress)) { - require( - !tokenAddressLimitReached(_bountyAddress), - Errors.TOO_MANY_TOKEN_ADDRESSES - ); - } + require(msg.sender == bounty.issuer(), Errors.CALLER_NOT_ISSUER); - require(bountyIsOpen(_bountyAddress), Errors.CONTRACT_ALREADY_CLOSED); + require(isWhitelisted(_tokenAddress), Errors.TOKEN_NOT_ACCEPTED); (bytes32 depositId, uint256 volumeReceived) = bounty.receiveFunds{ value: msg.value @@ -104,48 +100,6 @@ contract DepositManagerV1 is DepositManagerStorageV1 { ); } - /// @notice Transfers NFT from msg.sender to bounty address - /// @param _bountyAddress The address of the bounty to fund - /// @param _tokenAddress The ERC721 token address of the NFT - /// @param _tokenId The tokenId of the NFT to transfer - /// @param _expiration The duration until the deposit becomes refundable - /// @param _data The tier of the NFT (not relevant for non-tiered bounties) - function fundBountyNFT( - address _bountyAddress, - address _tokenAddress, - uint256 _tokenId, - uint256 _expiration, - bytes calldata _data - ) external onlyProxy { - IBounty bounty = IBounty(payable(_bountyAddress)); - - require(isWhitelisted(_tokenAddress), Errors.TOKEN_NOT_ACCEPTED); - require(bountyIsOpen(_bountyAddress), Errors.CONTRACT_ALREADY_CLOSED); - - bytes32 depositId = bounty.receiveNft( - msg.sender, - _tokenAddress, - _tokenId, - _expiration, - _data - ); - - emit NFTDepositReceived( - depositId, - _bountyAddress, - bounty.bountyId(), - bounty.organization(), - _tokenAddress, - block.timestamp, - msg.sender, - _expiration, - _tokenId, - 0, - _data, - VERSION_1 - ); - } - /// @notice Refunds an individual deposit from bountyAddress to sender if expiration time has passed /// @param _bountyAddress The address of the bounty that has the deposit to refund /// @param _depositId The depositId associated with the deposit being refunded @@ -201,30 +155,6 @@ contract DepositManagerV1 is DepositManagerStorageV1 { return openQTokenWhitelist.isWhitelisted(_tokenAddress); } - /// @notice Returns true if the total number of unique tokens deposited on then bounty is greater than the OpenQWhitelist TOKEN_ADDRESS_LIMIT - /// @param _bountyAddress Address of bounty - /// @return True if the token address limit has been reached - function tokenAddressLimitReached(address _bountyAddress) - public - view - returns (bool) - { - IBounty bounty = IBounty(payable(_bountyAddress)); - - return - bounty.getTokenAddressesCount() >= - openQTokenWhitelist.TOKEN_ADDRESS_LIMIT(); - } - - /// @notice Checks if bounty associated with _bountyId is open - /// @param _bountyAddress Address of bounty - /// @return bool True if _bountyId is associated with an open bounty - function bountyIsOpen(address _bountyAddress) public view returns (bool) { - IBounty bounty = IBounty(payable(_bountyAddress)); - bool isOpen = bounty.status() == OpenQDefinitions.OPEN; - return isOpen; - } - /// @notice Override for UUPSUpgradeable._authorizeUpgrade(address newImplementation) to enforce onlyOwner upgrades function _authorizeUpgrade(address) internal override onlyOwner {} } diff --git a/contracts/Library/Errors.sol b/contracts/Library/Errors.sol index d558c620..430dd0cc 100755 --- a/contracts/Library/Errors.sol +++ b/contracts/Library/Errors.sol @@ -25,7 +25,6 @@ library Errors { string constant NOT_A_TIERED_BOUNTY = 'NOT_A_TIERED_BOUNTY'; string constant NOT_A_FIXED_TIERED_BOUNTY = 'NOT_A_FIXED_TIERED_BOUNTY'; string constant PREMATURE_REFUND_REQUEST = 'PREMATURE_REFUND_REQUEST'; - string constant NFT_DEPOSIT_LIMIT_REACHED = 'NFT_DEPOSIT_LIMIT_REACHED'; string constant NO_ZERO_ADDRESS = 'NO_ZERO_ADDRESS'; string constant CONTRACT_IS_NOT_CLAIMABLE = 'CONTRACT_IS_NOT_CLAIMABLE'; string constant TOO_MANY_TOKEN_ADDRESSES = 'TOO_MANY_TOKEN_ADDRESSES'; diff --git a/contracts/Library/OpenQDefinitions.sol b/contracts/Library/OpenQDefinitions.sol index aa6dabff..8d918ba9 100755 --- a/contracts/Library/OpenQDefinitions.sol +++ b/contracts/Library/OpenQDefinitions.sol @@ -16,8 +16,6 @@ library OpenQDefinitions { /// @notice Bounty types uint32 internal constant ATOMIC = 0; - uint32 internal constant ONGOING = 1; - uint32 internal constant TIERED_PERCENTAGE = 2; uint32 internal constant TIERED_FIXED = 3; uint32 internal constant OPEN = 0; diff --git a/contracts/Mocks/MockNft.sol b/contracts/Mocks/MockNft.sol deleted file mode 100755 index 004c4a8d..00000000 --- a/contracts/Mocks/MockNft.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.17; - -import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; -import '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol'; -import '@openzeppelin/contracts/access/Ownable.sol'; -import '@openzeppelin/contracts/utils/Counters.sol'; - -contract MockNft is ERC721, ERC721URIStorage, Ownable { - using Counters for Counters.Counter; - - Counters.Counter private _tokenIdCounter; - - constructor() ERC721('MockNft', 'MNFT') {} - - string uri = - 'https://darkhorse.infura-ipfs.io/ipfs/Qmdpxsr7Ynhf6pF6Y5rke4fWNEZc3geWupLiG2uKC3R8RT'; - - function safeMint(address to) external onlyOwner { - uint256 tokenId = _tokenIdCounter.current(); - _tokenIdCounter.increment(); - _safeMint(to, tokenId); - _setTokenURI(tokenId, uri); - } - - // The following functions are overrides required by Solidity. - - function _burn(uint256 tokenId) - internal - override(ERC721, ERC721URIStorage) - { - super._burn(tokenId); - } - - function tokenURI(uint256 tokenId) - public - view - override(ERC721, ERC721URIStorage) - returns (string memory) - { - return super.tokenURI(tokenId); - } -} diff --git a/contracts/OpenQ/Implementations/OpenQV1.sol b/contracts/OpenQ/Implementations/OpenQV1.sol index 98686a06..2aa98936 100755 --- a/contracts/OpenQ/Implementations/OpenQV1.sol +++ b/contracts/OpenQ/Implementations/OpenQV1.sol @@ -249,55 +249,6 @@ contract OpenQV1 is OpenQStorageV1 { ); } - /// @notice Sets payout token address and volume on bounty with id _bountyId - /// @param _bountyId The id to update - /// @param _payoutToken The token address to be used for the payout - /// @param _payoutVolume The volume of token to be used for the payout - function setPayout( - string calldata _bountyId, - address _payoutToken, - uint256 _payoutVolume - ) external onlyProxy { - IBounty bounty = getBounty(_bountyId); - - require(msg.sender == bounty.issuer(), Errors.CALLER_NOT_ISSUER); - - bounty.setPayout(_payoutToken, _payoutVolume); - - emit PayoutSet( - address(bounty), - _payoutToken, - _payoutVolume, - bounty.bountyType(), - new bytes(0), - VERSION_1 - ); - } - - /// @notice Sets payout volume array on percentage tiered bounty with id _bountyId - /// @dev There is no tokenAddress needed here - payouts on percentage tiered bounties is a percentage of whatever is deposited on the contract - /// @param _bountyId The bounty to update - /// @param _payoutSchedule An array of payout volumes for each tier - function setPayoutSchedule( - string calldata _bountyId, - uint256[] calldata _payoutSchedule - ) external onlyProxy { - IBounty bounty = getBounty(_bountyId); - - require(msg.sender == bounty.issuer(), Errors.CALLER_NOT_ISSUER); - - bounty.setPayoutSchedule(_payoutSchedule); - - emit PayoutScheduleSet( - address(bounty), - address(0), - _payoutSchedule, - bounty.bountyType(), - new bytes(0), - VERSION_1 - ); - } - /// @notice Sets payout volume array on fixed tiered bounty with id _bountyId /// @param _bountyId The bounty to update /// @param _payoutSchedule An array of payout volumes for each tier @@ -323,33 +274,6 @@ contract OpenQV1 is OpenQStorageV1 { ); } - /// @notice Closes and ongoing bounty - /// @param _bountyId The ongoing bounty to close - function closeOngoing(string calldata _bountyId) external { - require(bountyIsOpen(_bountyId), Errors.CONTRACT_ALREADY_CLOSED); - require( - bountyType(_bountyId) == OpenQDefinitions.ONGOING, - Errors.NOT_AN_ONGOING_CONTRACT - ); - - IBounty bounty = IBounty(payable(bountyIdToAddress[_bountyId])); - - require(msg.sender == bounty.issuer(), Errors.CALLER_NOT_ISSUER); - - bounty.closeOngoing(msg.sender); - - emit BountyClosed( - _bountyId, - bountyIdToAddress[_bountyId], - bounty.organization(), - address(0), - block.timestamp, - bounty.bountyType(), - new bytes(0), - VERSION_1 - ); - } - /// @notice Checks if bounty associated with _bountyId is open /// @param _bountyId The bounty id /// @return True if _bountyId is associated with an open bounty, false otherwise @@ -402,16 +326,6 @@ contract OpenQV1 is OpenQStorageV1 { return _tierClaimed; } - /// @notice Determines whether or not an ongoing bounty or tiered bounty have enough funds to cover payouts - /// @param _bountyId The bounty id - /// @return True if solvent, false otherwise - function solvent(string calldata _bountyId) external view returns (bool) { - IBounty bounty = getBounty(_bountyId); - - uint256 balance = bounty.getTokenBalance(bounty.payoutTokenAddress()); - return balance >= bounty.payoutVolume(); - } - /// @notice Returns an IBounty ABI wrapped arround given bounty address /// @param _bountyId The bounty id /// @return An IBounty upon which any methods in IBounty can be called @@ -425,22 +339,6 @@ contract OpenQV1 is OpenQStorageV1 { return bounty; } - /// @notice Determines whether or not a given submission by claimant has already been used for a claim - /// @param _bountyId The bounty id - /// @param _claimant The external user id to check - /// @param _claimantAsset The external id of the claimant's asset to check - /// @return True if claimed, false otherwise - function ongoingClaimed( - string calldata _bountyId, - string calldata _claimant, - string calldata _claimantAsset - ) external view returns (bool) { - IBounty bounty = getBounty(_bountyId); - bytes32 claimId = keccak256(abi.encode(_claimant, _claimantAsset)); - bool _ongoingClaimed = bounty.claimId(claimId); - return _ongoingClaimed; - } - /// @notice Override for UUPSUpgradeable._authorizeUpgrade(address newImplementation) to enforce onlyOwner upgrades function _authorizeUpgrade(address) internal override onlyOwner {} diff --git a/contracts/TokenWhitelist/OpenQTokenWhitelist.sol b/contracts/TokenWhitelist/OpenQTokenWhitelist.sol index a153d5c1..15887a36 100755 --- a/contracts/TokenWhitelist/OpenQTokenWhitelist.sol +++ b/contracts/TokenWhitelist/OpenQTokenWhitelist.sol @@ -9,8 +9,5 @@ import './TokenWhitelist.sol'; /// @dev Whitelisting and token address limit is implemented primarily as a means of preventing out-of-gas exceptions when looping over funded addresses for payouts contract OpenQTokenWhitelist is TokenWhitelist { /// @notice Initializes OpenQTokenWhitelist with maximum token address limit to prevent out-of-gas errors - /// @param _tokenAddressLimit Maximum number of token addresses allowed - constructor(uint256 _tokenAddressLimit) TokenWhitelist() { - TOKEN_ADDRESS_LIMIT = _tokenAddressLimit; - } + constructor() TokenWhitelist() {} } diff --git a/contracts/TokenWhitelist/TokenWhitelist.sol b/contracts/TokenWhitelist/TokenWhitelist.sol index eb0a6e9c..38b05b00 100755 --- a/contracts/TokenWhitelist/TokenWhitelist.sol +++ b/contracts/TokenWhitelist/TokenWhitelist.sol @@ -9,7 +9,6 @@ import '../Library/Errors.sol'; /// @notice Base contract for token whitelists /// @dev Whitelisting and token address limit is implemented primarily as a means of preventing out-of-gas exceptions when looping over funded addresses for payouts abstract contract TokenWhitelist is Ownable { - uint256 public TOKEN_ADDRESS_LIMIT; uint256 public tokenCount; mapping(address => bool) public whitelist; @@ -41,13 +40,4 @@ abstract contract TokenWhitelist is Ownable { whitelist[_tokenAddress] = false; tokenCount--; } - - /// @notice Updates the tokenAddressLimit - /// @param _newTokenAddressLimit The new value for TOKEN_ADDRESS_LIMIT - function setTokenAddressLimit(uint256 _newTokenAddressLimit) - external - onlyOwner - { - TOKEN_ADDRESS_LIMIT = _newTokenAddressLimit; - } } diff --git a/deploy/deploy_bounties.js b/deploy/deploy_bounties.js index a2b2a99d..300bb00b 100755 --- a/deploy/deploy_bounties.js +++ b/deploy/deploy_bounties.js @@ -23,21 +23,7 @@ async function deployBounties() { const abiEncodedParamsAtomic = abiCoder.encode(['bool', 'address', 'uint256' , 'bool' , 'bool', 'bool' , 'string', 'string' , 'string'], [true, process.env.MOCK_LINK_TOKEN_ADDRESS, 100, true, true, true, 'po', 'po', 'po']); let atomicBountyInitOperation = [0, abiEncodedParamsAtomic]; - // ONGOING - - const abiEncodedParamsOngoing = abiCoder.encode(['address', 'uint256', 'bool', 'address', 'uint256', 'bool', 'bool', 'bool', 'string', 'string', 'string'], [process.env.MOCK_LINK_TOKEN_ADDRESS, '100', true, process.env.MOCK_LINK_TOKEN_ADDRESS, '1000', true, true, true, '', '', '']); - let ongoingBountyInitOperation = [1, abiEncodedParamsOngoing]; - - const abiEncodedParamsOngoingNoFundingGoal = abiCoder.encode(['address', 'uint256', 'bool', 'address', 'uint256', 'bool', 'bool', 'bool', 'string', 'string', 'string'], [process.env.MOCK_LINK_TOKEN_ADDRESS, '100', false, ethers.constants.AddressZero, 0, true, true, true, '', '', '']); - let ongoingBountyNoFundingGoalInitOperation = [1, abiEncodedParamsOngoingNoFundingGoal]; - // CONTEST - const abiEncodedParamsContestPercentage = abiCoder.encode(['uint256[]', 'bool', 'address', 'uint256', 'bool', 'bool', 'bool', 'string', 'string', 'string'], [[70, 20, 10], true, process.env.MOCK_LINK_TOKEN_ADDRESS, 100, true, true, true, '', '', '']); - let contestPercentageInitOperation = [2, abiEncodedParamsContestPercentage]; - - const abiEncodedParamsContestPercentageNoFundingGoal = abiCoder.encode(['uint256[]', 'bool', 'address', 'uint256', 'bool', 'bool', 'bool', 'string', 'string', 'string'], [[70, 20, 10], false, ethers.constants.AddressZero, 0, true, true, true, '', '', '']); - let contestPercentageNoFundingGoalInitOperation = [2, abiEncodedParamsContestPercentageNoFundingGoal]; - const abiEncodedParamsTieredFixedBounty = abiCoder.encode(['uint256[]', 'address', 'bool', 'bool', 'bool', 'string', 'string', 'string'], [[80, 20], process.env.MOCK_LINK_TOKEN_ADDRESS, true, true, true, '', '', '']); let tieredFixedBountyInitOperation = [3, abiEncodedParamsTieredFixedBounty]; @@ -65,48 +51,7 @@ async function deployBounties() { await optionalSleep(10000); console.log('Atomic Contract with funding goal deployed!'); - // ONGOING - console.log('Minting Ongoing contract with no funding goal...'); - await openQ.mintBounty(openQIssueIds[2], 'MDEyOk9yZ2FuaXphdGlvbjc3NDAyNTM4', ongoingBountyNoFundingGoalInitOperation); - await optionalSleep(10000); - console.log('Ongoing contract with no funding goal deployed!'); - - console.log('Minting Ongoing contract with no funding goal...'); - await openQ.mintBounty(otherOrgIssueIds[2], otherOrgIssueOwners[2], ongoingBountyNoFundingGoalInitOperation); - await optionalSleep(10000); - console.log('Ongoing contract with no funding goal deployed!'); - - console.log('Minting Ongoing contract with funding goal...'); - await openQ.mintBounty(openQIssueIds[3], 'MDEyOk9yZ2FuaXphdGlvbjc3NDAyNTM4', ongoingBountyInitOperation); - await optionalSleep(10000); - console.log('Ongoing contract with funding goal deployed!'); - - console.log('Minting Ongoing contract with funding goal...'); - await openQ.mintBounty(otherOrgIssueIds[3], otherOrgIssueOwners[3], ongoingBountyInitOperation); - await optionalSleep(10000); - console.log('Ongoing contract with funding goal deployed!'); - // CONTEST - console.log('Minting Contest percentage contract with funding goal...'); - await openQ.mintBounty(openQIssueIds[4], 'MDEyOk9yZ2FuaXphdGlvbjc3NDAyNTM4', contestPercentageInitOperation); - await optionalSleep(10000); - console.log('Contest percentage contract with funding goal deployed!'); - - console.log('Minting Contest percentage contract with funding goal...'); - await openQ.mintBounty(otherOrgIssueIds[4], otherOrgIssueOwners[4], contestPercentageInitOperation); - await optionalSleep(10000); - console.log('Contest percentage contract with funding goal deployed!'); - - console.log('Minting Contest percentage contract with no funding goal...'); - await openQ.mintBounty(openQIssueIds[5], 'MDEyOk9yZ2FuaXphdGlvbjc3NDAyNTM4', contestPercentageNoFundingGoalInitOperation); - await optionalSleep(10000); - console.log('Contest percentage contract with no funding goal deployed!'); - - console.log('Minting Contest percentage contract with no funding goal...'); - await openQ.mintBounty(otherOrgIssueIds[5], otherOrgIssueOwners[5], contestPercentageNoFundingGoalInitOperation); - await optionalSleep(10000); - console.log('Contest percentage contract with no funding goal deployed!'); - console.log('Minting Contest fixed contract...'); await openQ.mintBounty(openQIssueIds[6], 'MDEyOk9yZ2FuaXphdGlvbjc3NDAyNTM4', tieredFixedBountyInitOperation); await optionalSleep(10000); diff --git a/deploy/deploy_contracts.js b/deploy/deploy_contracts.js index 8638909a..4aef6ce6 100755 --- a/deploy/deploy_contracts.js +++ b/deploy/deploy_contracts.js @@ -10,7 +10,6 @@ async function deployContracts() { let mockLink; let mockDai; - let mockNFT; let mockDaiBlacklisted; if (network.name === 'docker' || network.name === 'localhost') { console.log('Deploying MockLink...'); @@ -20,13 +19,6 @@ async function deployContracts() { await optionalSleep(10000); console.log(`MockLink Deployed to ${mockLink.address}\n`); - console.log('Deploying MockNFT...'); - const MockNFT = await ethers.getContractFactory('MockNft'); - mockNFT = await MockNFT.deploy(); - await mockNFT.deployed(); - await optionalSleep(10000); - console.log(`MockNFT Deployed to ${mockNFT.address}\n`); - console.log('Deploying MockDai...'); const MockDai = await ethers.getContractFactory('MockDai'); mockDai = await MockDai.deploy(); @@ -117,7 +109,7 @@ async function deployContracts() { console.log('Deploying OpenQTokenWhitelist...'); const OpenQTokenWhitelist = await ethers.getContractFactory('OpenQTokenWhitelist'); - const openQTokenWhitelist = await OpenQTokenWhitelist.deploy(5); + const openQTokenWhitelist = await OpenQTokenWhitelist.deploy(); await openQTokenWhitelist.deployed(); await optionalSleep(10000); console.log(`OpenQTokenWhitelist Deployed to ${openQTokenWhitelist.address}\n`); @@ -135,20 +127,6 @@ async function deployContracts() { await optionalSleep(10000); console.log(`AtomicBountyV1 Deployed to ${atomicBountyV1.address}\n`); - console.log('Deploying OngoingBountyV1 implementation...'); - const OngoingBountyV1 = await ethers.getContractFactory('OngoingBountyV1'); - const ongoingBountyV1 = await OngoingBountyV1.deploy(); - await ongoingBountyV1.deployed(); - await optionalSleep(10000); - console.log(`OngoingBountyV1 Deployed to ${ongoingBountyV1.address}\n`); - - console.log('Deploying TieredPercentageBountyV1 implementation...'); - const TieredPercentageBountyV1 = await ethers.getContractFactory('TieredPercentageBountyV1'); - const tieredPercentageBountyV1 = await TieredPercentageBountyV1.deploy(); - await tieredPercentageBountyV1.deployed(); - await optionalSleep(10000); - console.log(`TieredPercentageBountyV1 Deployed to ${tieredPercentageBountyV1.address}\n`); - console.log('Deploying TieredFixedBountyV1 implementation...'); const TieredFixedBountyV1 = await ethers.getContractFactory('TieredFixedBountyV1'); const tieredFixedBountyV1 = await TieredFixedBountyV1.deploy(); @@ -165,18 +143,6 @@ async function deployContracts() { await optionalSleep(10000); console.log(`AtomicBountyBeacon Deployed to ${atomicBountyBeacon.address}\n`); - console.log('Deploying OngoingBountyBeacon...'); - const ongoingBountyBeacon = await BountyBeacon.deploy(ongoingBountyV1.address); - await ongoingBountyBeacon.deployed(); - await optionalSleep(10000); - console.log(`OngoingBountyBeacon Deployed to ${ongoingBountyBeacon.address}\n`); - - console.log('Deploying TieredBountyBeacon...'); - const tieredBountyBeacon = await BountyBeacon.deploy(tieredPercentageBountyV1.address); - await tieredBountyBeacon.deployed(); - await optionalSleep(10000); - console.log(`TieredBountyBeacon Deployed to ${tieredBountyBeacon.address}\n`); - console.log('Deploying TieredFixedBountyBeacon...'); const tieredFixedBountyBeacon = await BountyBeacon.deploy(tieredFixedBountyV1.address); await tieredFixedBountyBeacon.deployed(); @@ -188,8 +154,6 @@ async function deployContracts() { const bountyFactory = await BountyFactory.deploy( openQProxy.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredBountyBeacon.address, tieredFixedBountyBeacon.address ); await bountyFactory.deployed(); @@ -202,13 +166,9 @@ async function deployContracts() { console.log('\nBOUNTY PROXY and IMPLEMENTATION ADDRESSES'); console.log(`AtomicBountyV1 (Implementation) deployed to ${atomicBountyV1.address}\n`); - console.log(`OngoingBountyV1 (Implementation) deployed to ${ongoingBountyV1.address}\n`); - console.log(`TieredPercentageBountyV1 (Implementation) deployed to ${tieredPercentageBountyV1.address}\n`); console.log(`TieredFixedBountyV1 (Implementation) deployed to ${tieredFixedBountyV1.address}\n`); console.log(`AtomicBountyBeacon deployed to ${atomicBountyBeacon.address}`); - console.log(`OngoingBountyBeacon deployed to ${ongoingBountyBeacon.address}`); - console.log(`TieredBountyBeacon deployed to ${tieredBountyBeacon.address}`); console.log(`TieredFixedBountyBeacon deployed to ${tieredFixedBountyBeacon.address}`); console.log(`BountyFactory deployed to: ${bountyFactory.address}`); @@ -216,7 +176,6 @@ async function deployContracts() { if (network.name === 'docker' || network.name === 'localhost') { console.log(`MockLink deployed to: ${mockLink.address}`); console.log(`MockDai deployed to: ${mockDai.address}`); - console.log(`MockNFT deployed to: ${mockNFT.address}`); console.log(`MockDai (BlackListed) deployed to: ${mockDaiBlacklisted.address}`); } @@ -256,14 +215,11 @@ DEPOSIT_MANAGER_PROXY_ADDRESS=${depositManagerProxy.address} DEPOSIT_MANAGER_IMPLEMENTATION_ADDRESS=${depositManager.address} OPENQ_BOUNTY_FACTORY_ADDRESS=${bountyFactory.address} ATOMIC_BOUNTY_BEACON_ADDRESS=${atomicBountyBeacon.address} -ONGOING_BOUNTY_BEACON_ADDRESS=${ongoingBountyBeacon.address} -TIERED_BOUNTY_BEACON_ADDRESS=${tieredBountyBeacon.address} TIERED_FIXED_BOUNTY_BEACON_ADDRESS=${tieredFixedBountyBeacon.address} OPENQ_TOKEN_WHITELIST_ADDRESS=${openQTokenWhitelist.address} OPENQ_DEPLOY_BLOCK_NUMBER=${deployBlockNumber} MOCK_LINK_TOKEN_ADDRESS=${mockLink.address} MOCK_DAI_TOKEN_ADDRESS=${mockDai.address} -MOCK_NFT_TOKEN_ADDRESS=${mockNFT.address} MOCK_DAI_BLACKLISTED_TOKEN_ADDRESS=${mockDaiBlacklisted.address} `; } else { @@ -278,7 +234,6 @@ OPENQ_TOKEN_WHITELIST_ADDRESS=${openQTokenWhitelist.address} OPENQ_DEPLOY_BLOCK_NUMBER=${deployBlockNumber} MOCK_LINK_TOKEN_ADDRESS=0x326C977E6efc84E512bB9C30f76E30c160eD06FB MOCK_DAI_TOKEN_ADDRESS=0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1 -MOCK_NFT_TOKEN_ADDRESS=0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1 `; } diff --git a/deploy/fund_bounties.js b/deploy/fund_bounties.js index cfe26776..d6549054 100755 --- a/deploy/fund_bounties.js +++ b/deploy/fund_bounties.js @@ -16,9 +16,6 @@ async function fundBounties() { const MockDai = await ethers.getContractFactory('MockDai'); const mockDai = await MockDai.attach(process.env.MOCK_DAI_TOKEN_ADDRESS); - const MockNFT = await ethers.getContractFactory('MockNft'); - const mockNFT = await MockNFT.attach(process.env.MOCK_NFT_TOKEN_ADDRESS); - const OpenQ = await ethers.getContractFactory('OpenQV1'); const openQ = await OpenQ.attach(process.env.OPENQ_PROXY_ADDRESS); @@ -116,12 +113,6 @@ async function fundBounties() { await mockDai.transfer(contributor.address, two); await mockLink.transfer(contributor.address, two); console.log('Transfer to Client 2 succeeded'); - await mockNFT.safeMint(contributor.address); - await mockNFT.safeMint(contributor.address); - await mockNFT.safeMint(contributor.address); - await mockNFT.safeMint(contributor.address); - await mockNFT.safeMint(contributor.address); - await mockNFT.safeMint(contributor.address); console.log('Approving funds for Client 2...'); await mockLink.connect(contributor).approve(openQBounty1Address, one); diff --git a/test/Bounty/AtomicBounty.test.js b/test/Bounty/AtomicBounty.test.js index 13e80b34..9d1e3074 100755 --- a/test/Bounty/AtomicBounty.test.js +++ b/test/Bounty/AtomicBounty.test.js @@ -20,7 +20,6 @@ describe('AtomicBountyV1.sol', () => { // MOCK ASSETS let mockLink; let mockDai; - let mockNft; // UTILS let abiCoder = new ethers.utils.AbiCoder; @@ -43,7 +42,6 @@ describe('AtomicBountyV1.sol', () => { AtomicBountyV1 = await ethers.getContractFactory('AtomicBountyV1'); const MockLink = await ethers.getContractFactory('MockLink'); const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); [owner, claimManager, depositManager] = await ethers.getSigners(); @@ -54,16 +52,6 @@ describe('AtomicBountyV1.sol', () => { mockDai = await MockDai.deploy(); await mockDai.deployed(); - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - // ATOMIC CONTRACT W/ FUNDING GOAL atomicContract = await AtomicBountyV1.deploy(); await atomicContract.deployed(); @@ -72,13 +60,6 @@ describe('AtomicBountyV1.sol', () => { initializationTimestamp = await setNextBlockTimestamp(); await atomicContract.initialize(Constants.bountyId, owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, atomicBountyInitOperation); - await mockNft.approve(atomicContract.address, 0); - await mockNft.approve(atomicContract.address, 1); - await mockNft.approve(atomicContract.address, 2); - await mockNft.approve(atomicContract.address, 3); - await mockNft.approve(atomicContract.address, 4); - await mockNft.approve(atomicContract.address, 5); - // Pre-approve LINK and DAI for transfers during testing await mockLink.approve(atomicContract.address, 10000000); await mockDai.approve(atomicContract.address, 10000000); @@ -134,67 +115,6 @@ describe('AtomicBountyV1.sol', () => { }); }); - describe('receiveNFT', () => { - - describe('REVERTS', () => { - it('should revert if too many NFT deposits', async () => { - // ASSUME - expect(await mockNft.ownerOf(0)).to.equal(owner.address); - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - expect(await mockNft.ownerOf(2)).to.equal(owner.address); - expect(await mockNft.ownerOf(3)).to.equal(owner.address); - expect(await mockNft.ownerOf(4)).to.equal(owner.address); - - // ACT - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 1, []); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, 1, []); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 2, 1, []); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 3, 1, []); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 4, 1, []); - - // ASSERT - await expect(atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 5, 1, [])).to.be.revertedWith('NFT_DEPOSIT_LIMIT_REACHED'); - }); - - it('should revert if expiration is negative', async () => { - await expect(atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 0, [])).to.be.revertedWith('EXPIRATION_NOT_GREATER_THAN_ZERO'); - }); - }); - - describe('DEPOSIT INITIALIZATION', () => { - it(`should initialize nft deposit data with correct metadata`, async () => { - - // ACT - const expectedTimestamp = await setNextBlockTimestamp(); - const depositId = generateDepositId(Constants.bountyId, 0); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, Constants.thirtyDays, []); - - // ASSERT - expect(await atomicContract.funder(depositId)).to.equal(owner.address); - expect(await atomicContract.tokenAddress(depositId)).to.equal(mockNft.address); - expect(await atomicContract.tokenId(depositId)).to.equal(1); - expect(await atomicContract.expiration(depositId)).to.equal(Constants.thirtyDays); - expect(await atomicContract.isNFT(depositId)).to.equal(true); - - const depositTime = await atomicContract.depositTime(depositId); - expect(depositTime.toString()).to.equal(expectedTimestamp.toString()); - }); - }); - - describe('transfer', () => { - it('should transfer NFT from owner to bounty contract', async () => { - // ASSUME - expect(await mockNft.ownerOf(0)).to.equal(owner.address); - - // ACT - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 1, []); - - // ASSERT - expect(await mockNft.ownerOf(0)).to.equal(atomicContract.address); - }); - }); - }); - describe('claimBalance', () => { it('should transfer protocol token from contract to payout address and set token balance to zero', async () => { // ARRANGE @@ -271,40 +191,6 @@ describe('AtomicBountyV1.sol', () => { }); }); - describe('claimNft', () => { - describe('require and revert', () => { - it('should revert if not called by Claim Manager contract', async () => { - // ARRANGE - const [, , , , , notClaimManager] = await ethers.getSigners(); - const value = 10000; - let issueWithNonOwnerAccount = atomicContract.connect(notClaimManager); - - // ASSERT - await expect(issueWithNonOwnerAccount.claimNft(notClaimManager.address, ethers.utils.formatBytes32String('mockDepositId'))).to.be.revertedWith('ClaimManagerOwnable: caller is not the current OpenQ Claim Manager'); - }); - }); - - describe('transfer', () => { - it('should transfer NFT deposit from bounty contract to claimer', async () => { - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - - // ARRANGE - const depositId = generateDepositId(Constants.bountyId, 0); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, 1, []); - - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(atomicContract.address); - - // ACT - await atomicContract.connect(claimManager).claimNft(owner.address, depositId); - - // ASSERT - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - }); - }); - }); - describe('close', () => { it('should revert if not called by ClaimManager contract', async () => { // ARRANGE diff --git a/test/Bounty/BountyCore.test.js b/test/Bounty/BountyCore.test.js index 821351e9..ee31e773 100755 --- a/test/Bounty/BountyCore.test.js +++ b/test/Bounty/BountyCore.test.js @@ -26,7 +26,6 @@ describe('BountyCore.sol', () => { // MOCK ASSETS let mockLink; let mockDai; - let mockNft; // UTILS let abiCoder = new ethers.utils.AbiCoder; @@ -49,7 +48,6 @@ describe('BountyCore.sol', () => { AtomicBountyV1 = await ethers.getContractFactory('AtomicBountyV1'); const MockLink = await ethers.getContractFactory('MockLink'); const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); [owner, claimManager, depositManager] = await ethers.getSigners(); @@ -60,16 +58,6 @@ describe('BountyCore.sol', () => { mockDai = await MockDai.deploy(); await mockDai.deployed(); - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - // ATOMIC CONTRACT W/ FUNDING GOAL atomicContract = await AtomicBountyV1.deploy(); await atomicContract.deployed(); @@ -78,13 +66,6 @@ describe('BountyCore.sol', () => { initializationTimestamp = await setNextBlockTimestamp(); await atomicContract.initialize(Constants.bountyId, owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, atomicBountyInitOperation); - await mockNft.approve(atomicContract.address, 0); - await mockNft.approve(atomicContract.address, 1); - await mockNft.approve(atomicContract.address, 2); - await mockNft.approve(atomicContract.address, 3); - await mockNft.approve(atomicContract.address, 4); - await mockNft.approve(atomicContract.address, 5); - // Pre-approve LINK and DAI for transfers during testing await mockLink.approve(atomicContract.address, 10000000); await mockDai.approve(atomicContract.address, 10000000); @@ -145,7 +126,6 @@ describe('BountyCore.sol', () => { expect(await atomicContract.tokenAddress(depositId)).to.equal(mockLink.address); expect(await atomicContract.volume(depositId)).to.equal(Constants.volume); expect(await atomicContract.expiration(depositId)).to.equal(Constants.thirtyDays); - expect(await atomicContract.isNFT(depositId)).to.equal(false); expect(await atomicContract.depositTime(depositId)).to.equal(expectedTimestamp); }); @@ -351,24 +331,6 @@ describe('BountyCore.sol', () => { expect(newFunderMockLinkBalance).to.equal('10000000000000000000000'); expect(newFunderFakeTokenBalance).to.equal('10000000000000000000000'); }); - - it('should transfer NFT from bounty to sender', async () => { - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - - // ARRANGE - const depositId = generateDepositId(Constants.bountyId, 0); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, 1, []); - - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(atomicContract.address); - - // ACT - await atomicContract.connect(depositManager).refundDeposit(depositId, owner.address, 0); - - // ASSERT - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - }); }); }); @@ -390,39 +352,6 @@ describe('BountyCore.sol', () => { }); }); - describe('claimNft', () => { - describe('require and revert', () => { - it('should revert if not called by Claim Manager contract', async () => { - // ARRANGE - const [, , , , , notClaimManager] = await ethers.getSigners(); - let issueWithNonOwnerAccount = atomicContract.connect(notClaimManager); - - // ASSERT - await expect(issueWithNonOwnerAccount.claimNft(notClaimManager.address, ethers.utils.formatBytes32String('mockDepositId'))).to.be.revertedWith('ClaimManagerOwnable: caller is not the current OpenQ Claim Manager'); - }); - }); - - describe('transfer', () => { - it('should transfer NFT deposit from bounty contract to claimer', async () => { - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - - // ARRANGE - const depositId = generateDepositId(Constants.bountyId, 0); - await atomicContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, 1, []); - - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(atomicContract.address); - - // ACT - await atomicContract.connect(claimManager).claimNft(owner.address, depositId); - - // ASSERT - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - }); - }); - }); - describe('setFundingGoal', () => { it('should revert if not called by OpenQ contract', async () => { // ARRANGE diff --git a/test/Bounty/OngoingBounty.test.js b/test/Bounty/OngoingBounty.test.js deleted file mode 100755 index 93b324aa..00000000 --- a/test/Bounty/OngoingBounty.test.js +++ /dev/null @@ -1,392 +0,0 @@ -/* eslint-disable */ -const { BigNumber } = require('@ethersproject/bignumber'); -const { expect } = require('chai'); -const { ethers } = require("hardhat"); -const truffleAssert = require('truffle-assertions'); -require('@nomiclabs/hardhat-waffle'); - -const { generateDepositId, generateClaimantId } = require('../utils'); - -const { - Constants, - ongoingBountyInitOperationBuilder, - ongoingBountyInitOperationBuilder_noFundingGoal -} = require('../constants'); - -describe('OngoingBountyV1.sol', () => { - // CONTRACT FACTORIES - let OngoingBountyV1; - - // ACCOUNTS - let owner; - let claimManager; - let depositManager; - - // MOCK ASSETS - let mockLink; - let mockDai; - let mockNft; - - // UTILS - let abiCoder = new ethers.utils.AbiCoder; - - // CONSTANTS - let closerData = abiCoder.encode(['address', 'string', 'address', 'string'], [ethers.constants.AddressZero, "FlacoJones", ethers.constants.AddressZero, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398"]); - - - // INITIALIZATION OPERATIONS - let ongoingContractInitOperation; - - // TEST CONTRACTS - let ongoingContract; - let ongoingContract_noFundingGoal; - - // MISC - let initializationTimestampAtomic; - let initializationTimestampOngoingNoFundingGoal; - - beforeEach(async () => { - OngoingBountyV1 = await ethers.getContractFactory('OngoingBountyV1'); - const MockLink = await ethers.getContractFactory('MockLink'); - const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); - - [owner, claimManager, depositManager] = await ethers.getSigners(); - - // MOCK ASSETS - mockLink = await MockLink.deploy(); - await mockLink.deployed(); - - mockDai = await MockDai.deploy(); - await mockDai.deployed(); - - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - - // ONGOIN CONTRACT - ongoingContract = await OngoingBountyV1.deploy(); - await ongoingContract.deployed(); - - ongoingContractInitOperation = ongoingBountyInitOperationBuilder(mockLink.address) - initializationTimestamp = await setNextBlockTimestamp(); - await ongoingContract.initialize(Constants.bountyId, owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, ongoingContractInitOperation); - - await mockNft.approve(ongoingContract.address, 0); - await mockNft.approve(ongoingContract.address, 1); - await mockNft.approve(ongoingContract.address, 2); - await mockNft.approve(ongoingContract.address, 3); - await mockNft.approve(ongoingContract.address, 4); - await mockNft.approve(ongoingContract.address, 5); - - // Pre-approve LINK and DAI for transfers during testing - await mockLink.approve(ongoingContract.address, 10000000); - await mockDai.approve(ongoingContract.address, 10000000); - - // ATOMIC CONTRACT W/ NO FUNDING GOAL - ongoingContract_noFundingGoal = await OngoingBountyV1.deploy(); - await ongoingContract_noFundingGoal.deployed(); - - const ongoingBountyNoFundingGoalInitOperation = ongoingBountyInitOperationBuilder_noFundingGoal(mockLink.address) - initializationTimestampOngoingNoFundingGoal = await setNextBlockTimestamp(); - await ongoingContract_noFundingGoal.initialize(Constants.bountyId, owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, ongoingBountyNoFundingGoalInitOperation); - }); - - describe('initializer', () => { - it(`should initialize bounty with correct metadata`, async () => { - // ARRANGE/ASSERT - await expect(await ongoingContract.bountyId()).equals(Constants.bountyId); - await expect(await ongoingContract.issuer()).equals(owner.address); - await expect(await ongoingContract.organization()).equals(Constants.organization); - await expect(await ongoingContract.status()).equals(0); - await expect(await ongoingContract.openQ()).equals(owner.address); - await expect(await ongoingContract.claimManager()).equals(claimManager.address); - await expect(await ongoingContract.depositManager()).equals(depositManager.address); - await expect(await ongoingContract.bountyCreatedTime()).equals(initializationTimestamp); - await expect(await ongoingContract.bountyType()).equals(Constants.ONGOING_CONTRACT); - await expect(await ongoingContract.hasFundingGoal()).equals(true); - await expect(await ongoingContract.fundingToken()).equals(mockLink.address); - await expect(await ongoingContract.fundingGoal()).equals(100); - await expect(await ongoingContract.issuerExternalUserId()).equals(Constants.mockOpenQId); - - await expect(await ongoingContract.invoiceRequired()).equals(false); - await expect(await ongoingContract.kycRequired()).equals(false); - await expect(await ongoingContract.supportingDocumentsRequired()).equals(false); - }); - - it('should revert if bountyId is empty', async () => { - // ARRANGE - const OngoingBountyV1 = await ethers.getContractFactory('OngoingBountyV1'); - ongoingContract = await OngoingBountyV1.deploy(); - - // ASSERT - await expect(ongoingContract.initialize("", owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, ongoingContractInitOperation)).to.be.revertedWith('NO_EMPTY_BOUNTY_ID'); - }); - - it('should revert if organization is empty', async () => { - // ARRANGE - const OngoingBountyV1 = await ethers.getContractFactory('OngoingBountyV1'); - ongoingContract = await OngoingBountyV1.deploy(); - - // ASSERT - await expect(ongoingContract.initialize(Constants.bountyId, owner.address, "", owner.address, claimManager.address, depositManager.address, ongoingContractInitOperation)).to.be.revertedWith('NO_EMPTY_ORGANIZATION'); - }); - }); - - describe('receiveNFT', () => { - - describe('REVERTS', () => { - it('should revert if too many NFT deposits', async () => { - // ASSUME - expect(await mockNft.ownerOf(0)).to.equal(owner.address); - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - expect(await mockNft.ownerOf(2)).to.equal(owner.address); - expect(await mockNft.ownerOf(3)).to.equal(owner.address); - expect(await mockNft.ownerOf(4)).to.equal(owner.address); - - // ACT - await ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 1, []); - await ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, 1, []); - await ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 2, 1, []); - await ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 3, 1, []); - await ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 4, 1, []); - - // ASSERT - await expect(ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 5, 1, [])).to.be.revertedWith('NFT_DEPOSIT_LIMIT_REACHED'); - }); - - it('should revert if expiration is negative', async () => { - await expect(ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 0, [])).to.be.revertedWith('EXPIRATION_NOT_GREATER_THAN_ZERO'); - }); - }); - - describe('DEPOSIT INITIALIZATION', () => { - it(`should initialize nft deposit data with correct metadata`, async () => { - - // ACT - const expectedTimestamp = await setNextBlockTimestamp(); - const depositId = generateDepositId(Constants.bountyId, 0); - await ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, Constants.thirtyDays, []); - - // ASSERT - expect(await ongoingContract.funder(depositId)).to.equal(owner.address); - expect(await ongoingContract.tokenAddress(depositId)).to.equal(mockNft.address); - expect(await ongoingContract.tokenId(depositId)).to.equal(1); - expect(await ongoingContract.expiration(depositId)).to.equal(Constants.thirtyDays); - expect(await ongoingContract.isNFT(depositId)).to.equal(true); - - const depositTime = await ongoingContract.depositTime(depositId); - expect(depositTime.toString()).to.equal(expectedTimestamp.toString()); - }); - }); - - describe('transfer', () => { - it('should transfer NFT from owner to bounty contract', async () => { - // ASSUME - expect(await mockNft.ownerOf(0)).to.equal(owner.address); - - // ACT - await ongoingContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 1, []); - - // ASSERT - expect(await mockNft.ownerOf(0)).to.equal(ongoingContract.address); - }); - }); - }); - - describe('claimOngoingPayout', () => { - it('should transfer payoutVolume of payoutTokenAddress to claimant', async () => { - // ARRANGE - const volume = 300; - - const [, claimer] = await ethers.getSigners(); - - await ongoingContract.connect(depositManager).receiveFunds(owner.address, mockLink.address, volume, Constants.thirtyDays); - - const deposits = await ongoingContract.getDeposits(); - const linkDepositId = deposits[0]; - - // ASSUME - const bountyMockTokenBalance = (await mockLink.balanceOf(ongoingContract.address)).toString(); - expect(bountyMockTokenBalance).to.equal('300'); - - const claimerMockTokenBalance = (await mockLink.balanceOf(claimer.address)).toString(); - expect(claimerMockTokenBalance).to.equal('0'); - - // ACT - await ongoingContract.connect(claimManager).claimOngoingPayout(claimer.address, closerData); - - // ASSERT - const newClaimerMockTokenBalance = (await mockLink.balanceOf(claimer.address)).toString(); - expect(newClaimerMockTokenBalance).to.equal('100'); - - const newBountyMockLinkBalance = (await mockLink.balanceOf(ongoingContract.address)).toString(); - expect(newBountyMockLinkBalance).to.equal('200'); - - // ACT - await ongoingContract.connect(claimManager).claimOngoingPayout(claimer.address, closerData); - - // ASSERT - const newClaimerMockTokenBalance2 = (await mockLink.balanceOf(claimer.address)).toString(); - expect(newClaimerMockTokenBalance2).to.equal('200'); - - const newBountyMockLinkBalance2 = (await mockLink.balanceOf(ongoingContract.address)).toString(); - expect(newBountyMockLinkBalance2).to.equal('100'); - }); - - it('should set claimId to true for the claimant and claimant asset', async () => { - // ARRANGE - let claimId = generateClaimantId('FlacoJones', "https://github.com/OpenQDev/OpenQ-Frontend/pull/398"); - await ongoingContract.connect(depositManager).receiveFunds(owner.address, mockLink.address, 10000000, Constants.thirtyDays); - - - // ASSUME - let claimantIdClaimed = await ongoingContract.claimId(claimId); - expect(claimantIdClaimed).to.equal(false); - - // ACT - await ongoingContract.connect(claimManager).claimOngoingPayout(owner.address, closerData); - - // ASSERT - claimantIdClaimed = await ongoingContract.claimId(claimId); - expect(claimantIdClaimed).to.equal(true); - }); - - it('should revert if not called by claim manager', async () => { - // ACT/ASSERT - await expect(ongoingContract.claimOngoingPayout(owner.address, closerData)).to.be.revertedWith('ClaimManagerOwnable: caller is not the current OpenQ Claim Manager'); - }); - }); - - describe('setPayout', () => { - it('should revert if not called by OpenQ contract', async () => { - // ARRANGE - const [, notOwner] = await ethers.getSigners(); - - // ASSERT - await expect(ongoingContract.connect(notOwner).setPayout(mockLink.address, 100)).to.be.revertedWith('Method is only callable by OpenQ'); - }); - - it('should set payoutTokenAddress and payoutTokenVolume', async () => { - // ASSUME - expect(await ongoingContract.payoutTokenAddress()).to.equal(mockLink.address) - expect(await ongoingContract.payoutVolume()).to.equal(100) - - // ACT - await ongoingContract.setPayout(mockDai.address, 250); - - // ASSERT - expect(await ongoingContract.payoutTokenAddress()).to.equal(mockDai.address) - expect(await ongoingContract.payoutVolume()).to.equal(250) - }) - }) - - describe('closeOngoing', () => { - it('should revert if not called by OpenQ contract', async () => { - // ARRANGE - const [, , , , , notOpenQ] = await ethers.getSigners(); - - // ASSERT - await expect(ongoingContract.connect(notOpenQ).closeOngoing(owner.address)).to.be.revertedWith('Method is only callable by OpenQ'); - }); - - it('should revert if already closed', async () => { - // ARRANGE - ongoingContract.connect(owner).closeOngoing(owner.address); - //ACT / ASSERT - await expect(ongoingContract.connect(owner).closeOngoing(owner.address)).to.be.revertedWith('CONTRACT_ALREADY_CLOSED'); - }); - - it('should change status to CLOSED (1)', async () => { - // ASSUME - await expect(await ongoingContract.status()).equals(0); - //ACT - await ongoingContract.connect(owner).closeOngoing(owner.address); - // ASSERT - await expect(await ongoingContract.status()).equals(1); - }); - - it('should set bountyClosedTime to the block timestamp', async () => { - // ARRANGE - const expectedTimestamp = await setNextBlockTimestamp(); - // ASSUME - await expect(await ongoingContract.bountyClosedTime()).equals(0); - //ACT - await ongoingContract.connect(owner).closeOngoing(owner.address); - // ASSERT - await expect(await ongoingContract.bountyClosedTime()).equals(expectedTimestamp); - }); - }); - - describe('setInvoiceComplete', () => { - it('should revert if not called by OpenQ contract', async () => { - // ARRANGE - const [, notOwner] = await ethers.getSigners(); - - const claimId = generateClaimantId(Constants.mockOpenQId, Constants.mockClaimantAsset) - let setInvoiceCompleteData = abiCoder.encode(["bytes32", "bool"], [claimId, true]); - - // ASSERT - await expect(ongoingContract.connect(notOwner).setInvoiceComplete(setInvoiceCompleteData)).to.be.revertedWith('Method is only callable by OpenQ'); - }); - - it('should set invoiceComplete for given claimId', async () => { - const claimId = generateClaimantId(Constants.mockOpenQId, Constants.mockClaimantAsset) - let setInvoiceCompleteData = abiCoder.encode(["bytes32", "bool"], [claimId, true]); - - // ASSUME - expect(await ongoingContract.invoiceComplete(claimId)).to.equal(false) - - // ACT - await ongoingContract.setInvoiceComplete(setInvoiceCompleteData); - - // ASSERT - expect(await ongoingContract.invoiceComplete(claimId)).to.equal(true) - }) - }) - - describe('setSupportingDocumentsComplete', () => { - it('should revert if not called by OpenQ contract', async () => { - // ARRANGE - const [, notOwner] = await ethers.getSigners(); - - const claimId = generateClaimantId(Constants.mockOpenQId, Constants.mockClaimantAsset) - let setSupportingDocumentsCompleteData = abiCoder.encode(["bytes32", "bool"], [claimId, true]); - - // ASSERT - await expect(ongoingContract.connect(notOwner).setSupportingDocumentsComplete(setSupportingDocumentsCompleteData)).to.be.revertedWith('Method is only callable by OpenQ'); - }); - - it('should set supportingDocumentsComplete for given claimId', async () => { - const claimId = generateClaimantId(Constants.mockOpenQId, Constants.mockClaimantAsset) - let setSupportingDocumentsCompleteData = abiCoder.encode(["bytes32", "bool"], [claimId, true]); - - // ASSUME - expect(await ongoingContract.supportingDocumentsComplete(claimId)).to.equal(false) - - // ACT - await ongoingContract.setSupportingDocumentsComplete(setSupportingDocumentsCompleteData); - - // ASSERT - expect(await ongoingContract.supportingDocumentsComplete(claimId)).to.equal(true) - }) - }) - -}); - -async function setNextBlockTimestamp() { - return new Promise(async (resolve,) => { - const blockNumBefore = await ethers.provider.getBlockNumber(); - const blockBefore = await ethers.provider.getBlock(blockNumBefore); - const timestampBefore = blockBefore.timestamp; - const expectedTimestamp = timestampBefore + 10; - await network.provider.send("evm_setNextBlockTimestamp", [expectedTimestamp]); - resolve(expectedTimestamp); - }); -} \ No newline at end of file diff --git a/test/Bounty/TieredBountyCore.test.js b/test/Bounty/TieredBountyCore.test.js index 782b9fc5..2457a852 100755 --- a/test/Bounty/TieredBountyCore.test.js +++ b/test/Bounty/TieredBountyCore.test.js @@ -9,14 +9,11 @@ const { generateDepositId, generateClaimantId } = require('../utils'); const { Constants, - tieredBountyInitOperationBuilder, tieredFixedBountyInitOperationBuilder, - tieredBountyInitOperation_not100, setInvoiceCompleteData_tiered, setSupportingDocumentsComplete_tiered, setInvoiceCompleteData_atomic, setSupportingDocumentsComplete_atomic, - tieredBountyInitOperationBuilder_permissionless, tieredFixedBountyInitOperationBuilder_permissionless } = require('../constants'); @@ -32,7 +29,6 @@ describe('TieredBountyCore.sol', () => { // MOCK ASSETS let mockLink; let mockDai; - let mockNft; // UTILS let abiCoder = new ethers.utils.AbiCoder; @@ -54,7 +50,6 @@ describe('TieredBountyCore.sol', () => { TieredFixedBountyV1 = await ethers.getContractFactory('TieredFixedBountyV1'); const MockLink = await ethers.getContractFactory('MockLink'); const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); [owner, claimManager, depositManager] = await ethers.getSigners(); @@ -65,16 +60,6 @@ describe('TieredBountyCore.sol', () => { mockDai = await MockDai.deploy(); await mockDai.deployed(); - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - // TIERED BOUNTY tieredFixedContract = await TieredFixedBountyV1.deploy(); await tieredFixedContract.deployed(); @@ -87,75 +72,6 @@ describe('TieredBountyCore.sol', () => { // Pre-approve LINK and DAI for transfers during testing await mockLink.approve(tieredFixedContract.address, 10000000); await mockDai.approve(tieredFixedContract.address, 10000000); - - await mockNft.approve(tieredFixedContract.address, 0); - await mockNft.approve(tieredFixedContract.address, 1); - await mockNft.approve(tieredFixedContract.address, 2); - await mockNft.approve(tieredFixedContract.address, 3); - await mockNft.approve(tieredFixedContract.address, 4); - }); - - describe('receiveNFT', () => { - let tierData = abiCoder.encode(['uint256'], ['0']); - - describe('REVERTS', () => { - it('should revert if too many NFT deposits', async () => { - // ASSUME - expect(await mockNft.ownerOf(0)).to.equal(owner.address); - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - expect(await mockNft.ownerOf(2)).to.equal(owner.address); - expect(await mockNft.ownerOf(3)).to.equal(owner.address); - expect(await mockNft.ownerOf(4)).to.equal(owner.address); - - // ACT - await tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 1, tierData); - await tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, 1, tierData); - await tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 2, 1, tierData); - await tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 3, 1, tierData); - await tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 4, 1, tierData); - - // ASSERT - await expect(tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 5, 1, tierData)).to.be.revertedWith('NFT_DEPOSIT_LIMIT_REACHED'); - }); - - it('should revert if expiration is negative', async () => { - await expect(tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 0, tierData)).to.be.revertedWith('EXPIRATION_NOT_GREATER_THAN_ZERO'); - }); - }); - - describe('DEPOSIT INITIALIZATION', () => { - it(`should initialize nft deposit data with correct metadata`, async () => { - - // ACT - const expectedTimestamp = await setNextBlockTimestamp(); - const depositId = generateDepositId(Constants.bountyId, 0); - await tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 1, Constants.thirtyDays, tierData); - - // ASSERT - expect(await tieredFixedContract.funder(depositId)).to.equal(owner.address); - expect(await tieredFixedContract.tokenAddress(depositId)).to.equal(mockNft.address); - expect(await tieredFixedContract.tokenId(depositId)).to.equal(1); - expect(await tieredFixedContract.expiration(depositId)).to.equal(Constants.thirtyDays); - expect(await tieredFixedContract.isNFT(depositId)).to.equal(true); - - const depositTime = await tieredFixedContract.depositTime(depositId); - expect(depositTime.toString()).to.equal(expectedTimestamp.toString()); - }); - }); - - describe('transfer', () => { - it('should transfer NFT from owner to bounty contract', async () => { - // ASSUME - expect(await mockNft.ownerOf(0)).to.equal(owner.address); - - // ACT - - await tieredFixedContract.connect(depositManager).receiveNft(owner.address, mockNft.address, 0, 1, tierData); - - // ASSERT - expect(await mockNft.ownerOf(0)).to.equal(tieredFixedContract.address); - }); - }); }); describe('setTierWinner', () => { diff --git a/test/Bounty/TieredFixedBounty.test.js b/test/Bounty/TieredFixedBounty.test.js index 752fbfe5..49fffadf 100755 --- a/test/Bounty/TieredFixedBounty.test.js +++ b/test/Bounty/TieredFixedBounty.test.js @@ -24,7 +24,6 @@ describe('TieredFixedBountyV1.sol', () => { // MOCK ASSETS let mockLink; let mockDai; - let mockNft; // UTILS let abiCoder = new ethers.utils.AbiCoder; @@ -45,7 +44,6 @@ describe('TieredFixedBountyV1.sol', () => { TieredFixedBountyV1 = await ethers.getContractFactory('TieredFixedBountyV1'); const MockLink = await ethers.getContractFactory('MockLink'); const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); [owner, claimManager, depositManager] = await ethers.getSigners(); @@ -56,16 +54,6 @@ describe('TieredFixedBountyV1.sol', () => { mockDai = await MockDai.deploy(); await mockDai.deployed(); - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - // TIERED BOUNTY tieredFixedContract = await TieredFixedBountyV1.deploy(); await tieredFixedContract.deployed(); @@ -78,10 +66,6 @@ describe('TieredFixedBountyV1.sol', () => { // Pre-approve LINK and DAI for transfers during testing await mockLink.approve(tieredFixedContract.address, 10000000); await mockDai.approve(tieredFixedContract.address, 10000000); - - await mockNft.approve(tieredFixedContract.address, 0); - await mockNft.approve(tieredFixedContract.address, 1); - await mockNft.approve(tieredFixedContract.address, 2); }); describe('initializer', () => { diff --git a/test/Bounty/TieredPercentageBounty.test.js b/test/Bounty/TieredPercentageBounty.test.js deleted file mode 100755 index df9ab96f..00000000 --- a/test/Bounty/TieredPercentageBounty.test.js +++ /dev/null @@ -1,290 +0,0 @@ -/* eslint-disable */ -const { BigNumber } = require('@ethersproject/bignumber'); -const { expect } = require('chai'); -const { ethers } = require("hardhat"); -const truffleAssert = require('truffle-assertions'); -require('@nomiclabs/hardhat-waffle'); - -const { generateDepositId, generateClaimantId } = require('../utils'); - -const { - Constants, - tieredBountyInitOperationBuilder, - tieredBountyInitOperationBuilder_noFundingGoal -} = require('../constants'); - -describe('TieredPercentageBountyV1.sol', () => { - // CONTRACT FACTORIES - let TieredPercentageBountyV1; - - // ACCOUNTS - let owner; - let claimManager; - let depositManager; - - // MOCK ASSETS - let mockLink; - let mockDai; - let mockNft; - - // UTILS - let abiCoder = new ethers.utils.AbiCoder; - - // CONSTANTS - let closerData = abiCoder.encode(['address', 'string', 'address', 'string'], [ethers.constants.AddressZero, "FlacoJones", ethers.constants.AddressZero, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398"]); - - - // INITIALIZATION OPERATIONS - let tieredBountyInitOperation; - - // TEST CONTRACTS - let tieredContract; - let tieredContract_noFundingGoal; - - // MISC - let initializationTimestampTiered; - - beforeEach(async () => { - TieredPercentageBountyV1 = await ethers.getContractFactory('TieredPercentageBountyV1'); - const MockLink = await ethers.getContractFactory('MockLink'); - const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); - - [owner, claimManager, depositManager] = await ethers.getSigners(); - - // MOCK ASSETS - mockLink = await MockLink.deploy(); - await mockLink.deployed(); - - mockDai = await MockDai.deploy(); - await mockDai.deployed(); - - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - - // TIERED PERCENTAGE BOUNTY W/ FUNDING GOAL - tieredContract = await TieredPercentageBountyV1.deploy(); - await tieredContract.deployed(); - tieredBountyInitOperation = tieredBountyInitOperationBuilder(mockLink.address) - initializationTimestampTiered = await setNextBlockTimestamp(); - await tieredContract.initialize(Constants.bountyId, owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, tieredBountyInitOperation); - - // TIERED PERCENTAGE BOUNTY - NO FUNDING GOAL - tieredContract_noFundingGoal = await TieredPercentageBountyV1.deploy(); - await tieredContract_noFundingGoal.deployed(); - tieredBountyInitOperation_noFundingGoal = tieredBountyInitOperationBuilder_noFundingGoal() - await tieredContract_noFundingGoal.initialize(Constants.bountyId, owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, tieredBountyInitOperation_noFundingGoal); - - // Pre-approve LINK and DAI for transfers during testing - await mockLink.approve(tieredContract.address, 10000000); - await mockDai.approve(tieredContract.address, 10000000); - - await mockNft.approve(tieredContract.address, 0); - await mockNft.approve(tieredContract.address, 1); - await mockNft.approve(tieredContract.address, 2); - }); - - describe('initializer', () => { - it('should init with tiered correct metadata', async () => { - const actualBountyPayoutSchedule = await tieredContract.getPayoutSchedule(); - const payoutToString = actualBountyPayoutSchedule.map(thing => thing.toString()); - - await expect(await tieredContract.bountyId()).equals(Constants.bountyId); - await expect(await tieredContract.issuer()).equals(owner.address); - await expect(await tieredContract.organization()).equals(Constants.organization); - await expect(await tieredContract.status()).equals(0); - await expect(await tieredContract.openQ()).equals(owner.address); - await expect(await tieredContract.claimManager()).equals(claimManager.address); - await expect(await tieredContract.depositManager()).equals(depositManager.address); - await expect(await tieredContract.bountyCreatedTime()).equals(initializationTimestampTiered); - await expect(await tieredContract.bountyType()).equals(Constants.TIERED_PERCENTAGE_CONTRACT); - await expect(await tieredContract.hasFundingGoal()).equals(true); - await expect(await tieredContract.fundingToken()).equals(mockLink.address); - await expect(await tieredContract.fundingGoal()).equals(100); - await expect(payoutToString[0]).equals("60"); - await expect(payoutToString[1]).equals("30"); - await expect(payoutToString[2]).equals("10"); - await expect(await tieredContract.invoiceRequired()).equals(true); - await expect(await tieredContract.kycRequired()).equals(true); - await expect(await tieredContract.issuerExternalUserId()).equals(Constants.mockOpenQId); - await expect(await tieredContract.supportingDocumentsRequired()).equals(true); - - await expect(await tieredContract.invoiceComplete(0)).equals(false); - await expect(await tieredContract.supportingDocumentsComplete(0)).equals(false); - }); - - it('should revert if payoutSchedule values do not add up to 100', async () => { - // ARRANGE - tieredContract = await TieredPercentageBountyV1.deploy(); - await tieredContract.deployed(); - - const abiEncodedParamsTieredBountyNot100 = abiCoder.encode(["uint256[]", "bool", "address", "uint256", "bool", "bool", "bool", "string", "string", "string"], [[1, 2], true, mockLink.address, 100, true, true, true, Constants.mockOpenQId, "", ""]); - - tieredBountyInitOperation = [2, abiEncodedParamsTieredBountyNot100]; - - // ACT/ASSERT - await expect(tieredContract.initialize(Constants.bountyId, owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, tieredBountyInitOperation)).to.be.revertedWith('PAYOUT_SCHEDULE_MUST_ADD_TO_100'); - }); - - it('should revert if bountyId is empty', async () => { - // ARRANGE - const TieredPercentageBountyV1 = await ethers.getContractFactory('TieredPercentageBountyV1'); - tieredContract = await TieredPercentageBountyV1.deploy(); - - // ASSERT - await expect(tieredContract.initialize("", owner.address, Constants.organization, owner.address, claimManager.address, depositManager.address, tieredBountyInitOperation)).to.be.revertedWith('NO_EMPTY_BOUNTY_ID'); - }); - - it('should revert if organization is empty', async () => { - // ARRANGE - const TieredPercentageBountyV1 = await ethers.getContractFactory('TieredPercentageBountyV1'); - tieredContract = await TieredPercentageBountyV1.deploy(); - - // ASSERT - await expect(tieredContract.initialize(Constants.bountyId, owner.address, "", owner.address, claimManager.address, depositManager.address, tieredBountyInitOperation)).to.be.revertedWith('NO_EMPTY_ORGANIZATION'); - }); - }); - - describe('claimTiered', () => { - it('should transfer volume of tokenAddress balance based on payoutSchedule', async () => { - // ARRANGE - const volume = 1000; - - const [, firstPlace, secondPlace] = await ethers.getSigners(); - - await tieredContract.connect(depositManager).receiveFunds(owner.address, mockLink.address, volume, Constants.thirtyDays); - - const deposits = await tieredContract.getDeposits(); - const linkDepositId = deposits[0]; - - await tieredContract.connect(claimManager).closeCompetition(); - - // ASSUME - const bountyMockTokenBalance = (await mockLink.balanceOf(tieredContract.address)).toString(); - expect(bountyMockTokenBalance).to.equal('1000'); - - const claimerMockTokenBalance = (await mockLink.balanceOf(firstPlace.address)).toString(); - expect(claimerMockTokenBalance).to.equal('0'); - - // ACT - await tieredContract.connect(claimManager).claimTiered(firstPlace.address, 0, mockLink.address); - - // // // ASSERT - // const newClaimerMockTokenBalance = (await mockLink.balanceOf(firstPlace.address)).toString(); - // expect(newClaimerMockTokenBalance).to.equal('800'); - - // // ACT - // await tieredContract.connect(claimManager).claimTiered(secondPlace.address, 1, mockLink.address); - - // // // ASSERT - // const secondPlaceMockTokenBalance = (await mockLink.balanceOf(secondPlace.address)).toString(); - // expect(secondPlaceMockTokenBalance).to.equal('200'); - }); - - it('should revert if not called by claim manager', async () => { - // ACT/ASSERT - await expect(tieredContract.claimTiered(owner.address, 0, mockLink.address)).to.be.revertedWith('ClaimManagerOwnable: caller is not the current OpenQ Claim Manager'); - }); - }); - - describe('closeCompetition', () => { - it('should set bounty status to 1, freeze balances and set bountyClosedTime', async () => { - // ARRANGE - const volume = 1000; - - const [, firstPlace, secondPlace] = await ethers.getSigners(); - - await tieredContract.connect(depositManager).receiveFunds(owner.address, mockLink.address, volume, Constants.thirtyDays); - - // ASSUME - const bountyMockTokenBalance = (await mockLink.balanceOf(tieredContract.address)).toString(); - expect(bountyMockTokenBalance).to.equal('1000'); - - const claimerMockTokenBalance = (await mockLink.balanceOf(firstPlace.address)).toString(); - expect(claimerMockTokenBalance).to.equal('0'); - - // ASSUME - let status = await tieredContract.status(); - let mockTokenFundingTotal = await tieredContract.fundingTotals(mockLink.address); - let bountyClosedTime = await tieredContract.bountyClosedTime(); - - expect(status).to.equal(0); - expect(mockTokenFundingTotal).to.equal(0); - expect(bountyClosedTime).to.equal(0); - - const expectedTimestamp = await setNextBlockTimestamp(); - // ACT - await tieredContract.connect(claimManager).closeCompetition(); - - // ASSERT - status = await tieredContract.status(); - mockTokenFundingTotal = await tieredContract.fundingTotals(mockLink.address); - bountyClosedTime = await tieredContract.bountyClosedTime(); - - expect(status).to.equal(1); - expect(mockTokenFundingTotal).to.equal(1000); - expect(bountyClosedTime).to.equal(expectedTimestamp); - }); - - it('should revert if already closed', async () => { - await tieredContract.connect(claimManager).closeCompetition(); - await expect(tieredContract.connect(claimManager).closeCompetition()).to.be.revertedWith('CONTRACT_ALREADY_CLOSED'); - }); - }); - - describe('setPayoutSchedule', () => { - it('should revert if not called by OpenQ contract', async () => { - // ARRANGE - const [, notOwner] = await ethers.getSigners(); - - // ASSERT - await expect(tieredContract.connect(notOwner).setPayoutSchedule([80, 20])).to.be.revertedWith('Method is only callable by OpenQ'); - }); - - it('should revert if payoutschedule doesnt add to 100', async () => { - // ARRANGE - const [, notOwner] = await ethers.getSigners(); - - // ASSERT - await expect(tieredContract.connect(notOwner).setPayoutSchedule([100, 20])).to.be.revertedWith('Method is only callable by OpenQ'); - }); - - it('should set payout schedule', async () => { - // ASSUME - let initialPayoutSchedule = await tieredContract.getPayoutSchedule(); - let payoutToString = initialPayoutSchedule.map(thing => thing.toString()); - expect(payoutToString[0]).to.equal('60'); - expect(payoutToString[1]).to.equal('30'); - expect(payoutToString[2]).to.equal('10'); - - // ACT - await tieredContract.setPayoutSchedule([70, 20, 10]); - - // ASSERT - let expectedPayoutSchedule = await tieredContract.getPayoutSchedule(); - payoutToString = expectedPayoutSchedule.map(thing => thing.toString()); - expect(payoutToString[0]).to.equal('70'); - expect(payoutToString[1]).to.equal('20'); - expect(payoutToString[2]).to.equal('10'); - }); - }); - -}); - -async function setNextBlockTimestamp() { - return new Promise(async (resolve,) => { - const blockNumBefore = await ethers.provider.getBlockNumber(); - const blockBefore = await ethers.provider.getBlock(blockNumBefore); - const timestampBefore = blockBefore.timestamp; - const expectedTimestamp = timestampBefore + 10; - await network.provider.send("evm_setNextBlockTimestamp", [expectedTimestamp]); - resolve(expectedTimestamp); - }); -} \ No newline at end of file diff --git a/test/BountyFactory.test.js b/test/BountyFactory.test.js index 567b7c84..24fe7453 100755 --- a/test/BountyFactory.test.js +++ b/test/BountyFactory.test.js @@ -10,8 +10,6 @@ const { atomicBountyInitOperation_fundingGoal, atomicBountyInitOperation_noFundingGoal, atomicBountyInitOperation_permissioned, - ongoingBountyInitOperationBuilder, - tieredBountyInitOperationBuilder, tieredFixedBountyInitOperationBuilder, tieredBountyInitOperation_not100 } = require('./constants'); @@ -27,8 +25,6 @@ describe('BountyFactory', () => { let OpenQProxy; let AtomicBountyV1; - let OngoingBountyV1; - let TieredPercentageBountyV1; let TieredFixedBountyV1; let BountyBeacon; @@ -41,8 +37,6 @@ describe('BountyFactory', () => { let depositManager; let atomicBountyInitOperation; - let ongoingBountyInitOperation; - let tieredPercentageBountyInitOperation; let tieredFixedBountyInitOperation; const mockOpenQId = "mockOpenQId" @@ -57,8 +51,6 @@ describe('BountyFactory', () => { BountyBeacon = await hre.ethers.getContractFactory('BountyBeacon'); AtomicBountyV1 = await hre.ethers.getContractFactory('AtomicBountyV1'); - OngoingBountyV1 = await hre.ethers.getContractFactory('OngoingBountyV1'); - TieredPercentageBountyV1 = await hre.ethers.getContractFactory('TieredPercentageBountyV1'); TieredFixedBountyV1 = await hre.ethers.getContractFactory('TieredFixedBountyV1'); [owner, oracle, notOpenQ, claimManager, depositManager] = await ethers.getSigners(); @@ -74,12 +66,6 @@ describe('BountyFactory', () => { atomicBountyV1 = await AtomicBountyV1.deploy(); await atomicBountyV1.deployed(); - ongoingBountyV1 = await OngoingBountyV1.deploy(); - await ongoingBountyV1.deployed(); - - tieredPercentageBountyV1 = await TieredPercentageBountyV1.deploy(); - await tieredPercentageBountyV1.deployed(); - tieredFixedBountyV1 = await TieredFixedBountyV1.deploy(); await tieredFixedBountyV1.deployed(); @@ -87,12 +73,6 @@ describe('BountyFactory', () => { atomicBountyBeacon = await BountyBeacon.deploy(atomicBountyV1.address); await atomicBountyBeacon.deployed(); - ongoingBountyBeacon = await BountyBeacon.deploy(ongoingBountyV1.address); - await ongoingBountyBeacon.deployed(); - - tieredPercentageBountyBeacon = await BountyBeacon.deploy(tieredPercentageBountyV1.address); - await tieredPercentageBountyBeacon.deployed(); - tieredFixedBountyBeacon = await BountyBeacon.deploy(tieredFixedBountyV1.address); await tieredFixedBountyBeacon.deployed(); @@ -110,8 +90,6 @@ describe('BountyFactory', () => { bountyFactory = await BountyFactory.deploy( openQProxy.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, tieredFixedBountyBeacon.address ); await bountyFactory.deployed(); @@ -120,8 +98,6 @@ describe('BountyFactory', () => { const abiCoder = new ethers.utils.AbiCoder; atomicBountyInitOperation = atomicBountyInitOperation_fundingGoal(mockLink.address) - ongoingBountyInitOperation = ongoingBountyInitOperationBuilder(mockLink.address) - tieredPercentageBountyInitOperation = tieredBountyInitOperationBuilder(mockLink.address) tieredFixedBountyInitOperation = tieredFixedBountyInitOperationBuilder(mockLink.address) }); @@ -129,8 +105,6 @@ describe('BountyFactory', () => { it('should initiatlize with correct OpenQ proxy address and BountyBeacon address', async () => { expect(await bountyFactory.openQ()).equals(openQProxy.address); expect(await bountyFactory.atomicBountyBeacon()).equals(atomicBountyBeacon.address); - expect(await bountyFactory.ongoingBountyBeacon()).equals(ongoingBountyBeacon.address); - expect(await bountyFactory.tieredPercentageBountyBeacon()).equals(tieredPercentageBountyBeacon.address); expect(await bountyFactory.tieredFixedBountyBeacon()).equals(tieredFixedBountyBeacon.address); }); }); @@ -147,8 +121,6 @@ describe('BountyFactory', () => { let newBountyFactory = await BountyFactory.deploy( owner.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, tieredFixedBountyBeacon.address ); await newBountyFactory.deployed(); @@ -189,115 +161,11 @@ describe('BountyFactory', () => { await expect(atomicContract.initialize(mockOpenQId, owner.address, organization, owner.address, claimManager.address, depositManager.address, atomicBountyInitOperation)).to.be.revertedWith('Initializable: contract is already initialized'); }); - it('should mint a bounty with expected data - ONGOING', async () => { - // Must redeploy and pretend that owner account is OpenQ in order to call BountyFactory.mintBounty - let newBountyFactory = await BountyFactory.deploy( - owner.address, - atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, - tieredFixedBountyBeacon.address - ); - await newBountyFactory.deployed(); - - let initializationTimestamp = await setNextBlockTimestamp(); - - const txn = await newBountyFactory.mintBounty( - mockId, - owner.address, - organization, - claimManager.address, - depositManager.address, - ongoingBountyInitOperation - ); - - const receipt = await txn.wait(); - - const ongoingContract = await OngoingBountyV1.attach(receipt.events[0].address); - - await expect(await ongoingContract.bountyId()).equals(mockId); - await expect(await ongoingContract.issuer()).equals(owner.address); - await expect(await ongoingContract.organization()).equals(organization); - await expect(await ongoingContract.status()).equals(0); - await expect(await ongoingContract.openQ()).equals(owner.address); - await expect(await ongoingContract.claimManager()).equals(claimManager.address); - await expect(await ongoingContract.depositManager()).equals(depositManager.address); - await expect(await ongoingContract.bountyCreatedTime()).equals(initializationTimestamp); - await expect(await ongoingContract.bountyType()).equals(Constants.ONGOING_CONTRACT); - await expect(await ongoingContract.hasFundingGoal()).equals(true); - await expect(await ongoingContract.fundingToken()).equals(mockLink.address); - await expect(await ongoingContract.fundingGoal()).equals(Constants.volume); - await expect(await ongoingContract.issuerExternalUserId()).equals(mockOpenQId); - - await expect(await ongoingContract.invoiceRequired()).equals(false); - await expect(await ongoingContract.kycRequired()).equals(false); - await expect(await ongoingContract.supportingDocumentsRequired()).equals(false); - - await expect(ongoingContract.initialize('mock-id', owner.address, 'mock-organization', owner.address, claimManager.address, depositManager.address, ongoingBountyInitOperation)).to.be.revertedWith('Initializable: contract is already initialized'); - }); - - it('should mint a bounty with expected data - TIERED PERCENTAGE', async () => { - // Must redeploy and pretend that owner account is OpenQ in order to call BountyFactory.mintBounty - let newBountyFactory = await BountyFactory.deploy( - owner.address, - atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, - tieredFixedBountyBeacon.address - ); - await newBountyFactory.deployed(); - - let initializationTimestamp = await setNextBlockTimestamp(); - - const txn = await newBountyFactory.mintBounty( - mockId, - owner.address, - organization, - claimManager.address, - depositManager.address, - tieredPercentageBountyInitOperation - ); - - const receipt = await txn.wait(); - - const tieredPercentageContract = await TieredPercentageBountyV1.attach(receipt.events[0].address); - - const actualBountyPayoutSchedule = await tieredPercentageContract.getPayoutSchedule(); - const payoutToString = actualBountyPayoutSchedule.map(thing => thing.toString()); - - await expect(await tieredPercentageContract.bountyId()).equals(mockId); - await expect(await tieredPercentageContract.bountyId()).equals(mockId); - await expect(await tieredPercentageContract.issuer()).equals(owner.address); - await expect(await tieredPercentageContract.organization()).equals(organization); - await expect(await tieredPercentageContract.status()).equals(0); - await expect(await tieredPercentageContract.openQ()).equals(owner.address); - await expect(await tieredPercentageContract.claimManager()).equals(claimManager.address); - await expect(await tieredPercentageContract.depositManager()).equals(depositManager.address); - await expect(await tieredPercentageContract.bountyCreatedTime()).equals(initializationTimestamp); - await expect(await tieredPercentageContract.bountyType()).equals(Constants.TIERED_PERCENTAGE_CONTRACT); - await expect(await tieredPercentageContract.hasFundingGoal()).equals(true); - await expect(await tieredPercentageContract.fundingToken()).equals(mockLink.address); - await expect(await tieredPercentageContract.fundingGoal()).equals(Constants.volume); - await expect(payoutToString[0]).equals("60"); - await expect(payoutToString[1]).equals("30"); - await expect(await tieredPercentageContract.invoiceRequired()).equals(true); - await expect(await tieredPercentageContract.kycRequired()).equals(true); - await expect(await tieredPercentageContract.issuerExternalUserId()).equals(mockOpenQId); - - await expect(await tieredPercentageContract.supportingDocumentsRequired()).equals(true); - await expect(await tieredPercentageContract.invoiceComplete(0)).equals(false); - await expect(await tieredPercentageContract.supportingDocumentsComplete(0)).equals(false); - - await expect(tieredPercentageContract.initialize(mockOpenQId, owner.address, organization, owner.address, claimManager.address, depositManager.address, tieredPercentageBountyInitOperation)).to.be.revertedWith('Initializable: contract is already initialized'); - }); - it('should mint a bounty with expected data - TIERED FIXED', async () => { // Must redeploy and pretend that owner account is OpenQ in order to call BountyFactory.mintBounty let newBountyFactory = await BountyFactory.deploy( owner.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, tieredFixedBountyBeacon.address ); await newBountyFactory.deployed(); @@ -348,8 +216,6 @@ describe('BountyFactory', () => { let newBountyFactory = await BountyFactory.deploy( owner.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, tieredFixedBountyBeacon.address ); await newBountyFactory.deployed(); diff --git a/test/ClaimManager.test.js b/test/ClaimManager.test.js index 41be3ce4..2a56b7f9 100755 --- a/test/ClaimManager.test.js +++ b/test/ClaimManager.test.js @@ -12,15 +12,11 @@ const { atomicBountyInitOperation_fundingGoal, atomicBountyInitOperation_noFundingGoal, atomicBountyInitOperation_permissioned, - ongoingBountyInitOperationBuilder, - tieredBountyInitOperationBuilder, tieredFixedBountyInitOperationBuilder, - tieredBountyInitOperation_not100, setInvoiceCompleteData_tiered, setSupportingDocumentsComplete_tiered, setInvoiceCompleteData_atomic, setSupportingDocumentsComplete_atomic, - tieredBountyInitOperationBuilder_permissionless, tieredFixedBountyInitOperationBuilder_permissionless } = require('./constants'); @@ -33,7 +29,6 @@ describe('ClaimManager.sol', () => { let mockLink; let mockDai; let blacklistedMockDai; - let mockNft; let openQTokenWhitelist; let mockKyc; @@ -53,9 +48,6 @@ describe('ClaimManager.sol', () => { // INIT OPERATIONS let atomicBountyInitOperation; - let ongoingBountyInitOperation; - let tieredPercentageBountyInitOperation_permissionless; - let tieredPercentageBountyInitOperation_permissioned let tieredFixedBountyInitOperation_permissionless; let tieredFixedBountyInitOperation_permissioned; @@ -71,8 +63,6 @@ describe('ClaimManager.sol', () => { let abiEncodedTieredCloserDataThirdPlace; let AtomicBountyV1 - let OngoingBountyV1 - let TieredPercentageBountyV1 let TieredFixedBountyV1 beforeEach(async () => { @@ -80,27 +70,18 @@ describe('ClaimManager.sol', () => { const OpenQProxy = await ethers.getContractFactory('OpenQProxy'); const MockLink = await ethers.getContractFactory('MockLink'); const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); const OpenQTokenWhitelist = await ethers.getContractFactory('OpenQTokenWhitelist'); const DepositManager = await ethers.getContractFactory('DepositManagerV1'); const ClaimManager = await ethers.getContractFactory('ClaimManagerV1'); const MockKyc = await ethers.getContractFactory('MockKyc'); AtomicBountyV1 = await ethers.getContractFactory('AtomicBountyV1'); - OngoingBountyV1 = await ethers.getContractFactory('OngoingBountyV1'); - TieredPercentageBountyV1 = await ethers.getContractFactory('TieredPercentageBountyV1'); TieredFixedBountyV1 = await ethers.getContractFactory('TieredFixedBountyV1'); // BOUNTY IMPLEMENTATIONS atomicBountyV1 = await AtomicBountyV1.deploy(); await atomicBountyV1.deployed(); - ongoingBountyV1 = await OngoingBountyV1.deploy(); - await ongoingBountyV1.deployed(); - - tieredPercentageBountyV1 = await TieredPercentageBountyV1.deploy(); - await tieredPercentageBountyV1.deployed(); - tieredFixedBountyV1 = await TieredFixedBountyV1.deploy(); await tieredFixedBountyV1.deployed(); @@ -133,32 +114,17 @@ describe('ClaimManager.sol', () => { blacklistedMockDai = await MockDai.deploy(); await blacklistedMockDai.deployed(); - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - openQTokenWhitelist = await OpenQTokenWhitelist.deploy(5); + openQTokenWhitelist = await OpenQTokenWhitelist.deploy(); await openQTokenWhitelist.deployed(); await openQTokenWhitelist.addToken(mockLink.address); await openQTokenWhitelist.addToken(mockDai.address); await openQTokenWhitelist.addToken(ethers.constants.AddressZero); - await openQTokenWhitelist.addToken(mockNft.address); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); // BOUNTY BEACONS atomicBountyBeacon = await BountyBeacon.deploy(atomicBountyV1.address); await atomicBountyBeacon.deployed(); - ongoingBountyBeacon = await BountyBeacon.deploy(ongoingBountyV1.address); - await ongoingBountyBeacon.deployed(); - - tieredPercentageBountyBeacon = await BountyBeacon.deploy(tieredPercentageBountyV1.address); - await tieredPercentageBountyBeacon.deployed(); - tieredFixedBountyBeacon = await BountyBeacon.deploy(tieredFixedBountyV1.address); await tieredFixedBountyBeacon.deployed(); @@ -166,8 +132,6 @@ describe('ClaimManager.sol', () => { bountyFactory = await BountyFactory.deploy( openQProxy.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, tieredFixedBountyBeacon.address ); await bountyFactory.deployed(); @@ -198,10 +162,6 @@ describe('ClaimManager.sol', () => { abiCoder = new ethers.utils.AbiCoder; atomicBountyInitOperation = atomicBountyInitOperation_fundingGoal(mockLink.address) - ongoingBountyInitOperation = ongoingBountyInitOperationBuilder(mockLink.address) - - tieredPercentageBountyInitOperation_permissionless = tieredBountyInitOperationBuilder_permissionless(mockLink.address) - tieredPercentageBountyInitOperation_permissioned = tieredBountyInitOperationBuilder(mockLink.address) tieredFixedBountyInitOperation_permissionless = tieredFixedBountyInitOperationBuilder_permissionless(mockLink.address) tieredFixedBountyInitOperation_permissioned = tieredFixedBountyInitOperationBuilder(mockLink.address) @@ -250,46 +210,6 @@ describe('ClaimManager.sol', () => { }); }); - describe('bountyIsClaimable', () => { - describe('ATOMIC', () => { - it('should return TRUE if atomic bounty is open, FALSE if atomic bounty is closed', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - // ASSUME - let bountyIsClaimable = await claimManager.bountyIsClaimable(bountyAddress); - expect(bountyIsClaimable).to.equal(true); - - // ACT - await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedSingleCloserData); - - // ASSERT - bountyIsClaimable = await claimManager.bountyIsClaimable(bountyAddress); - expect(bountyIsClaimable).to.equal(false); - }); - }); - - describe('ONGOING', () => { - it('should return TRUE if ongoing bounty is open, FALSE if ongoing bounty is closed', async () => { - // ARRANCE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, ongoingBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - // ASSUME - let bountyIsClaimable = await claimManager.bountyIsClaimable(bountyAddress); - expect(bountyIsClaimable).to.equal(true); - - // ACT - await openQProxy.closeOngoing(Constants.bountyId); - - // ASSERT - bountyIsClaimable = await claimManager.bountyIsClaimable(bountyAddress); - expect(bountyIsClaimable).to.equal(false); - }); - }); - }); - describe('claimBounty', () => { describe('ALL', () => { @@ -427,22 +347,6 @@ describe('ClaimManager.sol', () => { expect(newClaimerMockTokenBalance).to.equal('100'); expect(newClaimerFakeTokenBalance).to.equal('100'); }); - - it('should transfer all NFT assets from bounty contract to claimant', async () => { - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - // ACT - - await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedSingleCloserData); - - // ASSERT - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - }); }); describe('EVENTS', () => { @@ -469,26 +373,6 @@ describe('ClaimManager.sol', () => { .withArgs(Constants.bountyId, bountyAddress, Constants.organization, owner.address, expectedTimestamp, ethers.constants.AddressZero, volume, 0, abiEncodedSingleCloserData, Constants.VERSION_1); }); - it('should emit an NftClaimed event with correct parameters', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await mockNft.approve(bountyAddress, 1); - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, 1, 1, zeroTier); - - // Closer data for 2nd and 3rd place - let abiEncodedTieredCloserDataFirstPlace = abiCoder.encode(['address', 'string', 'address', 'string', 'uint256'], [owner.address, "FlacoJones", owner.address, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398", 0]); - - const expectedTimestamp = await setNextBlockTimestamp(); - - // ACT/ASSERT - - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedSingleCloserData)) - .to.emit(claimManager, 'NFTClaimed') - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, owner.address, expectedTimestamp, mockNft.address, 1, 0, abiEncodedSingleCloserData, Constants.VERSION_1); - }); - it('should emit a BountyClosed event with correct parameters', async () => { // ARRANGE await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); @@ -505,284 +389,6 @@ describe('ClaimManager.sol', () => { }); }); - describe('ONGOING', () => { - describe('REVERTS', () => { - it('should revert if ongoing bounty is closed', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, ongoingBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await openQProxy.closeOngoing(Constants.bountyId); - - // ACT/ASSERT - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedSingleCloserData)).to.be.revertedWith('CONTRACT_IS_NOT_CLAIMABLE'); - }); - }); - - describe('TRANSFER', () => { - it('should transfer payoutAmount from bounty pool to claimant', async () => { - // ARRANGE - const volume = 1000; - const expectedTimestamp = await setNextBlockTimestamp(); - - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, ongoingBountyInitOperation); - - const bountyIsOpen = await openQProxy.bountyIsOpen(Constants.bountyId); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - const newBounty = await OngoingBountyV1.attach( - bountyAddress - ); - - await mockLink.approve(bountyAddress, 10000000); - - await depositManager.fundBountyToken(bountyAddress, mockLink.address, volume, 1, Constants.funderUuid); - - // ACT - - await claimManager.connect(oracle).claimBounty(bountyAddress, claimant.address, abiEncodedOngoingCloserData); - - // ASSERT - const newBountyMockTokenBalance = (await mockLink.balanceOf(bountyAddress)).toString(); - expect(newBountyMockTokenBalance).to.equal('900'); - - const newClaimerMockTokenBalance = (await mockLink.balanceOf(claimant.address)).toString(); - expect(newClaimerMockTokenBalance).to.equal('100'); - }); - }); - - describe('EVENTS', () => { - it('should emit a TokenBalanceClaimed event with correct parameters', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, ongoingBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - const volume = 1000; - const bounty = OngoingBountyV1.attach(bountyAddress); - const payoutVolume = await bounty.payoutVolume(); - - await mockLink.approve(bountyAddress, 10000000); - await depositManager.fundBountyToken(bountyAddress, mockLink.address, volume, 1, Constants.funderUuid); - - const expectedTimestamp = await setNextBlockTimestamp(); - // ACT - // ASSERT - - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedOngoingCloserData)) - .to.emit(claimManager, 'TokenBalanceClaimed') - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, owner.address, expectedTimestamp, mockLink.address, payoutVolume, 1, abiEncodedOngoingCloserData, Constants.VERSION_1); - }); - }); - }); - - describe('TIERED', () => { - describe('REVERTS', () => { - it('should revert if tier is claimed', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await mockLink.approve(bountyAddress, 10000000); - await depositManager.fundBountyToken(bountyAddress, mockLink.address, 10000000, 1, Constants.funderUuid); - - await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData); - - // ACT/ASSERT - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData)).to.be.revertedWith('TIER_ALREADY_CLAIMED'); - }); - }); - - describe('BOUNTY UPDATES', () => { - it('should close competition if it is the first claimant', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - const bounty = TieredPercentageBountyV1.attach(bountyAddress); - - // ASSUME - const isOpen = await bounty.status(); - await expect(isOpen).to.equal(0); - - // ACT - await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData); - - // ACT/ASSERT - const isClosed = await bounty.status(); - await expect(isClosed).to.equal(1); - }); - }); - - describe('TRANSFER', () => { - it('should transfer all assets from bounty contract to claimant', async () => { - // ARRANGE - const volume = 1000; - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await mockLink.approve(bountyAddress, 10000000); - await mockDai.approve(bountyAddress, 10000000); - - await depositManager.fundBountyToken(bountyAddress, mockLink.address, volume, 1, Constants.funderUuid); - await depositManager.fundBountyToken(bountyAddress, mockDai.address, volume, 1, Constants.funderUuid); - await depositManager.fundBountyToken(bountyAddress, ethers.constants.AddressZero, volume, 1, Constants.funderUuid, { value: volume }); - - // ASSUME - const bountyMockLinkTokenBalance = (await mockLink.balanceOf(bountyAddress)).toString(); - const bountyDaiTokenBalance = (await mockDai.balanceOf(bountyAddress)).toString(); - const bountyProtcolTokenBalance = (await ethers.provider.getBalance(bountyAddress)).toString(); - expect(bountyMockLinkTokenBalance).to.equal('1000'); - expect(bountyDaiTokenBalance).to.equal('1000'); - expect(bountyProtcolTokenBalance).to.equal('1000'); - - const claimantMockTokenBalance = (await mockLink.balanceOf(claimant.address)).toString(); - const claimantFakeTokenBalance = (await mockDai.balanceOf(claimant.address)).toString(); - const claimantProtocolTokenBalance = (await ethers.provider.getBalance(claimant.address)).toString(); - expect(claimantMockTokenBalance).to.equal('0'); - expect(claimantFakeTokenBalance).to.equal('0'); - - // ACT - await claimManager.connect(oracle).claimBounty(bountyAddress, claimant.address, abiEncodedTieredCloserDataFirstPlace); - await claimManager.connect(oracle).claimBounty(bountyAddress, claimantSecondPlace.address, abiEncodedTieredCloserDataSecondPlace); - await claimManager.connect(oracle).claimBounty(bountyAddress, claimantThirdPlace.address, abiEncodedTieredCloserDataThirdPlace); - - // ASSERT - const newBountyMockTokenBalance = (await mockLink.balanceOf(bountyAddress)).toString(); - const newBountyFakeTokenBalance = (await mockDai.balanceOf(bountyAddress)).toString(); - const newBountyProtocolTokenBalance = (await ethers.provider.getBalance(bountyAddress)).toString(); - expect(newBountyMockTokenBalance).to.equal('0'); - expect(newBountyFakeTokenBalance).to.equal('0'); - expect(newBountyProtocolTokenBalance).to.equal('0'); - - const newClaimerMockTokenBalance = (await mockLink.balanceOf(claimant.address)).toString(); - const newClaimerFakeTokenBalance = (await mockDai.balanceOf(claimant.address)).toString(); - const newClaimerProtocolTokenBalance = (await ethers.provider.getBalance(claimant.address)).toString(); - expect(newClaimerMockTokenBalance).to.equal('600'); - expect(newClaimerFakeTokenBalance).to.equal('600'); - - const newClaimerMockTokenBalanceSecondPlace = (await mockLink.balanceOf(claimantSecondPlace.address)).toString(); - const newClaimerFakeTokenBalanceSecondPlace = (await mockDai.balanceOf(claimantSecondPlace.address)).toString(); - const newClaimerProtocolTokenBalanceSecondPlace = (await ethers.provider.getBalance(claimantSecondPlace.address)).toString(); - expect(newClaimerMockTokenBalanceSecondPlace).to.equal('300'); - expect(newClaimerFakeTokenBalanceSecondPlace).to.equal('300'); - - const newClaimerMockTokenBalanceThirdPlace = (await mockLink.balanceOf(claimantThirdPlace.address)).toString(); - const newClaimerFakeTokenBalanceThirdPlace = (await mockDai.balanceOf(claimantThirdPlace.address)).toString(); - const newClaimerProtocolTokenBalanceThirdPlace = (await ethers.provider.getBalance(claimantThirdPlace.address)).toString(); - expect(newClaimerMockTokenBalanceThirdPlace).to.equal('100'); - expect(newClaimerFakeTokenBalanceThirdPlace).to.equal('100'); - }); - - it('should transfer NFT assets with the same tier the claimant won and emit an NFTClaimed event', async () => { - const FIRST_PLACE_NFT = 1; - const SECOND_PLACE_NFT = 2; - - // ASSUME - expect(await mockNft.ownerOf(FIRST_PLACE_NFT)).to.equal(owner.address); - expect(await mockNft.ownerOf(SECOND_PLACE_NFT)).to.equal(owner.address); - - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - // Fund with NFTs for 2nd and 3rd place - await mockNft.approve(bountyAddress, FIRST_PLACE_NFT); - await mockNft.approve(bountyAddress, SECOND_PLACE_NFT); - - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, FIRST_PLACE_NFT, 1, zeroTier); - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, SECOND_PLACE_NFT, 1, oneTier); - - let expectedTimestamp = await setNextBlockTimestamp(); - - // ACT - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, claimant.address, abiEncodedTieredCloserDataFirstPlace)) - .to.emit(claimManager, 'NFTClaimed') - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, claimant.address, expectedTimestamp, mockNft.address, 1, 2, abiEncodedTieredCloserDataFirstPlace, Constants.VERSION_1); - - - expectedTimestamp = await setNextBlockTimestamp(); - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, claimantSecondPlace.address, abiEncodedTieredCloserDataSecondPlace)) - .to.emit(claimManager, 'NFTClaimed') - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, claimantSecondPlace.address, expectedTimestamp, mockNft.address, 2, 2, abiEncodedTieredCloserDataSecondPlace, Constants.VERSION_1); - - // ASSERT - expect(await mockNft.ownerOf(FIRST_PLACE_NFT)).to.equal(claimant.address); - expect(await mockNft.ownerOf(SECOND_PLACE_NFT)).to.equal(claimantSecondPlace.address); - }); - }); - - describe('EVENTS', () => { - it('should emit a BountyClosed event with correct parameters', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - const volume = 1000; - const bounty = TieredPercentageBountyV1.attach(bountyAddress); - const payoutSchedule = await bounty.getPayoutSchedule(); - const proportion = payoutSchedule[1].toString(); - const payoutAmount = (proportion / 100) * volume; - - await mockLink.approve(bountyAddress, 10000000); - await mockDai.approve(bountyAddress, 10000000); - await depositManager.fundBountyToken(bountyAddress, mockLink.address, volume, 1, Constants.funderUuid); - await depositManager.fundBountyToken(bountyAddress, mockDai.address, volume, 1, Constants.funderUuid); - - const expectedTimestamp = await setNextBlockTimestamp(); - // ACT - // ASSERT - - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData)) - .to.emit(claimManager, 'BountyClosed') - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, ethers.constants.AddressZero, expectedTimestamp, 2, '0x', Constants.VERSION_1); - }); - - it('should emit a TokenBalanceClaimed event with correct parameters', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - const volume = 1000; - const bounty = TieredPercentageBountyV1.attach(bountyAddress); - const payoutSchedule = await bounty.getPayoutSchedule(); - const proportion = payoutSchedule[1].toString(); - const payoutAmount = (proportion / 100) * volume; - - await mockLink.approve(bountyAddress, 10000000); - await mockDai.approve(bountyAddress, 10000000); - await depositManager.fundBountyToken(bountyAddress, mockLink.address, volume, 1, Constants.funderUuid); - await depositManager.fundBountyToken(bountyAddress, mockDai.address, volume, 1, Constants.funderUuid); - - const expectedTimestamp = await setNextBlockTimestamp(); - // ACT - // ASSERT - - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData)) - .to.emit(claimManager, 'TokenBalanceClaimed') - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, owner.address, expectedTimestamp, mockLink.address, payoutAmount, 2, abiEncodedTieredCloserData, Constants.VERSION_1) - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, owner.address, expectedTimestamp, mockDai.address, payoutAmount, 2, abiEncodedTieredCloserData, Constants.VERSION_1); - }); - - it('should emit an NftClaimed event with correct parameters', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await mockNft.approve(bountyAddress, 1); - - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, 1, 1, zeroTier); - - const expectedTimestamp = await setNextBlockTimestamp(); - - // ACT/ASSERT - - await expect(claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserDataFirstPlace)) - .to.emit(claimManager, 'NFTClaimed') - .withArgs(Constants.bountyId, bountyAddress, Constants.organization, owner.address, expectedTimestamp, mockNft.address, 1, 2, abiEncodedTieredCloserDataFirstPlace, Constants.VERSION_1); - }); - }); - }); - describe('TIERED FIXED', () => { describe('REVERTS', () => { it('should revert if tier is already claimed', async () => { @@ -800,40 +406,6 @@ describe('ClaimManager.sol', () => { }); }); - describe('TRANSFER', () => { - it('should transfer NFT assets with tier corresponding to the tier the claimant won', async () => { - const FIRST_PLACE_NFT = 1; - const SECOND_PLACE_NFT = 2; - - // ASSUME - expect(await mockNft.ownerOf(FIRST_PLACE_NFT)).to.equal(owner.address); - expect(await mockNft.ownerOf(SECOND_PLACE_NFT)).to.equal(owner.address); - - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - // Fund with NFTs for 2nd and 3rd place - await mockNft.approve(bountyAddress, FIRST_PLACE_NFT); - await mockNft.approve(bountyAddress, SECOND_PLACE_NFT); - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, FIRST_PLACE_NFT, 1, zeroTier); - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, SECOND_PLACE_NFT, 1, oneTier); - - // Closer data for 2nd and 3rd place - let abiEncodedTieredCloserDataFirstPlace = abiCoder.encode(['address', 'string', 'address', 'string', 'uint256'], [owner.address, "FlacoJones", owner.address, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398", 0]); - let abiEncodedTieredCloserDataSecondPlace = abiCoder.encode(['address', 'string', 'address', 'string', 'uint256'], [owner.address, "FlacoJones", owner.address, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398", 1]); - - // ACT - - await claimManager.connect(oracle).claimBounty(bountyAddress, claimant.address, abiEncodedTieredCloserDataFirstPlace); - await claimManager.connect(oracle).claimBounty(bountyAddress, claimantSecondPlace.address, abiEncodedTieredCloserDataSecondPlace); - - // ASSERT - expect(await mockNft.ownerOf(FIRST_PLACE_NFT)).to.equal(claimant.address); - expect(await mockNft.ownerOf(SECOND_PLACE_NFT)).to.equal(claimantSecondPlace.address); - }); - }); - describe('EVENTS', () => { it('should emit BountyClosed event', async () => { // ARRANGE @@ -882,9 +454,9 @@ describe('ClaimManager.sol', () => { describe('tierClaimed', () => { it('should return FALSE if tier not claimed, TRUE if already claimed', async () => { // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); + await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissionless); const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - const bounty = await TieredPercentageBountyV1.attach(bountyAddress); + const bounty = await TieredFixedBountyV1.attach(bountyAddress); await mockLink.approve(bountyAddress, 10000000); await depositManager.fundBountyToken(bountyAddress, mockLink.address, 10000000, 1, Constants.funderUuid); @@ -896,35 +468,9 @@ describe('ClaimManager.sol', () => { // ACT await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData); - // // ASSERT - // tierClaimed = await openQProxy.tierClaimed(Constants.bountyId, 1); - // expect(tierClaimed).to.equal(true); - }); - }); - - describe('ongoingClaimed', () => { - it('should return FALSE if ongoing claimant is not claimed, TRUE if it is claimed', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, ongoingBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - const bounty = await OngoingBountyV1.attach(bountyAddress); - - await mockLink.approve(bountyAddress, 10000000); - await depositManager.fundBountyToken(bountyAddress, mockLink.address, 10000000, 1, Constants.funderUuid); - - let claimId = generateClaimantId('FlacoJones', "https://github.com/OpenQDev/OpenQ-Frontend/pull/398"); - - // ASSUME - let ongoingClaimed = await bounty.claimId(claimId); - expect(ongoingClaimed).to.equal(false); - - // ACT - - await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedOngoingCloserData); - - // // ASSERT - ongoingClaimed = await bounty.claimId(claimId); - expect(ongoingClaimed).to.equal(true); + // // // ASSERT + tierClaimed = await openQProxy.tierClaimed(Constants.bountyId, 1); + expect(tierClaimed).to.equal(true); }); }); @@ -947,7 +493,7 @@ describe('ClaimManager.sol', () => { it('should revert if caller lacks associated address to their uuid', async () => { // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissioned); + await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissioned); const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); // ASSERT @@ -956,7 +502,7 @@ describe('ClaimManager.sol', () => { it('should revert if claimant not tier winner', async () => { // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissioned); + await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissioned); const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); await openQProxy.connect(oracle).associateExternalIdToAddress(Constants.mockOpenQId, owner.address) @@ -967,7 +513,7 @@ describe('ClaimManager.sol', () => { it('should revert if caller lacks invoice', async () => { // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissioned); + await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissioned); const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); await mockKyc.setIsValid(true) @@ -979,7 +525,7 @@ describe('ClaimManager.sol', () => { it('should revert if caller lacks supporting documents', async () => { // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissioned); + await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissioned); const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); // Set Permissions @@ -993,7 +539,7 @@ describe('ClaimManager.sol', () => { it('should revert if caller is lacks KYC', async () => { // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissioned); + await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissioned); const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); await openQProxy.connect(oracle).associateExternalIdToAddress(Constants.mockOpenQId, owner.address) @@ -1005,64 +551,9 @@ describe('ClaimManager.sol', () => { await expect(claimManager.permissionedClaimTieredBounty(bountyAddress, abiEncodedTieredCloserDataFirstPlace)).to.be.revertedWith('ADDRESS_LACKS_KYC'); }); - it('should transfer tier to closer - TIERED', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissioned); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - const expectedTimestamp = await setNextBlockTimestamp(); - const volume = 100; - - const bounty = await TieredPercentageBountyV1.attach(bountyAddress); - - // ACT - await mockLink.approve(bountyAddress, 10000000); - await mockDai.approve(bountyAddress, 10000000); - - await depositManager.fundBountyToken(bountyAddress, mockLink.address, volume, 1, Constants.funderUuid); - await depositManager.fundBountyToken(bountyAddress, mockDai.address, volume, 1, Constants.funderUuid); - await depositManager.fundBountyToken(bountyAddress, ethers.constants.AddressZero, volume, 1, Constants.funderUuid, { value: volume }); - - // ASSUME - const bountyMockLinkTokenBalance = (await mockLink.balanceOf(bountyAddress)).toString(); - const bountyDaiTokenBalance = (await mockDai.balanceOf(bountyAddress)).toString(); - const bountyProtcolTokenBalance = (await ethers.provider.getBalance(bountyAddress)).toString(); - expect(bountyMockLinkTokenBalance).to.equal('100'); - expect(bountyDaiTokenBalance).to.equal('100'); - expect(bountyProtcolTokenBalance).to.equal('100'); - - const claimantMockTokenBalance = (await mockLink.balanceOf(claimant.address)).toString(); - const claimantFakeTokenBalance = (await mockDai.balanceOf(claimant.address)).toString(); - expect(claimantMockTokenBalance).to.equal('0'); - expect(claimantFakeTokenBalance).to.equal('0'); - - // ARRANGE - // Set Permissions - await mockKyc.setIsValid(true) - await openQProxy.connect(oracle).associateExternalIdToAddress(Constants.mockOpenQId, claimant.address) - await openQProxy.setTierWinner(Constants.bountyId, 0, Constants.mockOpenQId) - await openQProxy.setInvoiceComplete(Constants.bountyId, setInvoiceCompleteData_tiered(0, true)) - await openQProxy.setSupportingDocumentsComplete(Constants.bountyId, setSupportingDocumentsComplete_tiered(0, true)) - - // ACT - await claimManager.connect(claimant).permissionedClaimTieredBounty(bountyAddress, abiEncodedTieredCloserDataFirstPlace); - - // ASSERT - const newBountyMockTokenBalance = (await mockLink.balanceOf(bountyAddress)).toString(); - const newBountyFakeTokenBalance = (await mockDai.balanceOf(bountyAddress)).toString(); - const newBountyProtocolTokenBalance = (await ethers.provider.getBalance(bountyAddress)).toString(); - expect(newBountyMockTokenBalance).to.equal('40'); - expect(newBountyFakeTokenBalance).to.equal('40'); - expect(newBountyProtocolTokenBalance).to.equal('40'); - - const newClaimerMockTokenBalance = (await mockLink.balanceOf(claimant.address)).toString(); - const newClaimerFakeTokenBalance = (await mockDai.balanceOf(claimant.address)).toString(); - expect(newClaimerMockTokenBalance).to.equal('60'); - expect(newClaimerFakeTokenBalance).to.equal('60'); - }); - it('should transfer tier to closer - TIERED FIXED', async () => { // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissioned); + await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredFixedBountyInitOperation_permissioned); const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); const expectedTimestamp = await setNextBlockTimestamp(); const volume = 100; @@ -1095,10 +586,10 @@ describe('ClaimManager.sol', () => { // ASSERT const newBountyMockTokenBalance = (await mockLink.balanceOf(bountyAddress)).toString(); - expect(newBountyMockTokenBalance).to.equal('40'); + expect(newBountyMockTokenBalance).to.equal('20'); const newClaimerMockTokenBalance = (await mockLink.balanceOf(claimant.address)).toString(); - expect(newClaimerMockTokenBalance).to.equal('60'); + expect(newClaimerMockTokenBalance).to.equal('80'); }); }) diff --git a/test/DepositManager.test.js b/test/DepositManager.test.js index 00338d3b..fc9bdbf9 100755 --- a/test/DepositManager.test.js +++ b/test/DepositManager.test.js @@ -12,11 +12,7 @@ const { atomicBountyInitOperation_fundingGoal, atomicBountyInitOperation_noFundingGoal, atomicBountyInitOperation_permissioned, - ongoingBountyInitOperationBuilder, - tieredBountyInitOperationBuilder, - tieredFixedBountyInitOperationBuilder, - tieredBountyInitOperation_not100, - tieredBountyInitOperationBuilder_permissionless + tieredFixedBountyInitOperationBuilder_permissionless } = require('./constants'); describe('DepositManager.sol', () => { @@ -26,7 +22,6 @@ describe('DepositManager.sol', () => { let mockLink; let mockDai; let blacklistedMockDai; - let mockNft; let openQTokenWhitelist; let depositManager; let claimManager; @@ -43,21 +38,15 @@ describe('DepositManager.sol', () => { // INIT OPERATIONS let atomicBountyInitOperation; - let ongoingBountyInitOperation; - let tieredPercentageBountyInitOperation; - let tieredPercentageBountyInitOperation_permissionless; let tieredFixedBountyInitOperation; // CLOSER DATA let abiCoder; let abiEncodedSingleCloserData; - let abiEncodedOngoingCloserData; let abiEncodedTieredCloserData; let AtomicBountyV1 - let OngoingBountyV1 - let TieredPercentageBountyV1 let TieredFixedBountyV1 beforeEach(async () => { @@ -65,26 +54,17 @@ describe('DepositManager.sol', () => { const OpenQProxy = await ethers.getContractFactory('OpenQProxy'); const MockLink = await ethers.getContractFactory('MockLink'); const MockDai = await ethers.getContractFactory('MockDai'); - const MockNft = await ethers.getContractFactory('MockNft'); const OpenQTokenWhitelist = await ethers.getContractFactory('OpenQTokenWhitelist'); const DepositManager = await ethers.getContractFactory('DepositManagerV1'); const ClaimManager = await ethers.getContractFactory('ClaimManagerV1'); AtomicBountyV1 = await ethers.getContractFactory('AtomicBountyV1'); - OngoingBountyV1 = await ethers.getContractFactory('OngoingBountyV1'); - TieredPercentageBountyV1 = await ethers.getContractFactory('TieredPercentageBountyV1'); TieredFixedBountyV1 = await ethers.getContractFactory('TieredFixedBountyV1'); // BOUNTY IMPLEMENTATIONS atomicBountyV1 = await AtomicBountyV1.deploy(); await atomicBountyV1.deployed(); - ongoingBountyV1 = await OngoingBountyV1.deploy(); - await ongoingBountyV1.deployed(); - - tieredPercentageBountyV1 = await TieredPercentageBountyV1.deploy(); - await tieredPercentageBountyV1.deployed(); - tieredFixedBountyV1 = await TieredFixedBountyV1.deploy(); await tieredFixedBountyV1.deployed(); @@ -113,32 +93,17 @@ describe('DepositManager.sol', () => { blacklistedMockDai = await MockDai.deploy(); await blacklistedMockDai.deployed(); - mockNft = await MockNft.deploy(); - await mockNft.deployed(); - - openQTokenWhitelist = await OpenQTokenWhitelist.deploy(5); + openQTokenWhitelist = await OpenQTokenWhitelist.deploy(); await openQTokenWhitelist.deployed(); await openQTokenWhitelist.addToken(mockLink.address); await openQTokenWhitelist.addToken(mockDai.address); await openQTokenWhitelist.addToken(ethers.constants.AddressZero); - await openQTokenWhitelist.addToken(mockNft.address); - - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); - await mockNft.safeMint(owner.address); // BOUNTY BEACONS atomicBountyBeacon = await BountyBeacon.deploy(atomicBountyV1.address); await atomicBountyBeacon.deployed(); - ongoingBountyBeacon = await BountyBeacon.deploy(ongoingBountyV1.address); - await ongoingBountyBeacon.deployed(); - - tieredPercentageBountyBeacon = await BountyBeacon.deploy(tieredPercentageBountyV1.address); - await tieredPercentageBountyBeacon.deployed(); - tieredFixedBountyBeacon = await BountyBeacon.deploy(tieredFixedBountyV1.address); await tieredFixedBountyBeacon.deployed(); @@ -146,8 +111,6 @@ describe('DepositManager.sol', () => { bountyFactory = await BountyFactory.deploy( openQProxy.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, tieredFixedBountyBeacon.address ); await bountyFactory.deployed(); @@ -180,13 +143,9 @@ describe('DepositManager.sol', () => { funderUuidEncoded = abiCoder.encode(["string"], [Constants.funderUuid]); atomicBountyInitOperation = atomicBountyInitOperation_fundingGoal(mockLink.address) - ongoingBountyInitOperation = ongoingBountyInitOperationBuilder(mockLink.address) - tieredPercentageBountyInitOperation = tieredBountyInitOperationBuilder(mockLink.address) - tieredFixedBountyInitOperation = tieredFixedBountyInitOperationBuilder(mockLink.address) - tieredPercentageBountyInitOperation_permissionless = tieredBountyInitOperationBuilder_permissionless(mockLink.address) + tieredFixedBountyInitOperation = tieredFixedBountyInitOperationBuilder_permissionless(mockLink.address) abiEncodedSingleCloserData = abiCoder.encode(['address', 'string', 'address', 'string'], [owner.address, "FlacoJones", owner.address, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398"]); - abiEncodedOngoingCloserData = abiCoder.encode(['address', 'string', 'address', 'string'], [owner.address, "FlacoJones", owner.address, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398"]); abiEncodedTieredCloserData = abiCoder.encode(['address', 'string', 'address', 'string', 'uint256'], [owner.address, "FlacoJones", owner.address, "https://github.com/OpenQDev/OpenQ-Frontend/pull/398", 1]); }); @@ -222,7 +181,7 @@ describe('DepositManager.sol', () => { // ARRANGE const OpenQTokenWhitelist = await ethers.getContractFactory('OpenQTokenWhitelist'); - const openQTokenWhitelist = await OpenQTokenWhitelist.deploy(20); + const openQTokenWhitelist = await OpenQTokenWhitelist.deploy(); await openQTokenWhitelist.deployed(); // ACT @@ -234,34 +193,7 @@ describe('DepositManager.sol', () => { }); describe('fundBounty', () => { - it('should revert if bounty is already closed', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedSingleCloserData); - - await mockLink.approve(bountyAddress, 10000000); - - // ACT + ASSERT; - await expect(depositManager.fundBountyToken(bountyAddress, mockLink.address, 10000000, 1, Constants.funderUuid)).to.be.revertedWith('CONTRACT_ALREADY_CLOSED'); - }); - - it('should revert if tiered bounty is already closed', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, tieredPercentageBountyInitOperation_permissionless); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await claimManager.connect(oracle).claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData); - - await mockLink.approve(bountyAddress, 10000000); - await mockDai.approve(bountyAddress, 10000000); - - // ACT + ASSERT - await expect(depositManager.fundBountyToken(bountyAddress, mockLink.address, 10000000, 1, Constants.funderUuid)).to.be.revertedWith('CONTRACT_ALREADY_CLOSED'); - }); - - it('should revert if funded with a non-whitelisted token and bounty is at funded token address capacity', async () => { + it('should revert if funded with a non-whitelisted token', async () => { // ARRANGE await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); @@ -270,31 +202,10 @@ describe('DepositManager.sol', () => { await blacklistedMockDai.approve(bountyAddress, 10000000); await mockLink.approve(bountyAddress, 10000000); - // set lower capacity for token - await openQTokenWhitelist.setTokenAddressLimit(1); - await depositManager.fundBountyToken(bountyAddress, mockLink.address, 10000000, 1, Constants.funderUuid); // ACT + ASSERT - await expect(depositManager.fundBountyToken(bountyAddress, blacklistedMockDai.address, 10000000, 1, Constants.funderUuid)).to.be.revertedWith('TOO_MANY_TOKEN_ADDRESSES'); - }); - - it('should ALLOW funding with whitelisted token EVEN IF bounty is at funded token address capacity', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - - await mockDai.approve(bountyAddress, 10000000); - await mockLink.approve(bountyAddress, 10000000); - - // set lower capacity for token - await openQTokenWhitelist.setTokenAddressLimit(1); - - await depositManager.fundBountyToken(bountyAddress, mockLink.address, 10000000, 1, Constants.funderUuid); - - // ACT + ASSERT - await expect(depositManager.fundBountyToken(bountyAddress, mockDai.address, 10000000, 1, Constants.funderUuid)).to.not.be.revertedWith('TOO_MANY_TOKEN_ADDRESSES'); + await expect(depositManager.fundBountyToken(bountyAddress, blacklistedMockDai.address, 10000000, 1, Constants.funderUuid)).to.be.revertedWith('TOKEN_NOT_ACCEPTED'); }); it('should set funder to msg.sender', async () => { @@ -351,40 +262,6 @@ describe('DepositManager.sol', () => { expect(bountyFakeTokenBalance).to.equal('100'); }); - it('should transfer NFT from sender to bounty', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - await mockNft.approve(bountyAddress, 1); - - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - - // ACT - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, 1, 1, 0); - - // ASSERT - expect(await mockNft.ownerOf(1)).to.equal(bountyAddress); - }); - - it('should emit an NFTDepositReceived event', async () => { - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - await mockNft.approve(bountyAddress, 1); - - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - - const depositId = generateDepositId(Constants.bountyId, 0); - const expectedTimestamp = await setNextBlockTimestamp(); - - // ACT/ASSERT - await expect(depositManager.fundBountyNFT(bountyAddress, mockNft.address, 1, 1, 0)) - .to.emit(depositManager, 'NFTDepositReceived') - .withArgs(depositId, bountyAddress, Constants.bountyId, Constants.organization, mockNft.address, expectedTimestamp, owner.address, 1, 1, 0, [], Constants.VERSION_1); - }); - it('should emit a DepositReceived event with expected bountyId, bounty address, token address, funder, volume, timestamp, depositId, tokenStandard, tokenId, bountyType and data', async () => { // ARRANGE await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); @@ -544,30 +421,6 @@ describe('DepositManager.sol', () => { expect(newFunderMockTokenBalance).to.equal('10000000000000000000000'); expect(newFunderFakeTokenBalance).to.equal('10000000000000000000000'); }); - - it('should transfer NFT from bounty contract to funder', async () => { - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - - // ARRANGE - await openQProxy.mintBounty(Constants.bountyId, Constants.organization, atomicBountyInitOperation); - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId); - const bounty = await AtomicBountyV1.attach(bountyAddress); - - await mockNft.approve(bountyAddress, 1); - - const depositId = generateDepositId(Constants.bountyId, 0); - await depositManager.fundBountyNFT(bountyAddress, mockNft.address, 1, 1, 0); - - // ASSUME - expect(await mockNft.ownerOf(1)).to.equal(bountyAddress); - - // ACT - await depositManager.refundDeposit(bountyAddress, depositId); - - // ASSERT - expect(await mockNft.ownerOf(1)).to.equal(owner.address); - }); }); }); diff --git a/test/OpenQ.test.js b/test/OpenQ.test.js index 77539439..a2ee12c3 100755 --- a/test/OpenQ.test.js +++ b/test/OpenQ.test.js @@ -12,11 +12,8 @@ const { atomicBountyInitOperation_fundingGoal, atomicBountyInitOperation_noFundingGoal, atomicBountyInitOperation_permissioned, - ongoingBountyInitOperationBuilder, - tieredBountyInitOperationBuilder, tieredFixedBountyInitOperationBuilder, - tieredBountyInitOperation_not100, - tieredBountyInitOperationBuilder_permissionless + tieredFixedBountyInitOperationBuilder_permissionless, } = require('./constants'); describe('OpenQ.sol', () => { @@ -26,7 +23,6 @@ describe('OpenQ.sol', () => { let mockLink let mockDai let blacklistedMockDai - let mockNft let openQTokenWhitelist let depositManager let claimManager @@ -40,10 +36,6 @@ describe('OpenQ.sol', () => { // INIT OPERATIONS let atomicBountyInitOperation let atomicBountyInitOperationPermissioned - let ongoingBountyInitOperation - let tieredBountyInitOperation - let tieredBountyInitOperation_permissionless - let tieredBountyInitOperationNot100 let tieredFixedBountyInitOperation // CLOSER DATA @@ -51,12 +43,9 @@ describe('OpenQ.sol', () => { let abiEncodedSingleCloserData let abiEncodedOngoingCloserData - let abiEncodedTieredCloserData let abiEncodedTieredFixedCloserData let AtomicBountyV1 - let OngoingBountyV1 - let TieredPercentageBountyV1 let TieredFixedBountyV1 beforeEach(async () => { @@ -64,7 +53,6 @@ describe('OpenQ.sol', () => { const OpenQProxy = await ethers.getContractFactory('OpenQProxy') const MockLink = await ethers.getContractFactory('MockLink') const MockDai = await ethers.getContractFactory('MockDai') - const MockNft = await ethers.getContractFactory('MockNft') const OpenQTokenWhitelist = await ethers.getContractFactory( 'OpenQTokenWhitelist' ) @@ -72,22 +60,12 @@ describe('OpenQ.sol', () => { const ClaimManager = await ethers.getContractFactory('ClaimManagerV1') AtomicBountyV1 = await ethers.getContractFactory('AtomicBountyV1') - OngoingBountyV1 = await ethers.getContractFactory('OngoingBountyV1') - TieredPercentageBountyV1 = await ethers.getContractFactory( - 'TieredPercentageBountyV1' - ) TieredFixedBountyV1 = await ethers.getContractFactory('TieredFixedBountyV1') // BOUNTY IMPLEMENTATIONS atomicBountyV1 = await AtomicBountyV1.deploy() await atomicBountyV1.deployed() - ongoingBountyV1 = await OngoingBountyV1.deploy() - await ongoingBountyV1.deployed() - - tieredPercentageBountyV1 = await TieredPercentageBountyV1.deploy() - await tieredPercentageBountyV1.deployed() - tieredFixedBountyV1 = await TieredFixedBountyV1.deploy() await tieredFixedBountyV1.deployed() @@ -144,34 +122,17 @@ describe('OpenQ.sol', () => { blacklistedMockDai = await MockDai.deploy() await blacklistedMockDai.deployed() - mockNft = await MockNft.deploy() - await mockNft.deployed() - - openQTokenWhitelist = await OpenQTokenWhitelist.deploy(5) + openQTokenWhitelist = await OpenQTokenWhitelist.deploy() await openQTokenWhitelist.deployed() await openQTokenWhitelist.addToken(mockLink.address) await openQTokenWhitelist.addToken(mockDai.address) await openQTokenWhitelist.addToken(ethers.constants.AddressZero) - await openQTokenWhitelist.addToken(mockNft.address) - - await mockNft.safeMint(owner.address) - await mockNft.safeMint(owner.address) - await mockNft.safeMint(owner.address) - await mockNft.safeMint(owner.address) // BOUNTY BEACONS atomicBountyBeacon = await BountyBeacon.deploy(atomicBountyV1.address) await atomicBountyBeacon.deployed() - ongoingBountyBeacon = await BountyBeacon.deploy(ongoingBountyV1.address) - await ongoingBountyBeacon.deployed() - - tieredPercentageBountyBeacon = await BountyBeacon.deploy( - tieredPercentageBountyV1.address - ) - await tieredPercentageBountyBeacon.deployed() - tieredFixedBountyBeacon = await BountyBeacon.deploy( tieredFixedBountyV1.address ) @@ -181,8 +142,6 @@ describe('OpenQ.sol', () => { bountyFactory = await BountyFactory.deploy( openQProxy.address, atomicBountyBeacon.address, - ongoingBountyBeacon.address, - tieredPercentageBountyBeacon.address, tieredFixedBountyBeacon.address ) await bountyFactory.deployed() @@ -196,11 +155,8 @@ describe('OpenQ.sol', () => { abiCoder = new ethers.utils.AbiCoder() atomicBountyInitOperation = atomicBountyInitOperation_fundingGoal(mockLink.address) - ongoingBountyInitOperation = ongoingBountyInitOperationBuilder(mockLink.address) - tieredBountyInitOperation = tieredBountyInitOperationBuilder(mockLink.address) - tieredBountyInitOperationNot100 = tieredBountyInitOperation_not100(mockLink.address) tieredFixedBountyInitOperation = tieredFixedBountyInitOperationBuilder(mockLink.address) - tieredBountyInitOperation_permissionless = tieredBountyInitOperationBuilder_permissionless(mockLink.address) + tieredFixedBountyInitOperation_permissionless = tieredFixedBountyInitOperationBuilder_permissionless(mockLink.address) abiEncodedSingleCloserData = abiCoder.encode( ['address', 'string', 'address', 'string'], @@ -211,25 +167,7 @@ describe('OpenQ.sol', () => { 'https://github.com/OpenQDev/OpenQ-Frontend/pull/398', ] ) - abiEncodedOngoingCloserData = abiCoder.encode( - ['address', 'string', 'address', 'string'], - [ - owner.address, - 'FlacoJones', - owner.address, - 'https://github.com/OpenQDev/OpenQ-Frontend/pull/398', - ] - ) - abiEncodedTieredCloserData = abiCoder.encode( - ['address', 'string', 'address', 'string', 'uint256'], - [ - owner.address, - 'FlacoJones', - owner.address, - 'https://github.com/OpenQDev/OpenQ-Frontend/pull/398', - 1, - ] - ) + abiEncodedTieredFixedCloserData = abiCoder.encode( ['address', 'string', 'address', 'string', 'uint256'], [ @@ -376,97 +314,6 @@ describe('OpenQ.sol', () => { }) }) - describe('ONGOING', () => { - it('should deploy a new bounty contract with expected initial metadata', async () => { - // ACT - let initializationTimestamp = await setNextBlockTimestamp() - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - - const bountyIsOpen = await openQProxy.bountyIsOpen(Constants.bountyId) - - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) - const ongoingContract = await OngoingBountyV1.attach(bountyAddress) - - await expect(await ongoingContract.bountyId()).equals(Constants.bountyId) - await expect(await ongoingContract.issuer()).equals(owner.address) - await expect(await ongoingContract.organization()).equals(Constants.organization) - await expect(await ongoingContract.status()).equals(0) - await expect(await ongoingContract.openQ()).equals(openQProxy.address) - await expect(await ongoingContract.claimManager()).equals(claimManager.address) - await expect(await ongoingContract.depositManager()).equals(depositManager.address) - await expect(await ongoingContract.bountyCreatedTime()).equals(initializationTimestamp) - await expect(await ongoingContract.bountyType()).equals(Constants.ONGOING_CONTRACT) - await expect(await ongoingContract.hasFundingGoal()).equals(true) - await expect(await ongoingContract.fundingToken()).equals(mockLink.address) - await expect(await ongoingContract.payoutTokenAddress()).equals(mockLink.address) - await expect(await ongoingContract.payoutVolume()).equals(Constants.volume) - await expect(await ongoingContract.fundingGoal()).equals(Constants.volume) - await expect(await ongoingContract.issuerExternalUserId()).equals(Constants.mockOpenQId) - await expect(await ongoingContract.invoiceRequired()).equals(false) - await expect(await ongoingContract.kycRequired()).equals(false) - await expect(await ongoingContract.supportingDocumentsRequired()).equals(false) - }) - }) - - describe('TIERED', () => { - it('should correctly init bountyType and payout schedule', async () => { - // ARRANGE - let initializationTimestamp = await setNextBlockTimestamp() - - // ACT - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - tieredBountyInitOperation - ) - - const bountyIsOpen = await openQProxy.bountyIsOpen(Constants.bountyId) - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) - - const tieredPercentageContract = await TieredPercentageBountyV1.attach(bountyAddress) - - const actualBountyPayoutSchedule = await tieredPercentageContract.getPayoutSchedule() - const payoutToString = actualBountyPayoutSchedule.map((thing) => thing.toString()) - - await expect(await tieredPercentageContract.bountyId()).equals(Constants.bountyId) - await expect(await tieredPercentageContract.issuer()).equals(owner.address) - await expect(await tieredPercentageContract.organization()).equals(Constants.organization) - await expect(await tieredPercentageContract.status()).equals(0) - await expect(await tieredPercentageContract.openQ()).equals(openQProxy.address) - await expect(await tieredPercentageContract.claimManager()).equals(claimManager.address) - await expect(await tieredPercentageContract.depositManager()).equals(depositManager.address) - await expect(await tieredPercentageContract.bountyCreatedTime()).equals(initializationTimestamp) - await expect(await tieredPercentageContract.bountyType()).equals(Constants.TIERED_PERCENTAGE_CONTRACT) - await expect(await tieredPercentageContract.hasFundingGoal()).equals(true) - await expect(await tieredPercentageContract.fundingToken()).equals(mockLink.address) - await expect(await tieredPercentageContract.fundingGoal()).equals(Constants.volume) - await expect(payoutToString[0]).equals('60') - await expect(payoutToString[1]).equals('30') - await expect(await tieredPercentageContract.issuerExternalUserId()).equals(Constants.mockOpenQId) - - await expect(await tieredPercentageContract.supportingDocumentsRequired()).equals(true) - await expect(await tieredPercentageContract.invoiceRequired()).equals(true) - await expect(await tieredPercentageContract.kycRequired()).equals(true) - - await expect(await tieredPercentageContract.invoiceComplete(0)).equals(false) - await expect(await tieredPercentageContract.supportingDocumentsComplete(0)).equals(false) - }) - - it('should revert if payout schedule does not add to 100', async () => { - await expect( - openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - tieredBountyInitOperationNot100 - ) - ).to.be.revertedWith('PAYOUT_SCHEDULE_MUST_ADD_TO_100') - }) - }) - describe('TIERED FIXED', () => { it('should correctly init bountyType and payout schedule', async () => { // ARRANGE @@ -511,184 +358,18 @@ describe('OpenQ.sol', () => { }) }) - describe('solvent', () => { - it('should return TRUE for solvent ongoing contracts', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - await mockLink.approve(bountyAddress, 10000000) - await depositManager.fundBountyToken( - bountyAddress, - mockLink.address, - 10000000, - 1, - Constants.funderUuid - ) - - const solvent = await openQProxy.solvent(Constants.bountyId) - expect(solvent).to.equal(true) - }) - - it('should return FALSE for insolvent ongoing contracts', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - const solvent = await openQProxy.solvent(Constants.bountyId) - expect(solvent).to.equal(false) - }) - }) - - describe('closeOngoing', () => { - it('should close ongoing', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - // ASSUME - let status = await bounty.status() - expect(status).to.equal(0) - - // ACT - await openQProxy.closeOngoing(Constants.bountyId) - - // ASSERT - status = await bounty.status() - expect(status).to.equal(1) - }) - - it('should revert if not ongoing', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - atomicBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await AtomicBountyV1.attach(bountyAddress) - - // ASSUME - let status = await bounty.status() - expect(status).to.equal(0) - - // ASSERT - await expect( - openQProxy.closeOngoing(Constants.bountyId) - ).to.be.revertedWith('NOT_AN_ONGOING_CONTRACT') - }) - - it('should revert if already closed', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - // ASSUME - let status = await bounty.status() - expect(status).to.equal(0) - await openQProxy.closeOngoing(Constants.bountyId) - - // ASSERT - await expect( - openQProxy.closeOngoing(Constants.bountyId) - ).to.be.revertedWith('CONTRACT_ALREADY_CLOSED') - }) - - it('should revert if caller not issuer', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - // ASSUME - let status = await bounty.status() - expect(status).to.equal(0) - - // ASSERT - await expect( - openQProxy.connect(notIssuer).closeOngoing(Constants.bountyId) - ).to.be.revertedWith('CALLER_NOT_ISSUER') - }) - - it('should emit BountyClosed', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - // ASSUME - let status = await bounty.status() - expect(status).to.equal(0) - - const expectedTimestamp = await setNextBlockTimestamp() - // ACT - await expect(openQProxy.closeOngoing(Constants.bountyId)) - .to.emit(openQProxy, 'BountyClosed') - .withArgs( - Constants.bountyId, - anyValue, - Constants.organization, - ethers.constants.AddressZero, - expectedTimestamp, - 1, - [], - Constants.VERSION_1 - ) - }) - }) - describe('tierClaimed', () => { it('should return FALSE if tier not claimed, TRUE if already claimed', async () => { // ARRANGE await openQProxy.mintBounty( Constants.bountyId, Constants.organization, - tieredBountyInitOperation_permissionless + tieredFixedBountyInitOperation_permissionless ) const bountyAddress = await openQProxy.bountyIdToAddress( Constants.bountyId ) - const bounty = await TieredPercentageBountyV1.attach(bountyAddress) + const bounty = await TieredFixedBountyV1.attach(bountyAddress) await mockLink.approve(bountyAddress, 10000000) await depositManager.fundBountyToken( @@ -707,7 +388,7 @@ describe('OpenQ.sol', () => { await claimManager .connect(oracle) - .claimBounty(bountyAddress, owner.address, abiEncodedTieredCloserData) + .claimBounty(bountyAddress, owner.address, abiEncodedTieredFixedCloserData) // ASSERT tierClaimed = await openQProxy.tierClaimed(Constants.bountyId, 1) @@ -715,49 +396,6 @@ describe('OpenQ.sol', () => { }) }) - describe('ongoingClaimed', () => { - it('should return FALSE if ongoing claimant is not claimed, TRUE if it is claimed', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - await mockLink.approve(bountyAddress, 10000000) - await depositManager.fundBountyToken( - bountyAddress, - mockLink.address, - 10000000, - 1, - Constants.funderUuid - ) - - let claimId = generateClaimantId( - 'FlacoJones', - 'https://github.com/OpenQDev/OpenQ-Frontend/pull/398' - ) - - // ASSUME - let ongoingClaimed = await bounty.claimId(claimId) - expect(ongoingClaimed).to.equal(false) - - // ACT - - await claimManager - .connect(oracle) - .claimBounty(bountyAddress, owner.address, abiEncodedOngoingCloserData) - - // // ASSERT - ongoingClaimed = await bounty.claimId(claimId) - expect(ongoingClaimed).to.equal(true) - }) - }) - describe('setFundingGoal', () => { it('should set funding goal', async () => { // ARRANGE @@ -1171,46 +809,6 @@ const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) ) }) }) - - describe('ONGOING', () => { - it('should emit an SupportingDocumentsCompleteSet event with a boolean of supportingDocumentsComplete', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - atomicBountyInitOperation - ) - const bountyAddress = await openQProxy.bountyIdToAddress( - Constants.bountyId - ) - const bounty = await AtomicBountyV1.attach(bountyAddress) - - let setSupportingDocumentsCompleteData_1 = abiCoder.encode( - ['bool'], - [true] - ) - - const supportingDocumentsCompleteArrayData = abiCoder.encode( - ['bool'], - [true] - ) - - // ACT/ASSERT - await expect( - await openQProxy.setSupportingDocumentsComplete( - Constants.bountyId, - setSupportingDocumentsCompleteData_1 - ) - ) - .to.emit(openQProxy, 'SupportingDocumentsCompleteSet') - .withArgs( - bountyAddress, - Constants.ATOMIC_CONTRACT, - supportingDocumentsCompleteArrayData, - Constants.VERSION_1 - ) - }) - }) }) describe('setInvoiceComplete', () => { @@ -1436,146 +1034,6 @@ const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) }) }) - describe('setPayout', () => { - it('should set payout', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) -const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - // ACT - await openQProxy.setPayout(Constants.bountyId, mockDai.address, Constants.volume) - - // ASSERT - let payoutTokenAddress = await bounty.payoutTokenAddress() - let payoutVolume = await bounty.payoutVolume() - - expect(payoutTokenAddress).to.equal(mockDai.address) - expect(payoutVolume).to.equal(Constants.volume) - }) - - it('should emit a PayoutSet event', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) -const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) - const bounty = await OngoingBountyV1.attach(bountyAddress) - - // ACT/ASSERT - await expect( - await openQProxy.setPayout(Constants.bountyId, mockDai.address, Constants.volume) - ) - .to.emit(openQProxy, 'PayoutSet') - .withArgs( - bountyAddress, - mockDai.address, - Constants.volume, - Constants.ONGOING_CONTRACT, - [], - Constants.VERSION_1 - ) - }) - - it('should revert if not called by issuer', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - ongoingBountyInitOperation - ) - const notOwnerContract = openQProxy.connect(oracle) - - // ACT/ASSERT - await expect( - notOwnerContract.setPayout(Constants.bountyId, mockDai.address, Constants.volume) - ).to.be.revertedWith('CALLER_NOT_ISSUER') - }) - }) - - describe('setPayoutSchedule', () => { - it('should revert if payout schedule does not add to 100', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - tieredBountyInitOperation - ) - // ACT/ASSERT - await expect( - openQProxy.setPayoutSchedule(Constants.bountyId, [20, 23, 43]) - ).to.be.revertedWith('PAYOUT_SCHEDULE_MUST_ADD_TO_100') - }) - - it('should revert if caller is not issuer', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - tieredBountyInitOperation - ) - // ACT/ASSERT - await expect( - openQProxy - .connect(notIssuer) - .setPayoutSchedule(Constants.bountyId, [20, 23, 43]) - ).to.be.revertedWith('CALLER_NOT_ISSUER') - }) - - it('should set payout schedule', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - tieredBountyInitOperation - ) -const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) - const bounty = await TieredPercentageBountyV1.attach(bountyAddress) - - // ACT - await openQProxy.setPayoutSchedule(Constants.bountyId, [70, 20, 10]) - - // ASSERT - const payoutSchedule = await bounty.getPayoutSchedule() - const payoutToString = payoutSchedule.map((thing) => - parseInt(thing.toString()) - ) - expect(payoutToString[0]).to.equal(70) - expect(payoutToString[1]).to.equal(20) - expect(payoutToString[2]).to.equal(10) - }) - - it('should emit PayoutScheduleSet event', async () => { - // ARRANGE - await openQProxy.mintBounty( - Constants.bountyId, - Constants.organization, - tieredBountyInitOperation - ) -const bountyAddress = await openQProxy.bountyIdToAddress(Constants.bountyId) - const bounty = await TieredPercentageBountyV1.attach(bountyAddress) - - await expect( - openQProxy.setPayoutSchedule(Constants.bountyId, [70, 20, 10]) - ) - .to.emit(openQProxy, 'PayoutScheduleSet') - .withArgs( - bountyAddress, - ethers.constants.AddressZero, - [70, 20, 10], - 2, - [], - Constants.VERSION_1 - ) - }) - }) - describe('setPayoutScheduleFixed', () => { it('should revert if caller is not issuer', async () => { // ARRANGE diff --git a/test/OpenQTokenWhitelist.test.js b/test/OpenQTokenWhitelist.test.js index af8e8d0f..9a31ed32 100755 --- a/test/OpenQTokenWhitelist.test.js +++ b/test/OpenQTokenWhitelist.test.js @@ -20,7 +20,7 @@ describe('OpenQTokenWhitelist.sol', () => { [owner, notOwner] = await ethers.getSigners(); - openQTokenWhitelist = await OpenQTokenWhitelist.deploy(2); + openQTokenWhitelist = await OpenQTokenWhitelist.deploy(); await openQTokenWhitelist.deployed(); mockLink = await MockLink.deploy(); @@ -31,11 +31,6 @@ describe('OpenQTokenWhitelist.sol', () => { }); describe('OpenQTokenWhitelist methods', () => { - it('initializes', async () => { - const totalTokenAddresses = await openQTokenWhitelist.TOKEN_ADDRESS_LIMIT(); - expect(totalTokenAddresses).to.equal(2); - }); - it('can add token', async () => { // ASSUME let mockLinkWhitelist = await openQTokenWhitelist.isWhitelisted(mockLink.address); @@ -89,19 +84,6 @@ describe('OpenQTokenWhitelist.sol', () => { expect(tokenCount).to.equal(0); }); - it('increases token address limit', async () => { - // ASSUME - let tokenAddressLimit = await openQTokenWhitelist.TOKEN_ADDRESS_LIMIT(); - expect(tokenAddressLimit).to.equal(2); - - // ACT - await openQTokenWhitelist.setTokenAddressLimit(5); - - // ASSERT - tokenAddressLimit = await openQTokenWhitelist.TOKEN_ADDRESS_LIMIT(); - expect(tokenAddressLimit).to.equal(5); - }); - it('add and remove token is idempotent', async () => { // ARRANGE await openQTokenWhitelist.addToken(mockLink.address); @@ -116,7 +98,6 @@ describe('OpenQTokenWhitelist.sol', () => { // ACT/ASSERT await expect(openQTokenWhitelist.connect(notOwner).addToken(mockLink.address)).to.revertedWith('Ownable: caller is not the owner'); await expect(openQTokenWhitelist.connect(notOwner).removeToken(mockLink.address)).to.revertedWith('Ownable: caller is not the owner'); - await expect(openQTokenWhitelist.connect(notOwner).setTokenAddressLimit(5)).to.revertedWith('Ownable: caller is not the owner'); }); }); }); \ No newline at end of file diff --git a/test/constants.js b/test/constants.js index 8ad7df77..cd09f41f 100755 --- a/test/constants.js +++ b/test/constants.js @@ -115,8 +115,8 @@ const tieredFixedBountyInitOperationBuilder_permissionless = (tokenAddress) => { ['uint256[]','address','bool','bool','bool','string','string','string'], [[80, 20],tokenAddress,false,false,false,Constants.mockOpenQId,Constants.alternativeName,Constants.alternativeLogo] ); - const tieredPercentageBountyInitOperationComplete = [Constants.TIERED_FIXED_CONTRACT, abiEncodedParamsTieredFixedBounty]; - return tieredPercentageBountyInitOperationComplete; + const tieredFixedBountyInitOperationComplete = [Constants.TIERED_FIXED_CONTRACT, abiEncodedParamsTieredFixedBounty]; + return tieredFixedBountyInitOperationComplete; }; const setInvoiceCompleteData_tiered = (tier, invoiceComplete) => {