-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathOrg.sol
168 lines (145 loc) · 5.79 KB
/
Org.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
// SPDX-License-Identifier: BSD 3-Clause
pragma solidity ^0.6.10;
pragma experimental ABIEncoderV2;
import "./Administratable.sol";
import "./interfaces/IFactory.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";
//ORG CONTRACT
/**
* @title Org
* @author rheeger
* @notice Org is a contract that serves as a smart wallet for US nonprofit
* organizations. It holds the organization's federal Tax ID number as taxID,
* and allows for an address to submit a Claim struct to the contract whereby
* the organization can directly receive grant awards from Endaoment Funds.
*/
contract Org is Initializable, Administratable {
using SafeERC20 for IERC20;
// ========== STRUCTS & EVENTS ==========
struct Claim {
string firstName;
string lastName;
string eMail;
address desiredWallet;
}
event CashOutComplete(uint256 cashOutAmount);
event ClaimCreated(string claimId, Claim claim);
event ClaimApproved(string claimId, Claim claim);
event ClaimRejected(string claimId, Claim claim);
// ========== STATE VARIABLES ==========
IFactory public orgFactoryContract;
uint256 public taxId;
mapping(string => Claim) public pendingClaims; // claim UUID to Claim
Claim public activeClaim;
// ========== CONSTRUCTOR ==========
/**
* @notice Create new Organization Contract
* @dev Using initializer instead of constructor for minimal proxy support. This function
* can only be called once in the contract's lifetime
* @param ein The U.S. Tax Identification Number for the Organization
* @param orgFactory Address of the Factory contract.
*/
function initializeOrg(uint256 ein, address orgFactory) public initializer {
require(orgFactory != address(0), "Org: Factory cannot be null address.");
taxId = ein;
orgFactoryContract = IFactory(orgFactory);
}
// ========== Org Management & Info ==========
/**
* @notice Creates Organization Claim and emits a `ClaimCreated` event
* @param claimId UUID representing this claim
* @param fName First name of Administrator
* @param lName Last name of Administrator
* @param eMail Email contact for Organization Administrator.
* @param orgAdminWalletAddress Wallet address of Organization's Administrator.
*/
function claimRequest(
string calldata claimId,
string calldata fName,
string calldata lName,
string calldata eMail,
address orgAdminWalletAddress
) public {
require(!isEqual(claimId, ""), "Org: Must provide claimId");
require(!isEqual(fName, ""), "Org: Must provide the first name of the administrator");
require(!isEqual(lName, ""), "Org: Must provide the last name of the administrator");
require(!isEqual(eMail, ""), "Org: Must provide the email address of the administrator");
require(orgAdminWalletAddress != address(0), "Org: Wallet address cannot be the zero address");
require(
pendingClaims[claimId].desiredWallet == address(0),
"Org: Pending Claim with Id already exists"
);
Claim memory newClaim = Claim({
firstName: fName,
lastName: lName,
eMail: eMail,
desiredWallet: orgAdminWalletAddress
});
emit ClaimCreated(claimId, newClaim);
pendingClaims[claimId] = newClaim;
}
/**
* @notice Approves an Organization Claim and emits a `ClaimApproved` event
* @param claimId UUID of the claim being approved
*/
function approveClaim(string calldata claimId)
public
onlyAdminOrRole(orgFactoryContract.endaomentAdmin(), IEndaomentAdmin.Role.REVIEWER)
{
require(!isEqual(claimId, ""), "Fund: Must provide a claimId");
Claim storage claim = pendingClaims[claimId];
require(claim.desiredWallet != address(0), "Org: claim does not exist");
emit ClaimApproved(claimId, claim);
activeClaim = claim;
delete pendingClaims[claimId];
}
/**
* @notice Rejects an Organization Claim and emits a 'ClaimRejected` event
* @param claimId UUID of the claim being rejected
*/
function rejectClaim(string calldata claimId)
public
onlyAdminOrRole(orgFactoryContract.endaomentAdmin(), IEndaomentAdmin.Role.REVIEWER)
{
require(!isEqual(claimId, ""), "Fund: Must provide a claimId");
Claim storage claim = pendingClaims[claimId];
require(claim.desiredWallet != address(0), "Org: claim does not exist");
emit ClaimRejected(claimId, claim);
delete pendingClaims[claimId];
}
/**
* @notice Cashes out Organization Contract and emits a `CashOutComplete` event
* @param tokenAddress ERC20 address of desired token withdrawal
*/
function cashOutOrg(address tokenAddress)
public
onlyAdminOrRole(orgFactoryContract.endaomentAdmin(), IEndaomentAdmin.Role.ACCOUNTANT)
{
require(tokenAddress != address(0), "Org: Token address cannot be the zero address");
address payoutAddr = orgWallet();
require(payoutAddr != address(0), "Org: Cannot cashout unclaimed Org");
IERC20 tokenContract = IERC20(tokenAddress);
uint256 cashOutAmount = tokenContract.balanceOf(address(this));
tokenContract.safeTransfer(orgWallet(), cashOutAmount);
emit CashOutComplete(cashOutAmount);
}
/**
* @notice Retrieves Token Balance of Org Contract
* @param tokenAddress Address of desired token to query for balance
* @return Balance of conract in token base unit of provided tokenAddress
*/
function getTokenBalance(address tokenAddress) external view returns (uint256) {
IERC20 tokenContract = IERC20(tokenAddress);
uint256 balance = tokenContract.balanceOf(address(this));
return balance;
}
/**
* @notice Org Wallet convenience accessor
* @return The wallet specified in the active, approved claim
*/
function orgWallet() public view returns (address) {
return activeClaim.desiredWallet;
}
}