@ensuro/core 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +93 -0
  2. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +772 -0
  3. package/build/contracts/BaseAssetManager.sol/BaseAssetManager.json +494 -0
  4. package/build/contracts/DataTypes.sol/DataTypes.json +10 -0
  5. package/build/contracts/EToken.sol/EToken.json +1148 -0
  6. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +1093 -0
  7. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +412 -0
  8. package/build/contracts/Policy.sol/Policy.json +10 -0
  9. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +570 -0
  10. package/build/contracts/PolicyPool.sol/PolicyPool.json +1257 -0
  11. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +281 -0
  12. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +660 -0
  13. package/build/contracts/RiskModule.sol/RiskModule.json +748 -0
  14. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +1032 -0
  15. package/build/contracts/WadRayMath.sol/WadRayMath.json +10 -0
  16. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +50 -0
  17. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +322 -0
  18. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +571 -0
  19. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +602 -0
  20. package/build/interfaces/IAssetManager.sol/IAssetManager.json +31 -0
  21. package/build/interfaces/IEToken.sol/IEToken.json +491 -0
  22. package/build/interfaces/IInsolvencyHook.sol/IInsolvencyHook.json +42 -0
  23. package/build/interfaces/ILPWhitelist.sol/ILPWhitelist.json +74 -0
  24. package/build/interfaces/IPolicyNFT.sol/IPolicyNFT.json +321 -0
  25. package/build/interfaces/IPolicyPool.sol/IPolicyPool.json +445 -0
  26. package/build/interfaces/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +24 -0
  27. package/build/interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json +328 -0
  28. package/build/interfaces/IRiskModule.sol/IRiskModule.json +141 -0
  29. package/contracts/AaveAssetManager.sol +326 -0
  30. package/contracts/BaseAssetManager.sol +283 -0
  31. package/contracts/DataTypes.sol +287 -0
  32. package/contracts/EToken.sol +773 -0
  33. package/contracts/FlightDelayRiskModule.sol +208 -0
  34. package/contracts/LPManualWhitelist.sol +57 -0
  35. package/contracts/Policy.sol +104 -0
  36. package/contracts/PolicyNFT.sol +96 -0
  37. package/contracts/PolicyPool.sol +716 -0
  38. package/contracts/PolicyPoolComponent.sol +164 -0
  39. package/contracts/PolicyPoolConfig.sol +231 -0
  40. package/contracts/RiskModule.sol +323 -0
  41. package/contracts/TrustfulRiskModule.sol +81 -0
  42. package/contracts/WadRayMath.sol +135 -0
  43. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +371 -0
  44. package/contracts/upgraded/IPolicyNFTv1.sol +15 -0
  45. package/contracts/upgraded/PolicyNFTv1.sol +126 -0
  46. package/interfaces/IAssetManager.sol +23 -0
  47. package/interfaces/IEToken.sol +44 -0
  48. package/interfaces/IInsolvencyHook.sol +27 -0
  49. package/interfaces/ILPWhitelist.sol +23 -0
  50. package/interfaces/IPolicyNFT.sol +15 -0
  51. package/interfaces/IPolicyPool.sol +44 -0
  52. package/interfaces/IPolicyPoolComponent.sol +13 -0
  53. package/interfaces/IPolicyPoolConfig.sol +81 -0
  54. package/interfaces/IRiskModule.sol +29 -0
  55. package/package.json +30 -0
@@ -0,0 +1,208 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
5
+ import {Policy} from "./Policy.sol";
6
+ import {RiskModule} from "./RiskModule.sol";
7
+ import {Chainlink} from "@chainlink/contracts/src/v0.8/Chainlink.sol";
8
+ import {ChainlinkClientUpgradeable} from "./dependencies/ChainlinkClientUpgradeable.sol";
9
+
10
+ /**
11
+ * @title Flight Delay Risk Module
12
+ * @dev Risk Module that resolves policy based in actualarrivaldate of flight
13
+ * @custom:security-contact security@ensuro.co
14
+ * @author Ensuro
15
+ */
16
+ contract FlightDelayRiskModule is RiskModule, ChainlinkClientUpgradeable {
17
+ using Chainlink for Chainlink.Request;
18
+
19
+ bytes32 public constant PRICER_ROLE = keccak256("PRICER_ROLE");
20
+ bytes32 public constant ORACLE_ADMIN_ROLE = keccak256("ORACLE_ADMIN_ROLE");
21
+ // Multiplier to calculate expiration = expectedArrival + tolerance + delayTime * DELAY_EXPIRATION_TIMES
22
+ uint40 public constant DELAY_EXPIRATION_TIMES = 5;
23
+
24
+ struct PolicyData {
25
+ Policy.PolicyData ensuroPolicy;
26
+ string flight;
27
+ uint40 departure;
28
+ uint40 expectedArrival;
29
+ uint40 tolerance;
30
+ }
31
+
32
+ struct OracleParams {
33
+ address oracle;
34
+ uint96 delayTime;
35
+ uint256 fee;
36
+ bytes16 dataJobId;
37
+ bytes16 sleepJobId;
38
+ }
39
+
40
+ OracleParams internal _oracleParams;
41
+
42
+ mapping(bytes32 => uint256) internal _pendingQueries;
43
+ mapping(uint256 => PolicyData) internal _policies;
44
+
45
+ /// @custom:oz-upgrades-unsafe-allow constructor
46
+ // solhint-disable-next-line no-empty-blocks
47
+ constructor(IPolicyPool policyPool_) RiskModule(policyPool_) {}
48
+
49
+ /**
50
+ * @dev Initializes the RiskModule
51
+ * @param name_ Name of the Risk Module
52
+ * @param scrPercentage_ Solvency Capital Requirement percentage, to calculate
53
+ capital requirement as % of (payout - premium) (in ray)
54
+ * @param ensuroFee_ % of premium that will go for Ensuro treasury (in ray)
55
+ * @param scrInterestRate_ cost of capital (in ray)
56
+ * @param maxScrPerPolicy_ Max SCR to be allocated to this module (in wad)
57
+ * @param scrLimit_ Max SCR to be allocated to this module (in wad)
58
+ * @param wallet_ Address of the RiskModule provider
59
+ * @param linkToken_ Address of ChainLink LINK token
60
+ * @param oracleParams_ Parameters of the Oracle
61
+ */
62
+ function initialize(
63
+ string memory name_,
64
+ uint256 scrPercentage_,
65
+ uint256 ensuroFee_,
66
+ uint256 scrInterestRate_,
67
+ uint256 maxScrPerPolicy_,
68
+ uint256 scrLimit_,
69
+ address wallet_,
70
+ address linkToken_,
71
+ OracleParams memory oracleParams_
72
+ ) public initializer {
73
+ __RiskModule_init(
74
+ name_,
75
+ scrPercentage_,
76
+ ensuroFee_,
77
+ scrInterestRate_,
78
+ maxScrPerPolicy_,
79
+ scrLimit_,
80
+ wallet_
81
+ );
82
+ __ChainlinkClient_init();
83
+ __FlightDelayRiskModule_init_unchained(linkToken_, oracleParams_);
84
+ }
85
+
86
+ // solhint-disable-next-line func-name-mixedcase
87
+ function __FlightDelayRiskModule_init_unchained(
88
+ address linkToken_,
89
+ OracleParams memory oracleParams_
90
+ ) internal initializer {
91
+ setChainlinkToken(linkToken_);
92
+ _oracleParams = oracleParams_;
93
+ }
94
+
95
+ function setOracleParams(OracleParams memory newParams) external onlyRole(ORACLE_ADMIN_ROLE) {
96
+ _oracleParams = newParams;
97
+ }
98
+
99
+ function oracleParams() external view returns (OracleParams memory) {
100
+ return _oracleParams;
101
+ }
102
+
103
+ /**
104
+ * @dev Creates a new policy
105
+ * @param flight Flight Number as String (ex: NAX105)
106
+ * @param departure Departure in epoch seconds (ex: 1631817600)
107
+ * @param expectedArrival Expected arrival in epoch seconds (ex: 1631824800)
108
+ * @param tolerance In seconds, the tolerance margin after expectedArrival before trigger the policy
109
+ * @param payout Payout for customer in case policy is triggered
110
+ * @param premium Premium the customer pays
111
+ * @param lossProb Probability of policy being triggered
112
+ * @param customer Customer address (to take premium from and send payout)
113
+ */
114
+ function newPolicy(
115
+ string memory flight,
116
+ uint40 departure,
117
+ uint40 expectedArrival,
118
+ uint40 tolerance,
119
+ uint256 payout,
120
+ uint256 premium,
121
+ uint256 lossProb,
122
+ address customer,
123
+ uint96 internalId
124
+ ) external onlyRole(PRICER_ROLE) returns (uint256) {
125
+ require(expectedArrival > block.timestamp, "expectedArrival can't be in the past");
126
+ require(departure != 0 && expectedArrival > departure, "expectedArrival <= departure!");
127
+ uint40 expiration = expectedArrival +
128
+ tolerance +
129
+ uint40(_oracleParams.delayTime) *
130
+ DELAY_EXPIRATION_TIMES;
131
+ Policy.PolicyData memory ensuroPolicy = _newPolicy(
132
+ payout,
133
+ premium,
134
+ lossProb,
135
+ expiration,
136
+ customer,
137
+ internalId
138
+ );
139
+ PolicyData storage policy = _policies[ensuroPolicy.id];
140
+ policy.ensuroPolicy = ensuroPolicy;
141
+ policy.flight = flight;
142
+ policy.departure = departure;
143
+ policy.expectedArrival = expectedArrival;
144
+ policy.tolerance = tolerance;
145
+
146
+ uint256 until = expectedArrival + tolerance + uint256(_oracleParams.delayTime);
147
+ _chainlinkRequest(ensuroPolicy.id, policy, until);
148
+ return ensuroPolicy.id;
149
+ }
150
+
151
+ function _chainlinkRequest(
152
+ uint256 policyId,
153
+ PolicyData storage policy,
154
+ uint256 until
155
+ ) internal {
156
+ // request takes a JobID, a callback address, and callback function as input
157
+ Chainlink.Request memory req = buildChainlinkRequest(
158
+ until == 0 ? _oracleParams.dataJobId : _oracleParams.sleepJobId,
159
+ address(this),
160
+ this.fulfill.selector
161
+ );
162
+ req.add("flight", policy.flight);
163
+ req.add("endpoint", "actualarrivaldate");
164
+ req.addUint("departure", policy.departure);
165
+ if (until > 0) {
166
+ req.addUint("until", until);
167
+ }
168
+
169
+ // Sends the request with the amount of payment specified to the oracle
170
+ // (results will arrive with the callback = later)
171
+ bytes32 queryId = sendChainlinkRequestTo(_oracleParams.oracle, req, _oracleParams.fee);
172
+ _pendingQueries[queryId] = policyId;
173
+ }
174
+
175
+ /**
176
+ * @dev Forces the resolution of the policy (without waiting Chainlink scheduled on creation)
177
+ * @param policyId The id of the policy previously created (in newPolicy)
178
+ */
179
+ function resolvePolicy(uint256 policyId) external onlyRole(PRICER_ROLE) returns (uint256) {
180
+ PolicyData storage policy = _policies[policyId];
181
+ require(policy.expectedArrival != 0, "Policy not found!");
182
+ _chainlinkRequest(policyId, policy, 0);
183
+ return policyId;
184
+ }
185
+
186
+ function fulfill(bytes32 queryId, int256 actualArrivalDate)
187
+ public
188
+ recordChainlinkFulfillment(queryId)
189
+ {
190
+ uint256 policyId = _pendingQueries[queryId];
191
+ require(policyId != 0, "queryId not found!");
192
+ PolicyData storage policy = _policies[policyId];
193
+
194
+ if (actualArrivalDate == 0) {
195
+ if (block.timestamp > (policy.expectedArrival + policy.tolerance)) {
196
+ // Treat as arrived after tolerance
197
+ actualArrivalDate = int256(uint256((policy.expectedArrival + policy.tolerance) + 1));
198
+ } else {
199
+ // Not arrived yet
200
+ return;
201
+ }
202
+ }
203
+ bool customerWon = (actualArrivalDate <= 0 || // cancelled
204
+ uint256(actualArrivalDate) > uint256(policy.expectedArrival + policy.tolerance)); // arrived after tolerance
205
+
206
+ _policyPool.resolvePolicyFullPayout(policy.ensuroPolicy, customerWon);
207
+ }
208
+ }
@@ -0,0 +1,57 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
5
+ import {PolicyPoolComponent} from "./PolicyPoolComponent.sol";
6
+ import {ILPWhitelist} from "../interfaces/ILPWhitelist.sol";
7
+ import {IEToken} from "../interfaces/IEToken.sol";
8
+
9
+ /**
10
+ * @title Manual Whitelisting contract
11
+ * @dev LP addresses are whitelisted (and un-whitelisted) manually with transactions by user with given role
12
+ * @custom:security-contact security@ensuro.co
13
+ * @author Ensuro
14
+ */
15
+ contract LPManualWhitelist is ILPWhitelist, PolicyPoolComponent {
16
+ bytes32 public constant LP_WHITELIST_ROLE = keccak256("LP_WHITELIST_ROLE");
17
+
18
+ mapping(address => bool) private _whitelisted;
19
+
20
+ event LPWhitelisted(address provider, bool whitelisted);
21
+
22
+ /// @custom:oz-upgrades-unsafe-allow constructor
23
+ // solhint-disable-next-line no-empty-blocks
24
+ constructor(IPolicyPool policyPool_) PolicyPoolComponent(policyPool_) {}
25
+
26
+ /**
27
+ * @dev Initializes the Whitelist contract
28
+ */
29
+ function initialize() public initializer {
30
+ __PolicyPoolComponent_init();
31
+ }
32
+
33
+ function whitelistAddress(address provider, bool whitelisted)
34
+ external
35
+ onlyPoolRole(LP_WHITELIST_ROLE)
36
+ {
37
+ _whitelisted[provider] = whitelisted;
38
+ emit LPWhitelisted(provider, whitelisted);
39
+ }
40
+
41
+ function acceptsDeposit(
42
+ IEToken,
43
+ address provider,
44
+ uint256
45
+ ) external view override returns (bool) {
46
+ return _whitelisted[provider];
47
+ }
48
+
49
+ function acceptsTransfer(
50
+ IEToken,
51
+ address,
52
+ address providerTo,
53
+ uint256
54
+ ) external view override returns (bool) {
55
+ return _whitelisted[providerTo];
56
+ }
57
+ }
@@ -0,0 +1,104 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+ import {WadRayMath} from "./WadRayMath.sol";
4
+ import {IRiskModule} from "../interfaces/IRiskModule.sol";
5
+
6
+ /**
7
+ * @title Policy library
8
+ * @dev Library for PolicyData struct. This struct represents an active policy, how the premium is
9
+ * distributed, the probability of payout, duration and how the capital is locked.
10
+ * @custom:security-contact security@ensuro.co
11
+ * @author Ensuro
12
+ */
13
+ library Policy {
14
+ using WadRayMath for uint256;
15
+
16
+ uint256 internal constant SECONDS_IN_YEAR = 31536000e18; /* 365 * 24 * 3600 * 10e18 */
17
+ uint256 internal constant SECONDS_IN_YEAR_RAY = 31536000e27; /* 365 * 24 * 3600 * 10e27 */
18
+
19
+ // Active Policies
20
+ struct PolicyData {
21
+ uint256 id;
22
+ uint256 payout;
23
+ uint256 premium;
24
+ uint256 scr;
25
+ uint256 lossProb; // original loss probability (in ray)
26
+ uint256 purePremium; // share of the premium that covers expected losses
27
+ // equal to payout * lossProb * riskModule.moc
28
+ uint256 premiumForEnsuro; // share of the premium that goes for Ensuro (if policy won)
29
+ uint256 premiumForRm; // share of the premium that goes for the RM (if policy won)
30
+ uint256 premiumForLps; // share of the premium that goes to the liquidity providers (won or not)
31
+ IRiskModule riskModule;
32
+ uint40 start;
33
+ uint40 expiration;
34
+ }
35
+
36
+ /// #if_succeeds {:msg "premium preserved"} premium == (newPolicy.premium);
37
+ /// #if_succeeds
38
+ /// {:msg "premium distributed"}
39
+ /// premium == (newPolicy.purePremium + newPolicy.premiumForLps +
40
+ /// newPolicy.premiumForRm + newPolicy.premiumForEnsuro);
41
+ function initialize(
42
+ IRiskModule riskModule,
43
+ uint256 premium,
44
+ uint256 payout,
45
+ uint256 lossProb,
46
+ uint40 expiration
47
+ ) internal view returns (PolicyData memory newPolicy) {
48
+ require(premium <= payout, "Premium cannot be more than payout");
49
+ PolicyData memory policy;
50
+ policy.riskModule = riskModule;
51
+ policy.premium = premium;
52
+ policy.payout = payout;
53
+ policy.lossProb = lossProb;
54
+ policy.purePremium = payout.wadToRay().rayMul(lossProb.rayMul(riskModule.moc())).rayToWad();
55
+ policy.scr = payout.wadMul(riskModule.scrPercentage().rayToWad()) - policy.purePremium;
56
+ require(policy.scr != 0, "SCR can't be zero");
57
+ policy.start = uint40(block.timestamp);
58
+ policy.expiration = expiration;
59
+ policy.premiumForLps = policy.scr.wadMul(
60
+ (
61
+ (riskModule.scrInterestRate() * (policy.expiration - policy.start)).rayDiv(
62
+ SECONDS_IN_YEAR_RAY
63
+ )
64
+ ).rayToWad()
65
+ );
66
+ policy.premiumForEnsuro = (policy.purePremium + policy.premiumForLps).wadMul(
67
+ riskModule.ensuroFee().rayToWad()
68
+ );
69
+ require(
70
+ policy.purePremium + policy.premiumForEnsuro + policy.premiumForLps <= premium,
71
+ "Premium less than minimum"
72
+ );
73
+ policy.premiumForRm =
74
+ premium -
75
+ policy.purePremium -
76
+ policy.premiumForLps -
77
+ policy.premiumForEnsuro;
78
+ return policy;
79
+ }
80
+
81
+ function interestRate(PolicyData memory policy) internal pure returns (uint256) {
82
+ return
83
+ policy
84
+ .premiumForLps
85
+ .wadMul(SECONDS_IN_YEAR)
86
+ .wadDiv((policy.expiration - policy.start) * policy.scr)
87
+ .wadToRay();
88
+ }
89
+
90
+ function accruedInterest(PolicyData memory policy) internal view returns (uint256) {
91
+ uint256 secs = block.timestamp - policy.start;
92
+ return
93
+ policy
94
+ .scr
95
+ .wadToRay()
96
+ .rayMul(secs * interestRate(policy))
97
+ .rayDiv(SECONDS_IN_YEAR_RAY)
98
+ .rayToWad();
99
+ }
100
+
101
+ function hash(PolicyData memory policy) internal pure returns (bytes32) {
102
+ return keccak256(abi.encode(policy));
103
+ }
104
+ }
@@ -0,0 +1,96 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.2;
3
+
4
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5
+ import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
6
+ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
7
+ import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
8
+ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
9
+ import {IPolicyNFT} from "../interfaces/IPolicyNFT.sol";
10
+
11
+ /**
12
+ * @title PolicyNFT - NFT that keeps track of issued policies and its owners
13
+ * @dev Every time a new policy is accepted by the PolicyPool, a new NFT is minted generating a new
14
+ * policyId owned by the customer. Only the PolicyPool can mint NFTs.
15
+ * After creation, NFTs can be transferred in the ERC721 standard way and that changes the policy holder.
16
+ * @custom:security-contact security@ensuro.co
17
+ * @author Ensuro
18
+ */
19
+ contract PolicyNFT is UUPSUpgradeable, ERC721Upgradeable, PausableUpgradeable, IPolicyNFT {
20
+ bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
21
+ bytes32 public constant LEVEL1_ROLE = keccak256("LEVEL1_ROLE");
22
+
23
+ IPolicyPool internal _policyPool;
24
+
25
+ modifier onlyPolicyPool() {
26
+ require(_msgSender() == address(_policyPool), "The caller must be the PolicyPool");
27
+ _;
28
+ }
29
+
30
+ modifier onlyPoolRole2(bytes32 role1, bytes32 role2) {
31
+ _policyPool.config().checkRole2(role1, role2, msg.sender);
32
+ _;
33
+ }
34
+
35
+ modifier onlyPoolRole(bytes32 role) {
36
+ _policyPool.config().checkRole(role, msg.sender);
37
+ _;
38
+ }
39
+
40
+ function initialize(
41
+ string memory name_,
42
+ string memory symbol_,
43
+ IPolicyPool policyPool_
44
+ ) public initializer {
45
+ __UUPSUpgradeable_init();
46
+ __Pausable_init();
47
+ __ERC721_init(name_, symbol_);
48
+ __PolicyNFT_init_unchained(policyPool_);
49
+ }
50
+
51
+ // solhint-disable-next-line func-name-mixedcase
52
+ function __PolicyNFT_init_unchained(IPolicyPool policyPool_) internal initializer {
53
+ _policyPool = policyPool_;
54
+ }
55
+
56
+ // solhint-disable-next-line no-empty-blocks
57
+ function _authorizeUpgrade(address) internal override onlyPoolRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {}
58
+
59
+ function pause() public onlyPoolRole(GUARDIAN_ROLE) {
60
+ _pause();
61
+ }
62
+
63
+ function unpause() public onlyPoolRole2(GUARDIAN_ROLE, LEVEL1_ROLE) {
64
+ _unpause();
65
+ }
66
+
67
+ /**
68
+ * @dev This function can be called only once in contract's lifetime. It links the NFT with the
69
+ * PolicyPool contract. It's called in PolicyPool initialization.
70
+ */
71
+ function connect() external override {
72
+ require(
73
+ address(_policyPool) == address(0) || address(_policyPool) == _msgSender(),
74
+ "PolicyPool already connected"
75
+ );
76
+ _policyPool = IPolicyPool(_msgSender());
77
+ // Not possible to do this validation because connect is called in _policyPool initialize :'(
78
+ // require(_policyPool.policyNFT() == address(this), "PolicyPool not connected to this config");
79
+ }
80
+
81
+ function policyPool() external view returns (IPolicyPool) {
82
+ return _policyPool;
83
+ }
84
+
85
+ function safeMint(address to, uint256 policyId) external override onlyPolicyPool whenNotPaused {
86
+ _safeMint(to, policyId, "");
87
+ }
88
+
89
+ function _beforeTokenTransfer(
90
+ address from,
91
+ address to,
92
+ uint256 tokenId
93
+ ) internal override whenNotPaused {
94
+ super._beforeTokenTransfer(from, to, tokenId);
95
+ }
96
+ }