@ballkidz/defifa 0.0.7 → 0.0.9

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 (48) hide show
  1. package/ADMINISTRATION.md +3 -3
  2. package/ARCHITECTURE.md +2 -0
  3. package/AUDIT_INSTRUCTIONS.md +422 -0
  4. package/CRYPTO_ECON.md +5 -5
  5. package/README.md +1 -1
  6. package/RISKS.md +38 -335
  7. package/SKILLS.md +1 -1
  8. package/USER_JOURNEYS.md +691 -0
  9. package/package.json +7 -7
  10. package/script/Deploy.s.sol +14 -3
  11. package/script/helpers/DefifaDeploymentLib.sol +13 -15
  12. package/src/DefifaDeployer.sol +221 -192
  13. package/src/DefifaGovernor.sol +286 -276
  14. package/src/DefifaHook.sol +68 -34
  15. package/src/DefifaProjectOwner.sol +27 -4
  16. package/src/DefifaTokenUriResolver.sol +136 -134
  17. package/src/enums/DefifaGamePhase.sol +1 -1
  18. package/src/enums/DefifaScorecardState.sol +1 -1
  19. package/src/interfaces/IDefifaDeployer.sol +52 -50
  20. package/src/interfaces/IDefifaGamePhaseReporter.sol +2 -2
  21. package/src/interfaces/IDefifaGamePotReporter.sol +1 -1
  22. package/src/interfaces/IDefifaGovernor.sol +53 -54
  23. package/src/interfaces/IDefifaHook.sol +104 -103
  24. package/src/interfaces/IDefifaTokenUriResolver.sol +2 -2
  25. package/src/libraries/DefifaFontImporter.sol +11 -9
  26. package/src/libraries/DefifaHookLib.sol +66 -53
  27. package/src/structs/DefifaAttestations.sol +1 -1
  28. package/src/structs/DefifaDelegation.sol +1 -1
  29. package/src/structs/DefifaLaunchProjectData.sol +4 -4
  30. package/src/structs/DefifaOpsData.sol +1 -1
  31. package/src/structs/DefifaScorecard.sol +1 -1
  32. package/src/structs/DefifaTierCashOutWeight.sol +1 -1
  33. package/src/structs/DefifaTierParams.sol +2 -1
  34. package/test/DefifaAdversarialQuorum.t.sol +602 -0
  35. package/test/DefifaAuditLowGuards.t.sol +304 -0
  36. package/test/DefifaFeeAccounting.t.sol +37 -16
  37. package/test/DefifaGovernor.t.sol +43 -19
  38. package/test/DefifaHookRegressions.t.sol +14 -12
  39. package/test/DefifaMintCostInvariant.t.sol +31 -12
  40. package/test/DefifaNoContest.t.sol +34 -16
  41. package/test/DefifaSecurity.t.sol +46 -28
  42. package/test/DefifaUSDC.t.sol +45 -36
  43. package/test/Fork.t.sol +43 -43
  44. package/test/SVG.t.sol +2 -2
  45. package/test/TestAuditGaps.sol +982 -0
  46. package/test/TestQALastMile.t.sol +511 -0
  47. package/test/regression/FulfillmentBlocksRatification.t.sol +36 -30
  48. 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,83 +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 = mulDiv({
164
- x: _gamePot, y: _hook.cashOutWeightOf(_tokenId), denominator: _hook.TOTAL_CASHOUT_WEIGHT()
160
+ if (gamePhase == DefifaGamePhase.SCORING || gamePhase == DefifaGamePhase.COMPLETE) {
161
+ uint256 potPortion = mulDiv({
162
+ x: gamePot, y: hook.cashOutWeightOf(tokenId), denominator: hook.TOTAL_CASHOUT_WEIGHT()
165
163
  });
166
- _valueText = !_hook.cashOutWeightIsSet()
164
+ valueText = !hook.cashOutWeightIsSet()
167
165
  ? "Awaiting scorecard..."
168
166
  : _formatBalance({
169
- _amount: _potPortion,
170
- _token: _gamePotToken,
171
- _decimals: _gamePotDecimals,
172
- _fidelity: _IMG_DECIMAL_FIDELITY
167
+ amount: potPortion,
168
+ token: gamePotToken,
169
+ decimals: gamePotDecimals,
170
+ fidelity: _IMG_DECIMAL_FIDELITY
173
171
  });
174
172
  } else {
175
- _valueText = _formatBalance({
176
- _amount: _tier.price,
177
- _token: _gamePotToken,
178
- _decimals: _gamePotDecimals,
179
- _fidelity: _IMG_DECIMAL_FIDELITY
173
+ valueText = _formatBalance({
174
+ amount: tier.price,
175
+ token: gamePotToken,
176
+ decimals: gamePotDecimals,
177
+ fidelity: _IMG_DECIMAL_FIDELITY
180
178
  });
181
179
  }
182
180
  }
@@ -185,54 +183,54 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
185
183
  abi.encodePacked(
186
184
  '<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">',
187
185
  '<style>@font-face{font-family:"Capsules-500";src:url(data:font/truetype;charset=utf-8;base64,',
188
- DefifaFontImporter.getSkinnyFontSource(typeface),
186
+ DefifaFontImporter.getSkinnyFontSource(TYPEFACE),
189
187
  ');format("opentype");}',
190
188
  '@font-face{font-family:"Capsules-700";src:url(data:font/truetype;charset=utf-8;base64,',
191
- DefifaFontImporter.getBeefyFontSource(typeface),
189
+ DefifaFontImporter.getBeefyFontSource(TYPEFACE),
192
190
  ');format("opentype");}',
193
191
  "text{white-space:pre-wrap; width:100%; }</style>",
194
192
  '<rect width="100%" height="100%" fill="#181424"/>',
195
193
  '<text x="10" y="30" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">GAME: ',
196
- _gameId.toString(),
194
+ gameId.toString(),
197
195
  " | POT: ",
198
- _potText,
196
+ potText,
199
197
  " | CARDS: ",
200
- _hook.store().totalSupplyOf(address(_hook)).toString(),
198
+ hook.store().totalSupplyOf(address(hook)).toString(),
201
199
  "</text>",
202
200
  '<text x="10" y="50" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #ed017c;">',
203
- _gamePhaseText,
201
+ gamePhaseText,
204
202
  "</text>",
205
203
  '<text x="10" y="85" style="font-size:26px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">',
206
- _getSubstring(_title, 0, 30),
204
+ _getSubstring(title, 0, 30),
207
205
  "</text>",
208
206
  '<text x="10" y="120" style="font-size:26px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">',
209
- _getSubstring(_title, 30, 60),
207
+ _getSubstring(title, 30, 60),
210
208
  "</text>",
211
209
  '<text x="10" y="205" style="font-size:80px; font-family: Capsules-700; font-weight:700; fill: #fea282;">',
212
- bytes(_getSubstring(_team, 20, 30)).length != 0 && bytes(_getSubstring(_team, 10, 20)).length != 0
213
- ? _getSubstring(_team, 0, 10)
210
+ bytes(_getSubstring(team, 20, 30)).length != 0 && bytes(_getSubstring(team, 10, 20)).length != 0
211
+ ? _getSubstring(team, 0, 10)
214
212
  : "",
215
213
  "</text>",
216
214
  '<text x="10" y="295" style="font-size:80px; font-family: Capsules-700; font-weight:700; fill: #fea282;">',
217
- bytes(_getSubstring(_team, 20, 30)).length != 0
218
- ? _getSubstring(_team, 10, 20)
219
- : 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) : "",
220
218
  "</text>",
221
219
  '<text x="10" y="385" style="font-size:80px; font-family: Capsules-700; font-weight:700; fill: #fea282;">',
222
- bytes(_getSubstring(_team, 20, 30)).length != 0
223
- ? _getSubstring(_team, 20, 30)
224
- : bytes(_getSubstring(_team, 10, 20)).length != 0
225
- ? _getSubstring(_team, 10, 20)
226
- : _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),
227
225
  "</text>",
228
226
  '<text x="10" y="430" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">TOKEN ID: ',
229
- _tokenId.toString(),
227
+ tokenId.toString(),
230
228
  "</text>",
231
229
  '<text x="10" y="455" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">RARITY: ',
232
- _rarityText,
230
+ rarityText,
233
231
  "</text>",
234
232
  '<text x="10" y="480" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">BACKED BY: ',
235
- _valueText,
233
+ valueText,
236
234
  "</text>",
237
235
  "</svg>"
238
236
  )
@@ -242,72 +240,76 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
242
240
  return string.concat(parts[0], Base64.encode(abi.encodePacked(parts[1], parts[2], parts[3])));
243
241
  }
244
242
 
243
+ //*********************************************************************//
244
+ // ----------------------- internal views ---------------------------- //
245
+ //*********************************************************************//
246
+
245
247
  /// @notice Formats a balance from a fixed point number to a string.
246
- /// @param _amount The fixed point amount.
247
- /// @param _token The token the amount is in.
248
- /// @param _decimals The number of decimals in the fixed point amount.
249
- /// @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.
250
252
  /// @return The formatted balance.
251
253
  function _formatBalance(
252
- uint256 _amount,
253
- address _token,
254
- uint256 _decimals,
255
- uint256 _fidelity
254
+ uint256 amount,
255
+ address token,
256
+ uint256 decimals,
257
+ uint256 fidelity
256
258
  )
257
259
  internal
258
260
  view
259
261
  returns (string memory)
260
262
  {
261
- bool _isEth = _token == JBConstants.NATIVE_TOKEN;
263
+ bool isEth = token == JBConstants.NATIVE_TOKEN;
262
264
 
263
- uint256 _fixedPoint = 10 ** _decimals;
265
+ uint256 fixedPoint = 10 ** decimals;
264
266
 
265
267
  // Convert amount to a decimal format
266
- string memory _integerPart = (_amount / _fixedPoint).toString();
268
+ string memory integerPart = (amount / fixedPoint).toString();
267
269
 
268
- uint256 _remainder = _amount % _fixedPoint;
269
- uint256 _scaledRemainder = _remainder * (10 ** _fidelity);
270
- uint256 _decimalPart = _scaledRemainder / _fixedPoint;
270
+ uint256 remainder = amount % fixedPoint;
271
+ uint256 scaledRemainder = remainder * (10 ** fidelity);
272
+ uint256 decimalPart = scaledRemainder / fixedPoint;
271
273
 
272
274
  // Pad with zeros if necessary
273
- string memory _decimalPartStr = _decimalPart.toString();
274
- while (bytes(_decimalPartStr).length < _fidelity) {
275
- _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));
276
278
  }
277
279
 
278
280
  // Concatenate the strings
279
- return _isEth
280
- ? string(abi.encodePacked("\u039E", _integerPart, ".", _decimalPartStr))
281
- : 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()));
282
284
  }
283
285
 
284
286
  /// @notice Gets a substring.
285
287
  /// @dev If the first character is a space, it is not included.
286
- /// @param _str The string to get a substring of.
287
- /// @param _startIndex The first index of the substring from within the string.
288
- /// @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.
289
291
  /// @return substring The substring.
290
292
  function _getSubstring(
291
- string memory _str,
292
- uint256 _startIndex,
293
- uint256 _endIndex
293
+ string memory str,
294
+ uint256 startIndex,
295
+ uint256 endIndex
294
296
  )
295
297
  internal
296
298
  pure
297
299
  returns (string memory substring)
298
300
  {
299
- bytes memory _strBytes = bytes(_str);
300
- if (_startIndex >= _strBytes.length) return "";
301
- if (_endIndex > _strBytes.length) _endIndex = _strBytes.length;
302
- _startIndex = _strBytes[_startIndex] == bytes1(0x20) ? _startIndex + 1 : _startIndex;
303
- if (_startIndex >= _endIndex) return "";
304
- bytes memory _result = new bytes(_endIndex - _startIndex);
305
- for (uint256 _i = _startIndex; _i < _endIndex;) {
306
- _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];
307
309
  unchecked {
308
- ++_i;
310
+ ++i;
309
311
  }
310
312
  }
311
- return string(_result);
313
+ return string(result);
312
314
  }
313
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);