Skip to content

Commit 780eeeb

Browse files
committed
WIP Supply cap hook
1 parent a78b40d commit 780eeeb

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
Copyright 2021 Set Labs Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
pragma solidity 0.6.10;
18+
pragma experimental ABIEncoderV2;
19+
20+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
21+
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
22+
import { AddressArrayUtils } from "../lib/AddressArrayUtils.sol";
23+
24+
import { IManagerIssuanceHook } from "../interfaces/IManagerIssuanceHook.sol";
25+
import { ISetToken } from "../interfaces/ISetToken.sol";
26+
27+
28+
/**
29+
* @title SupplyCapAllowedCallerIssuanceHook
30+
* @author Set Protocol
31+
*
32+
* Issuance hook that checks new issuances won't push SetToken totalSupply over supply cap and checks if caller is allowed
33+
*/
34+
contract SupplyCapAllowedCallerIssuanceHook is Ownable, IManagerIssuanceHook {
35+
using SafeMath for uint256;
36+
using AddressArrayUtils for address[];
37+
38+
/* ============ Events ============ */
39+
40+
event SupplyCapUpdated(uint256 _newCap);
41+
event CallerStatusUpdated(address indexed _caller, bool _status);
42+
event AnyoneCallableUpdated(bool indexed _status);
43+
44+
/* ============ State Variables ============ */
45+
46+
// Cap on totalSupply of Sets
47+
uint256 public supplyCap;
48+
49+
// Boolean indicating if anyone can call function
50+
bool public anyoneCallable;
51+
52+
// Mapping of addresses allowed to call function
53+
mapping(address => bool) public callAllowList;
54+
55+
/* ============ Constructor ============ */
56+
57+
/**
58+
* Constructor, overwrites owner and original supply cap.
59+
*
60+
* @param _initialOwner Owner address, overwrites Ownable logic which sets to deployer as default
61+
* @param _supplyCap Supply cap for Set (in wei of Set)
62+
*/
63+
constructor(
64+
address _initialOwner,
65+
uint256 _supplyCap
66+
)
67+
public
68+
{
69+
supplyCap = _supplyCap;
70+
71+
// Overwrite _owner param of Ownable contract
72+
transferOwnership(_initialOwner);
73+
}
74+
75+
/* ============ External Functions ============ */
76+
77+
/**
78+
* Adheres to IManagerIssuanceHook interface, and checks to make sure the current issue call won't push total supply over cap.
79+
*/
80+
function invokePreIssueHook(
81+
ISetToken _setToken,
82+
uint256 _issueQuantity,
83+
address _sender,
84+
address /*_to*/
85+
)
86+
external
87+
override
88+
{
89+
uint256 totalSupply = _setToken.totalSupply();
90+
require(totalSupply.add(_issueQuantity) <= supplyCap, "Supply cap exceeded");
91+
92+
_validateAllowedCaller(_sender);
93+
}
94+
95+
/**
96+
* Adheres to IManagerIssuanceHook interface
97+
*/
98+
function invokePreRedeemHook(
99+
ISetToken /* _setToken */,
100+
uint256 /* _redeemQuantity */,
101+
address _sender,
102+
address /* _to */
103+
)
104+
external
105+
override
106+
{
107+
_validateAllowedCaller(_sender);
108+
}
109+
110+
/**
111+
* ONLY OWNER: Updates supply cap
112+
*/
113+
function updateSupplyCap(uint256 _newCap) external onlyOwner {
114+
supplyCap = _newCap;
115+
SupplyCapUpdated(_newCap);
116+
}
117+
118+
/**
119+
* ONLY OWNER: Toggle ability for passed addresses to call only allowed caller functions
120+
*
121+
* @param _callers Array of caller addresses to toggle status
122+
* @param _statuses Array of statuses for each caller
123+
*/
124+
function updateCallerStatus(address[] calldata _callers, bool[] calldata _statuses) external onlyOwner {
125+
require(_callers.length == _statuses.length, "Array length mismatch");
126+
require(_callers.length > 0, "Array length must be > 0");
127+
require(!_callers.hasDuplicate(), "Cannot duplicate callers");
128+
129+
for (uint256 i = 0; i < _callers.length; i++) {
130+
address caller = _callers[i];
131+
bool status = _statuses[i];
132+
callAllowList[caller] = status;
133+
emit CallerStatusUpdated(caller, status);
134+
}
135+
}
136+
137+
/**
138+
* ONLY OWNER: Toggle whether anyone can call function, bypassing the callAllowlist
139+
*
140+
* @param _status Boolean indicating whether to allow anyone call
141+
*/
142+
function updateAnyoneCallable(bool _status) external onlyOwner {
143+
anyoneCallable = _status;
144+
emit AnyoneCallableUpdated(_status);
145+
}
146+
147+
/* ============ Internal Functions ============ */
148+
149+
/**
150+
* Validate if passed address is allowed to call function. If anyoneCallable set to true anyone can call otherwise needs to be approved or an EOA.
151+
*/
152+
function _validateAllowedCaller(address _caller) internal view {
153+
bool isEOA = msg.sender == tx.origin;
154+
155+
require(anyoneCallable || isEOA || callAllowList[_caller], "Address not permitted to call");
156+
}
157+
}

utils/contracts/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export { FeeSplitAdapter } from "../../typechain/FeeSplitAdapter";
66
export { FlexibleLeverageStrategyAdapter } from "../../typechain/FlexibleLeverageStrategyAdapter";
77
export { FLIRebalanceViewer } from "../../typechain/FLIRebalanceViewer";
88
export { SupplyCapIssuanceHook } from "../../typechain/SupplyCapIssuanceHook";
9+
export { SupplyCapAllowedCallerIssuanceHook } from "../../typechain/SupplyCapAllowedCallerIssuanceHook";
910
export { IndexToken } from "../../typechain/IndexToken";
1011
export { MerkleDistributor } from "../../typechain/MerkleDistributor";
1112
export { MutualUpgradeMock } from "../../typechain/MutualUpgradeMock";

utils/deploys/deployHooks.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Signer, BigNumber } from "ethers";
22
import { Address } from "../types";
33
import { SupplyCapIssuanceHook } from "../contracts/index";
4+
import { SupplyCapAllowedCallerIssuanceHook } from "../contracts/index";
45

56
import { SupplyCapIssuanceHook__factory } from "../../typechain/factories/SupplyCapIssuanceHook__factory";
7+
import { SupplyCapAllowedCallerIssuanceHook__factory } from "../../typechain/factories/SupplyCapAllowedCallerIssuanceHook__factory";
68

79
export default class DeployHooks {
810
private _deployerSigner: Signer;
@@ -17,4 +19,11 @@ export default class DeployHooks {
1719
): Promise<SupplyCapIssuanceHook> {
1820
return await new SupplyCapIssuanceHook__factory(this._deployerSigner).deploy(initialOwner, supplyCap);
1921
}
22+
23+
public async deploySupplyCapAllowedCallerIssuanceHook(
24+
initialOwner: Address,
25+
supplyCap: BigNumber
26+
): Promise<SupplyCapAllowedCallerIssuanceHook> {
27+
return await new SupplyCapAllowedCallerIssuanceHook__factory(this._deployerSigner).deploy(initialOwner, supplyCap);
28+
}
2029
}

0 commit comments

Comments
 (0)