This repository has been archived by the owner on May 26, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathAtomicBountyV1.sol
executable file
·201 lines (179 loc) · 7.56 KB
/
AtomicBountyV1.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import '../Storage/AtomicBountyStorage.sol';
/// @title AtomicBountyV1
/// @author FlacoJones
/// @notice Bounty implementation for single contributor, single payout scenarios (e.g. 500 USDC for a single work completion)
/// @dev AtomicBountyV1 -> AtomicBountyStorageV1 -> BountyCore -> BountyStorageCore -> Core Dependencies (OZ + Custom)
/// @dev Do not add any new storage variables here. Put them in a AtomicBountyStorageV# and release new implementation
contract AtomicBountyV1 is AtomicBountyStorageV1 {
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 ATOMIC
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;
(
bool _hasFundingGoal,
address _fundingToken,
uint256 _fundingGoal,
bool _invoiceRequired,
bool _kycRequired,
bool _supportingDocumentsRequired,
string memory _issuerExternalUserId,
,
) = abi.decode(
_operation.data,
(
bool,
address,
uint256,
bool,
bool,
bool,
string,
string,
string
)
);
bountyType = OpenQDefinitions.ATOMIC;
hasFundingGoal = _hasFundingGoal;
fundingToken = _fundingToken;
fundingGoal = _fundingGoal;
invoiceRequired = _invoiceRequired;
kycRequired = _kycRequired;
supportingDocumentsRequired = _supportingDocumentsRequired;
issuerExternalUserId = _issuerExternalUserId;
}
/// @notice Transfers full balance of _tokenAddress from bounty to _payoutAddress
/// @param _tokenAddress ERC20 token address or Zero Address for protocol token
/// @param _payoutAddress The destination address for the funds
function claimBalance(address _payoutAddress, address _tokenAddress)
external
onlyClaimManager
nonReentrant
returns (uint256)
{
uint256 claimedBalance = getTokenBalance(_tokenAddress);
_transferToken(_tokenAddress, claimedBalance, _payoutAddress);
return claimedBalance;
}
/// @notice Changes bounty status from 0 (OPEN) to 1 (CLOSED)
/// @param _payoutAddress The closer of the bounty
/// @param _closerData ABI-encoded data about the claimant and claimant asset (see IBountyAtomic for data spec)
/// @dev See IAtomicBounty.close.(_closerData)for _closerData ABI encoding schema
function close(address _payoutAddress, bytes calldata _closerData)
external
onlyClaimManager
{
require(
status == OpenQDefinitions.OPEN,
Errors.CONTRACT_ALREADY_CLOSED
);
require(_payoutAddress != address(0), Errors.NO_ZERO_ADDRESS);
status = OpenQDefinitions.CLOSED;
closer = _payoutAddress;
bountyClosedTime = block.timestamp;
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
function setInvoiceComplete(bytes calldata _data) external onlyOpenQ {
bool _invoiceComplete = abi.decode(_data, (bool));
invoiceComplete = _invoiceComplete;
}
/// @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
{
bool _supportingDocumentsComplete = abi.decode(_data, (bool));
supportingDocumentsComplete = _supportingDocumentsComplete;
}
/// @notice Returns whether or not invoice is completed
/// @return True if completed, false otherwise
/// @dev We return from all getInvoiceComplete as bytes to accomodate different return types
/// @dev _data (bool)
/// @dev _data (invoiceComplete)
function getInvoiceComplete() external view returns (bytes memory) {
return abi.encode(invoiceComplete);
}
/// @notice Returns whether or not supporting documents have been completed
/// @return True if completed, false otherwise
/// @dev We return from all IBountyCore.getSupportingDocumentsComplete() as bytes to accomodate different return types
/// @dev _data (bool)
/// @dev _data (supportingDocumentsComplete)
function getSupportingDocumentsComplete()
external
view
returns (bytes memory)
{
return abi.encode(supportingDocumentsComplete);
}
/// @notice receive() method to accept protocol tokens
receive() external payable {
revert(
'Cannot send Ether directly to boutny contract. Please use the BountyV1.receiveFunds() method.'
);
}
}