@ballkidz/defifa 0.0.6 → 0.0.8

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 (47) hide show
  1. package/ADMINISTRATION.md +3 -3
  2. package/AUDIT_INSTRUCTIONS.md +422 -0
  3. package/CRYPTO_ECON.md +5 -5
  4. package/RISKS.md +38 -335
  5. package/SKILLS.md +1 -1
  6. package/STYLE_GUIDE.md +14 -1
  7. package/USER_JOURNEYS.md +691 -0
  8. package/package.json +7 -5
  9. package/script/Deploy.s.sol +26 -13
  10. package/script/helpers/DefifaDeploymentLib.sol +30 -14
  11. package/src/DefifaDeployer.sol +225 -187
  12. package/src/DefifaGovernor.sol +291 -281
  13. package/src/DefifaHook.sol +81 -42
  14. package/src/DefifaProjectOwner.sol +27 -4
  15. package/src/DefifaTokenUriResolver.sol +137 -134
  16. package/src/enums/DefifaGamePhase.sol +1 -1
  17. package/src/enums/DefifaScorecardState.sol +1 -1
  18. package/src/interfaces/IDefifaDeployer.sol +52 -50
  19. package/src/interfaces/IDefifaGamePhaseReporter.sol +2 -2
  20. package/src/interfaces/IDefifaGamePotReporter.sol +1 -1
  21. package/src/interfaces/IDefifaGovernor.sol +53 -54
  22. package/src/interfaces/IDefifaHook.sol +104 -103
  23. package/src/interfaces/IDefifaTokenUriResolver.sol +2 -2
  24. package/src/libraries/DefifaFontImporter.sol +11 -9
  25. package/src/libraries/DefifaHookLib.sol +68 -53
  26. package/src/structs/DefifaAttestations.sol +1 -1
  27. package/src/structs/DefifaDelegation.sol +1 -1
  28. package/src/structs/DefifaLaunchProjectData.sol +4 -4
  29. package/src/structs/DefifaOpsData.sol +1 -1
  30. package/src/structs/DefifaScorecard.sol +1 -1
  31. package/src/structs/DefifaTierCashOutWeight.sol +1 -1
  32. package/src/structs/DefifaTierParams.sol +2 -1
  33. package/test/DefifaAdversarialQuorum.t.sol +602 -0
  34. package/test/DefifaAuditLowGuards.t.sol +304 -0
  35. package/test/DefifaFeeAccounting.t.sol +37 -16
  36. package/test/DefifaGovernor.t.sol +37 -11
  37. package/test/DefifaHookRegressions.t.sol +14 -12
  38. package/test/DefifaMintCostInvariant.t.sol +31 -12
  39. package/test/DefifaNoContest.t.sol +33 -13
  40. package/test/DefifaSecurity.t.sol +45 -25
  41. package/test/DefifaUSDC.t.sol +44 -34
  42. package/test/Fork.t.sol +42 -40
  43. package/test/SVG.t.sol +2 -2
  44. package/test/TestAuditGaps.sol +982 -0
  45. package/test/TestQALastMile.t.sol +511 -0
  46. package/test/regression/FulfillmentBlocksRatification.t.sol +36 -30
  47. package/test/regression/GracePeriodBypass.t.sol +15 -10
@@ -1,98 +1,99 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity 0.8.26;
3
3
 
4
- import {Base64} from "lib/base64/base64.sol";
5
- import {mulDiv} from "@prb/math/src/Common.sol";
6
- import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
7
4
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
8
- import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
9
- import {IDefifaHook} from "./interfaces/IDefifaHook.sol";
10
- import {IDefifaTokenUriResolver} from "./interfaces/IDefifaTokenUriResolver.sol";
11
- import {DefifaFontImporter} from "./libraries/DefifaFontImporter.sol";
12
- import {DefifaGamePhase} from "./enums/DefifaGamePhase.sol";
5
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
6
+ import {mulDiv} from "@prb/math/src/Common.sol";
13
7
 
14
- import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
15
- import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
16
8
  import {ERC721} from "@bananapus/721-hook-v6/src/abstract/ERC721.sol";
9
+ import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
17
10
  import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
18
11
  import {JBIpfsDecoder} from "@bananapus/721-hook-v6/src/libraries/JBIpfsDecoder.sol";
12
+ import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
13
+
14
+ import {Base64} from "lib/base64/base64.sol";
15
+ import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
16
+
17
+ import {DefifaFontImporter} from "./libraries/DefifaFontImporter.sol";
18
+ import {DefifaGamePhase} from "./enums/DefifaGamePhase.sol";
19
+ import {IDefifaHook} from "./interfaces/IDefifaHook.sol";
20
+ import {IDefifaTokenUriResolver} from "./interfaces/IDefifaTokenUriResolver.sol";
19
21
 
20
- /// @title DefifaTokenUriResolver
21
22
  /// @notice Standard Token URIs for Defifa games.
22
23
  contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolver {
23
24
  using Strings for uint256;
24
25
 
25
26
  //*********************************************************************//
26
- // -------------------- private constant properties ------------------ //
27
+ // ----------------------- internal constants ------------------------ //
27
28
  //*********************************************************************//
28
29
 
29
30
  /// @notice The fidelity of the decimal returned in the NFT image.
30
- uint256 private constant _IMG_DECIMAL_FIDELITY = 5;
31
+ uint256 internal constant _IMG_DECIMAL_FIDELITY = 5;
31
32
 
32
33
  //*********************************************************************//
33
34
  // --------------- public immutable stored properties ---------------- //
34
35
  //*********************************************************************//
35
36
 
36
37
  /// @notice The typeface of the SVGs.
37
- ITypeface public immutable override typeface;
38
+ ITypeface public immutable override TYPEFACE;
38
39
 
39
40
  //*********************************************************************//
40
41
  // -------------------------- constructor ---------------------------- //
41
42
  //*********************************************************************//
42
43
 
43
- constructor(ITypeface _typeface) {
44
- typeface = _typeface;
44
+ constructor(ITypeface typeface) {
45
+ TYPEFACE = typeface;
45
46
  }
46
47
 
47
48
  //*********************************************************************//
48
- // ---------------------- external transactions ---------------------- //
49
+ // ----------------------- external views ---------------------------- //
49
50
  //*********************************************************************//
50
51
 
51
52
  /// @notice The metadata URI of the provided token ID.
52
53
  /// @dev Defer to the token's tier IPFS URI if set.
53
- /// @param _nft The address of the nft the token URI should be oriented to.
54
- /// @param _tokenId The ID of the token to get the tier URI for.
54
+ /// @param nft The address of the nft the token URI should be oriented to.
55
+ /// @param tokenId The ID of the token to get the tier URI for.
55
56
  /// @return The token URI corresponding with the tier.
56
- function tokenUriOf(address _nft, uint256 _tokenId) external view override returns (string memory) {
57
+ function tokenUriOf(address nft, uint256 tokenId) external view override returns (string memory) {
57
58
  // Keep a reference to the hook.
58
- IDefifaHook _hook = IDefifaHook(_nft);
59
+ IDefifaHook hook = IDefifaHook(nft);
59
60
 
60
61
  // Get the game ID.
61
- uint256 _gameId = _hook.PROJECT_ID();
62
+ uint256 gameId = hook.PROJECT_ID();
62
63
 
63
64
  // Keep a reference to the game phase text.
64
- string memory _gamePhaseText;
65
+ string memory gamePhaseText;
65
66
 
66
67
  // Keep a reference to the rarity text;
67
- string memory _rarityText;
68
+ string memory rarityText;
68
69
 
69
70
  // Keep a reference to the rarity text;
70
- string memory _valueText;
71
+ string memory valueText;
71
72
 
72
73
  // Keep a reference to the game's name.
73
74
  // TODO: Somehow make the `IDefifaHook` have the `name` function.
74
- string memory _title = ERC721(address(_hook)).name();
75
+ string memory title = ERC721(address(hook)).name();
75
76
 
76
77
  // Keep a reference to the tier's name.
77
- string memory _team;
78
+ string memory team;
78
79
 
79
80
  // Keep a reference to the SVG parts.
80
81
  string[] memory parts = new string[](4);
81
82
 
82
83
  // Keep a reference to the pot.
83
- string memory _potText;
84
+ string memory potText;
84
85
 
85
86
  {
86
87
  // Get a reference to the tier.
87
- JB721Tier memory _tier =
88
- _hook.store().tierOfTokenId({hook: address(_hook), tokenId: _tokenId, includeResolvedUri: false});
88
+ JB721Tier memory tier =
89
+ hook.store().tierOfTokenId({hook: address(hook), tokenId: tokenId, includeResolvedUri: false});
89
90
 
90
91
  // Set the tier's name.
91
- _team = _hook.tierNameOf(_tier.id);
92
+ team = hook.tierNameOf(tier.id);
92
93
 
93
94
  // Check to see if the tier has a URI. Return it if it does.
94
- if (_tier.encodedIPFSUri != bytes32(0)) {
95
- return JBIpfsDecoder.decode({baseUri: _hook.baseURI(), hexString: _tier.encodedIPFSUri});
95
+ if (tier.encodedIPFSUri != bytes32(0)) {
96
+ return JBIpfsDecoder.decode({baseUri: hook.baseURI(), hexString: tier.encodedIPFSUri});
96
97
  }
97
98
 
98
99
  parts[0] = string("data:application/json;base64,");
@@ -100,82 +101,80 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
100
101
  parts[1] = string(
101
102
  abi.encodePacked(
102
103
  '{"name":"',
103
- _team,
104
+ team,
104
105
  '", "id": "',
105
- uint256(_tier.id).toString(),
106
+ uint256(tier.id).toString(),
106
107
  '","description":"Team: ',
107
- _team,
108
+ team,
108
109
  ", ID: ",
109
- uint256(_tier.id).toString(),
110
+ uint256(tier.id).toString(),
110
111
  '.","image":"data:image/svg+xml;base64,'
111
112
  )
112
113
  );
113
114
 
114
115
  {
115
116
  // Get a reference to the game phase.
116
- DefifaGamePhase _gamePhase = _hook.gamePhaseReporter().currentGamePhaseOf(_gameId);
117
+ DefifaGamePhase gamePhase = hook.gamePhaseReporter().currentGamePhaseOf(gameId);
117
118
 
118
119
  // Keep a reference to the game pot.
119
- (uint256 _gamePot, address _gamePotToken, uint256 _gamePotDecimals) =
120
- _hook.gamePotReporter().currentGamePotOf({gameId: _gameId, includeCommitments: false});
120
+ (uint256 gamePot, address gamePotToken, uint256 gamePotDecimals) =
121
+ hook.gamePotReporter().currentGamePotOf({gameId: gameId, includeCommitments: false});
121
122
 
122
123
  // Include the amount redeemed.
123
- _gamePot = _gamePot + _hook.amountRedeemed();
124
+ gamePot = gamePot + hook.amountRedeemed();
124
125
 
125
126
  // Set the pot text.
126
- _potText = _formatBalance({
127
- _amount: _gamePot,
128
- _token: _gamePotToken,
129
- _decimals: _gamePotDecimals,
130
- _fidelity: _IMG_DECIMAL_FIDELITY
127
+ potText = _formatBalance({
128
+ amount: gamePot, token: gamePotToken, decimals: gamePotDecimals, fidelity: _IMG_DECIMAL_FIDELITY
131
129
  });
132
130
 
133
- if (_gamePhase == DefifaGamePhase.COUNTDOWN) {
134
- _gamePhaseText = "Minting starts soon.";
135
- } else if (_gamePhase == DefifaGamePhase.MINT) {
136
- _gamePhaseText = "Minting and refunds are open.";
137
- } else if (_gamePhase == DefifaGamePhase.REFUND) {
138
- _gamePhaseText = "Minting is over. Refunds are ending.";
139
- } else if (_gamePhase == DefifaGamePhase.SCORING) {
140
- _gamePhaseText = "Awaiting scorecard approval.";
141
- } else if (_gamePhase == DefifaGamePhase.COMPLETE) {
142
- _gamePhaseText = "Scorecard locked in. Burn to claim reward.";
143
- } else if (_gamePhase == DefifaGamePhase.NO_CONTEST) {
144
- _gamePhaseText = "No contest. Refunds open.";
131
+ if (gamePhase == DefifaGamePhase.COUNTDOWN) {
132
+ gamePhaseText = "Minting starts soon.";
133
+ } else if (gamePhase == DefifaGamePhase.MINT) {
134
+ gamePhaseText = "Minting and refunds are open.";
135
+ } else if (gamePhase == DefifaGamePhase.REFUND) {
136
+ gamePhaseText = "Minting is over. Refunds are ending.";
137
+ } else if (gamePhase == DefifaGamePhase.SCORING) {
138
+ gamePhaseText = "Awaiting scorecard approval.";
139
+ } else if (gamePhase == DefifaGamePhase.COMPLETE) {
140
+ gamePhaseText = "Scorecard locked in. Burn to claim reward.";
141
+ } else if (gamePhase == DefifaGamePhase.NO_CONTEST) {
142
+ gamePhaseText = "No contest. Refunds open.";
145
143
  }
146
144
 
147
145
  // Keep a reference to the number of tokens outstanding from this tier.
148
- uint256 _totalMinted = _hook.currentSupplyOfTier(_tier.id);
146
+ uint256 totalMinted = hook.currentSupplyOfTier(tier.id);
149
147
 
150
- if (_gamePhase == DefifaGamePhase.MINT) {
151
- _rarityText = string(
152
- abi.encodePacked(_totalMinted.toString(), _totalMinted == 1 ? " card so far" : " cards so far")
148
+ if (gamePhase == DefifaGamePhase.MINT) {
149
+ rarityText = string(
150
+ abi.encodePacked(totalMinted.toString(), totalMinted == 1 ? " card so far" : " cards so far")
153
151
  );
154
152
  } else {
155
- _rarityText = string(
153
+ rarityText = string(
156
154
  abi.encodePacked(
157
- _totalMinted.toString(), _totalMinted == 1 ? " card in existence" : " cards in existence"
155
+ totalMinted.toString(), totalMinted == 1 ? " card in existence" : " cards in existence"
158
156
  )
159
157
  );
160
158
  }
161
159
 
162
- if (_gamePhase == DefifaGamePhase.SCORING || _gamePhase == DefifaGamePhase.COMPLETE) {
163
- uint256 _potPortion =
164
- mulDiv(_gamePot, _hook.cashOutWeightOf(_tokenId), _hook.TOTAL_CASHOUT_WEIGHT());
165
- _valueText = !_hook.cashOutWeightIsSet()
160
+ if (gamePhase == DefifaGamePhase.SCORING || gamePhase == DefifaGamePhase.COMPLETE) {
161
+ uint256 potPortion = mulDiv({
162
+ x: gamePot, y: hook.cashOutWeightOf(tokenId), denominator: hook.TOTAL_CASHOUT_WEIGHT()
163
+ });
164
+ valueText = !hook.cashOutWeightIsSet()
166
165
  ? "Awaiting scorecard..."
167
166
  : _formatBalance({
168
- _amount: _potPortion,
169
- _token: _gamePotToken,
170
- _decimals: _gamePotDecimals,
171
- _fidelity: _IMG_DECIMAL_FIDELITY
167
+ amount: potPortion,
168
+ token: gamePotToken,
169
+ decimals: gamePotDecimals,
170
+ fidelity: _IMG_DECIMAL_FIDELITY
172
171
  });
173
172
  } else {
174
- _valueText = _formatBalance({
175
- _amount: _tier.price,
176
- _token: _gamePotToken,
177
- _decimals: _gamePotDecimals,
178
- _fidelity: _IMG_DECIMAL_FIDELITY
173
+ valueText = _formatBalance({
174
+ amount: tier.price,
175
+ token: gamePotToken,
176
+ decimals: gamePotDecimals,
177
+ fidelity: _IMG_DECIMAL_FIDELITY
179
178
  });
180
179
  }
181
180
  }
@@ -184,54 +183,54 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
184
183
  abi.encodePacked(
185
184
  '<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">',
186
185
  '<style>@font-face{font-family:"Capsules-500";src:url(data:font/truetype;charset=utf-8;base64,',
187
- DefifaFontImporter.getSkinnyFontSource(typeface),
186
+ DefifaFontImporter.getSkinnyFontSource(TYPEFACE),
188
187
  ');format("opentype");}',
189
188
  '@font-face{font-family:"Capsules-700";src:url(data:font/truetype;charset=utf-8;base64,',
190
- DefifaFontImporter.getBeefyFontSource(typeface),
189
+ DefifaFontImporter.getBeefyFontSource(TYPEFACE),
191
190
  ');format("opentype");}',
192
191
  "text{white-space:pre-wrap; width:100%; }</style>",
193
192
  '<rect width="100%" height="100%" fill="#181424"/>',
194
193
  '<text x="10" y="30" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">GAME: ',
195
- _gameId.toString(),
194
+ gameId.toString(),
196
195
  " | POT: ",
197
- _potText,
196
+ potText,
198
197
  " | CARDS: ",
199
- _hook.store().totalSupplyOf(address(_hook)).toString(),
198
+ hook.store().totalSupplyOf(address(hook)).toString(),
200
199
  "</text>",
201
200
  '<text x="10" y="50" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #ed017c;">',
202
- _gamePhaseText,
201
+ gamePhaseText,
203
202
  "</text>",
204
203
  '<text x="10" y="85" style="font-size:26px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">',
205
- _getSubstring(_title, 0, 30),
204
+ _getSubstring(title, 0, 30),
206
205
  "</text>",
207
206
  '<text x="10" y="120" style="font-size:26px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">',
208
- _getSubstring(_title, 30, 60),
207
+ _getSubstring(title, 30, 60),
209
208
  "</text>",
210
209
  '<text x="10" y="205" style="font-size:80px; font-family: Capsules-700; font-weight:700; fill: #fea282;">',
211
- bytes(_getSubstring(_team, 20, 30)).length != 0 && bytes(_getSubstring(_team, 10, 20)).length != 0
212
- ? _getSubstring(_team, 0, 10)
210
+ bytes(_getSubstring(team, 20, 30)).length != 0 && bytes(_getSubstring(team, 10, 20)).length != 0
211
+ ? _getSubstring(team, 0, 10)
213
212
  : "",
214
213
  "</text>",
215
214
  '<text x="10" y="295" style="font-size:80px; font-family: Capsules-700; font-weight:700; fill: #fea282;">',
216
- bytes(_getSubstring(_team, 20, 30)).length != 0
217
- ? _getSubstring(_team, 10, 20)
218
- : bytes(_getSubstring(_team, 10, 20)).length != 0 ? _getSubstring(_team, 0, 10) : "",
215
+ bytes(_getSubstring(team, 20, 30)).length != 0
216
+ ? _getSubstring(team, 10, 20)
217
+ : bytes(_getSubstring(team, 10, 20)).length != 0 ? _getSubstring(team, 0, 10) : "",
219
218
  "</text>",
220
219
  '<text x="10" y="385" style="font-size:80px; font-family: Capsules-700; font-weight:700; fill: #fea282;">',
221
- bytes(_getSubstring(_team, 20, 30)).length != 0
222
- ? _getSubstring(_team, 20, 30)
223
- : bytes(_getSubstring(_team, 10, 20)).length != 0
224
- ? _getSubstring(_team, 10, 20)
225
- : _getSubstring(_team, 0, 10),
220
+ bytes(_getSubstring(team, 20, 30)).length != 0
221
+ ? _getSubstring(team, 20, 30)
222
+ : bytes(_getSubstring(team, 10, 20)).length != 0
223
+ ? _getSubstring(team, 10, 20)
224
+ : _getSubstring(team, 0, 10),
226
225
  "</text>",
227
226
  '<text x="10" y="430" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">TOKEN ID: ',
228
- _tokenId.toString(),
227
+ tokenId.toString(),
229
228
  "</text>",
230
229
  '<text x="10" y="455" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">RARITY: ',
231
- _rarityText,
230
+ rarityText,
232
231
  "</text>",
233
232
  '<text x="10" y="480" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">BACKED BY: ',
234
- _valueText,
233
+ valueText,
235
234
  "</text>",
236
235
  "</svg>"
237
236
  )
@@ -241,72 +240,76 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
241
240
  return string.concat(parts[0], Base64.encode(abi.encodePacked(parts[1], parts[2], parts[3])));
242
241
  }
243
242
 
243
+ //*********************************************************************//
244
+ // ----------------------- internal views ---------------------------- //
245
+ //*********************************************************************//
246
+
244
247
  /// @notice Formats a balance from a fixed point number to a string.
245
- /// @param _amount The fixed point amount.
246
- /// @param _token The token the amount is in.
247
- /// @param _decimals The number of decimals in the fixed point amount.
248
- /// @param _fidelity The number of decimals that should be returned in the formatted string.
248
+ /// @param amount The fixed point amount.
249
+ /// @param token The token the amount is in.
250
+ /// @param decimals The number of decimals in the fixed point amount.
251
+ /// @param fidelity The number of decimals that should be returned in the formatted string.
249
252
  /// @return The formatted balance.
250
253
  function _formatBalance(
251
- uint256 _amount,
252
- address _token,
253
- uint256 _decimals,
254
- uint256 _fidelity
254
+ uint256 amount,
255
+ address token,
256
+ uint256 decimals,
257
+ uint256 fidelity
255
258
  )
256
259
  internal
257
260
  view
258
261
  returns (string memory)
259
262
  {
260
- bool _isEth = _token == JBConstants.NATIVE_TOKEN;
263
+ bool isEth = token == JBConstants.NATIVE_TOKEN;
261
264
 
262
- uint256 _fixedPoint = 10 ** _decimals;
265
+ uint256 fixedPoint = 10 ** decimals;
263
266
 
264
267
  // Convert amount to a decimal format
265
- string memory _integerPart = (_amount / _fixedPoint).toString();
268
+ string memory integerPart = (amount / fixedPoint).toString();
266
269
 
267
- uint256 _remainder = _amount % _fixedPoint;
268
- uint256 _scaledRemainder = _remainder * (10 ** _fidelity);
269
- uint256 _decimalPart = _scaledRemainder / _fixedPoint;
270
+ uint256 remainder = amount % fixedPoint;
271
+ uint256 scaledRemainder = remainder * (10 ** fidelity);
272
+ uint256 decimalPart = scaledRemainder / fixedPoint;
270
273
 
271
274
  // Pad with zeros if necessary
272
- string memory _decimalPartStr = _decimalPart.toString();
273
- while (bytes(_decimalPartStr).length < _fidelity) {
274
- _decimalPartStr = string(abi.encodePacked("0", _decimalPartStr));
275
+ string memory decimalPartStr = decimalPart.toString();
276
+ while (bytes(decimalPartStr).length < fidelity) {
277
+ decimalPartStr = string(abi.encodePacked("0", decimalPartStr));
275
278
  }
276
279
 
277
280
  // Concatenate the strings
278
- return _isEth
279
- ? string(abi.encodePacked("\u039E", _integerPart, ".", _decimalPartStr))
280
- : string(abi.encodePacked(_integerPart, ".", _decimalPartStr, " ", IERC20Metadata(_token).symbol()));
281
+ return isEth
282
+ ? string(abi.encodePacked("\u039E", integerPart, ".", decimalPartStr))
283
+ : string(abi.encodePacked(integerPart, ".", decimalPartStr, " ", IERC20Metadata(token).symbol()));
281
284
  }
282
285
 
283
286
  /// @notice Gets a substring.
284
287
  /// @dev If the first character is a space, it is not included.
285
- /// @param _str The string to get a substring of.
286
- /// @param _startIndex The first index of the substring from within the string.
287
- /// @param _endIndex The last index of the string from within the string.
288
+ /// @param str The string to get a substring of.
289
+ /// @param startIndex The first index of the substring from within the string.
290
+ /// @param endIndex The last index of the string from within the string.
288
291
  /// @return substring The substring.
289
292
  function _getSubstring(
290
- string memory _str,
291
- uint256 _startIndex,
292
- uint256 _endIndex
293
+ string memory str,
294
+ uint256 startIndex,
295
+ uint256 endIndex
293
296
  )
294
297
  internal
295
298
  pure
296
299
  returns (string memory substring)
297
300
  {
298
- bytes memory _strBytes = bytes(_str);
299
- if (_startIndex >= _strBytes.length) return "";
300
- if (_endIndex > _strBytes.length) _endIndex = _strBytes.length;
301
- _startIndex = _strBytes[_startIndex] == bytes1(0x20) ? _startIndex + 1 : _startIndex;
302
- if (_startIndex >= _endIndex) return "";
303
- bytes memory _result = new bytes(_endIndex - _startIndex);
304
- for (uint256 _i = _startIndex; _i < _endIndex;) {
305
- _result[_i - _startIndex] = _strBytes[_i];
301
+ bytes memory strBytes = bytes(str);
302
+ if (startIndex >= strBytes.length) return "";
303
+ if (endIndex > strBytes.length) endIndex = strBytes.length;
304
+ startIndex = strBytes[startIndex] == bytes1(0x20) ? startIndex + 1 : startIndex;
305
+ if (startIndex >= endIndex) return "";
306
+ bytes memory result = new bytes(endIndex - startIndex);
307
+ for (uint256 i = startIndex; i < endIndex;) {
308
+ result[i - startIndex] = strBytes[i];
306
309
  unchecked {
307
- ++_i;
310
+ ++i;
308
311
  }
309
312
  }
310
- return string(_result);
313
+ return string(result);
311
314
  }
312
315
  }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
2
+ pragma solidity ^0.8.0;
3
3
 
4
4
  enum DefifaGamePhase {
5
5
  COUNTDOWN,
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
2
+ pragma solidity ^0.8.0;
3
3
 
4
4
  enum DefifaScorecardState {
5
5
  PENDING,
@@ -1,18 +1,24 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
3
-
4
- import {DefifaLaunchProjectData} from "../structs/DefifaLaunchProjectData.sol";
5
- import {IDefifaHook} from "./IDefifaHook.sol";
6
- import {IDefifaGovernor} from "./IDefifaGovernor.sol";
2
+ pragma solidity ^0.8.0;
7
3
 
8
4
  import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
9
- import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
10
- import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
11
5
  import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
6
+ import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
7
+ import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
8
+
9
+ import {DefifaLaunchProjectData} from "../structs/DefifaLaunchProjectData.sol";
10
+ import {IDefifaGovernor} from "./IDefifaGovernor.sol";
11
+ import {IDefifaHook} from "./IDefifaHook.sol";
12
12
 
13
13
  /// @notice Deploys and manages Defifa prediction games, including lifecycle phase transitions
14
14
  /// and commitment fulfillment.
15
15
  interface IDefifaDeployer {
16
+ event CommitmentPayoutFailed(uint256 indexed gameId, uint256 amount, bytes reason);
17
+
18
+ event DistributeToSplit(JBSplit split, uint256 amount, address caller);
19
+
20
+ event FulfilledCommitments(uint256 indexed gameId, uint256 pot, address caller);
21
+
16
22
  event LaunchGame(
17
23
  uint256 indexed gameId,
18
24
  IDefifaHook indexed hook,
@@ -21,55 +27,62 @@ interface IDefifaDeployer {
21
27
  address caller
22
28
  );
23
29
 
30
+ event QueuedNoContest(uint256 indexed gameId, address caller);
31
+
24
32
  event QueuedRefundPhase(uint256 indexed gameId, address caller);
25
33
 
26
34
  event QueuedScoringPhase(uint256 indexed gameId, address caller);
27
35
 
28
- event QueuedNoContest(uint256 indexed gameId, address caller);
36
+ /// @notice The fee divisor for base protocol fees (100 / fee percent).
37
+ /// @return The fee divisor.
38
+ function BASE_PROTOCOL_FEE_DIVISOR() external view returns (uint256);
29
39
 
30
- event FulfilledCommitments(uint256 indexed gameId, uint256 pot, address caller);
40
+ /// @notice The Juicebox project ID of the base protocol project.
41
+ /// @return The project ID.
42
+ function BASE_PROTOCOL_PROJECT_ID() external view returns (uint256);
31
43
 
32
- event DistributeToSplit(JBSplit split, uint256 amount, address caller);
44
+ /// @notice The Juicebox controller used to manage projects.
45
+ /// @return The controller contract.
46
+ function CONTROLLER() external view returns (IJBController);
33
47
 
34
- /// @notice The split group ID used for distributing game pot funds.
35
- /// @return The split group.
36
- function splitGroup() external view returns (uint256);
48
+ /// @notice The fee divisor for Defifa fees (100 / fee percent).
49
+ /// @return The fee divisor.
50
+ function DEFIFA_FEE_DIVISOR() external view returns (uint256);
37
51
 
38
52
  /// @notice The Juicebox project ID of the Defifa project.
39
53
  /// @return The project ID.
40
- function defifaProjectId() external view returns (uint256);
41
-
42
- /// @notice The Juicebox project ID of the base protocol project.
43
- /// @return The project ID.
44
- function baseProtocolProjectId() external view returns (uint256);
45
-
46
- /// @notice The code origin address used as an implementation for hook clones.
47
- /// @return The code origin address.
48
- function hookCodeOrigin() external view returns (address);
49
-
50
- /// @notice The token URI resolver used for game NFT metadata.
51
- /// @return The token URI resolver contract.
52
- function tokenUriResolver() external view returns (IJB721TokenUriResolver);
54
+ function DEFIFA_PROJECT_ID() external view returns (uint256);
53
55
 
54
56
  /// @notice The governor contract used for scorecard governance.
55
57
  /// @return The governor contract.
56
- function governor() external view returns (IDefifaGovernor);
58
+ function GOVERNOR() external view returns (IDefifaGovernor);
57
59
 
58
- /// @notice The Juicebox controller used to manage projects.
59
- /// @return The controller contract.
60
- function controller() external view returns (IJBController);
60
+ /// @notice The code origin address used as an implementation for hook clones.
61
+ /// @return The code origin address.
62
+ function HOOK_CODE_ORIGIN() external view returns (address);
61
63
 
62
64
  /// @notice The address registry used for content-addressable deployment lookups.
63
65
  /// @return The address registry contract.
64
- function registry() external view returns (IJBAddressRegistry);
66
+ function REGISTRY() external view returns (IJBAddressRegistry);
65
67
 
66
- /// @notice The fee divisor for Defifa fees (100 / fee percent).
67
- /// @return The fee divisor.
68
- function DEFIFA_FEE_DIVISOR() external view returns (uint256);
68
+ /// @notice The split group ID used for distributing game pot funds.
69
+ /// @return The split group.
70
+ function SPLIT_GROUP() external view returns (uint256);
69
71
 
70
- /// @notice The fee divisor for base protocol fees (100 / fee percent).
71
- /// @return The fee divisor.
72
- function BASE_PROTOCOL_FEE_DIVISOR() external view returns (uint256);
72
+ /// @notice The token URI resolver used for game NFT metadata.
73
+ /// @return The token URI resolver contract.
74
+ function TOKEN_URI_RESOLVER() external view returns (IJB721TokenUriResolver);
75
+
76
+ /// @notice Whether the next game phase needs to be queued.
77
+ /// @param gameId The ID of the game.
78
+ /// @return True if the next phase needs queueing.
79
+ function nextPhaseNeedsQueueing(uint256 gameId) external view returns (bool);
80
+
81
+ /// @notice The safety parameters for a game.
82
+ /// @param gameId The ID of the game.
83
+ /// @return minParticipation The minimum participation threshold.
84
+ /// @return scorecardTimeout The scorecard timeout duration.
85
+ function safetyParamsOf(uint256 gameId) external view returns (uint256 minParticipation, uint32 scorecardTimeout);
73
86
 
74
87
  /// @notice The timing parameters for a game.
75
88
  /// @param gameId The ID of the game.
@@ -81,26 +94,15 @@ interface IDefifaDeployer {
81
94
  /// @return The token address.
82
95
  function tokenOf(uint256 gameId) external view returns (address);
83
96
 
84
- /// @notice The safety parameters for a game.
85
- /// @param gameId The ID of the game.
86
- /// @return minParticipation The minimum participation threshold.
87
- /// @return scorecardTimeout The scorecard timeout duration.
88
- function safetyParamsOf(uint256 gameId) external view returns (uint256 minParticipation, uint32 scorecardTimeout);
89
-
90
- /// @notice Whether the next game phase needs to be queued.
97
+ /// @notice Fulfill the commitments of a game by distributing the pot.
91
98
  /// @param gameId The ID of the game.
92
- /// @return True if the next phase needs queueing.
93
- function nextPhaseNeedsQueueing(uint256 gameId) external view returns (bool);
99
+ function fulfillCommitmentsOf(uint256 gameId) external;
94
100
 
95
101
  /// @notice Launch a new Defifa game.
96
102
  /// @param launchProjectData The configuration for launching the game.
97
103
  /// @return gameId The ID of the newly launched game.
98
104
  function launchGameWith(DefifaLaunchProjectData calldata launchProjectData) external returns (uint256 gameId);
99
105
 
100
- /// @notice Fulfill the commitments of a game by distributing the pot.
101
- /// @param gameId The ID of the game.
102
- function fulfillCommitmentsOf(uint256 gameId) external;
103
-
104
106
  /// @notice Trigger a no-contest outcome for a game.
105
107
  /// @param gameId The ID of the game.
106
108
  function triggerNoContestFor(uint256 gameId) external;
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
2
+ pragma solidity ^0.8.0;
3
3
 
4
- import {DefifaGamePhase} from "./../enums/DefifaGamePhase.sol";
4
+ import {DefifaGamePhase} from "../enums/DefifaGamePhase.sol";
5
5
 
6
6
  interface IDefifaGamePhaseReporter {
7
7
  function currentGamePhaseOf(uint256 gameId) external view returns (DefifaGamePhase);
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
2
+ pragma solidity ^0.8.0;
3
3
 
4
4
  interface IDefifaGamePotReporter {
5
5
  function fulfilledCommitmentsOf(uint256 gameId) external view returns (uint256);