@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.
- package/ADMINISTRATION.md +3 -3
- package/ARCHITECTURE.md +2 -0
- package/AUDIT_INSTRUCTIONS.md +422 -0
- package/CRYPTO_ECON.md +5 -5
- package/README.md +1 -1
- package/RISKS.md +38 -335
- package/SKILLS.md +1 -1
- package/USER_JOURNEYS.md +691 -0
- package/package.json +7 -7
- package/script/Deploy.s.sol +14 -3
- package/script/helpers/DefifaDeploymentLib.sol +13 -15
- package/src/DefifaDeployer.sol +221 -192
- package/src/DefifaGovernor.sol +286 -276
- package/src/DefifaHook.sol +68 -34
- package/src/DefifaProjectOwner.sol +27 -4
- package/src/DefifaTokenUriResolver.sol +136 -134
- package/src/enums/DefifaGamePhase.sol +1 -1
- package/src/enums/DefifaScorecardState.sol +1 -1
- package/src/interfaces/IDefifaDeployer.sol +52 -50
- package/src/interfaces/IDefifaGamePhaseReporter.sol +2 -2
- package/src/interfaces/IDefifaGamePotReporter.sol +1 -1
- package/src/interfaces/IDefifaGovernor.sol +53 -54
- package/src/interfaces/IDefifaHook.sol +104 -103
- package/src/interfaces/IDefifaTokenUriResolver.sol +2 -2
- package/src/libraries/DefifaFontImporter.sol +11 -9
- package/src/libraries/DefifaHookLib.sol +66 -53
- package/src/structs/DefifaAttestations.sol +1 -1
- package/src/structs/DefifaDelegation.sol +1 -1
- package/src/structs/DefifaLaunchProjectData.sol +4 -4
- package/src/structs/DefifaOpsData.sol +1 -1
- package/src/structs/DefifaScorecard.sol +1 -1
- package/src/structs/DefifaTierCashOutWeight.sol +1 -1
- package/src/structs/DefifaTierParams.sol +2 -1
- package/test/DefifaAdversarialQuorum.t.sol +602 -0
- package/test/DefifaAuditLowGuards.t.sol +304 -0
- package/test/DefifaFeeAccounting.t.sol +37 -16
- package/test/DefifaGovernor.t.sol +43 -19
- package/test/DefifaHookRegressions.t.sol +14 -12
- package/test/DefifaMintCostInvariant.t.sol +31 -12
- package/test/DefifaNoContest.t.sol +34 -16
- package/test/DefifaSecurity.t.sol +46 -28
- package/test/DefifaUSDC.t.sol +45 -36
- package/test/Fork.t.sol +43 -43
- package/test/SVG.t.sol +2 -2
- package/test/TestAuditGaps.sol +982 -0
- package/test/TestQALastMile.t.sol +511 -0
- package/test/regression/FulfillmentBlocksRatification.t.sol +36 -30
- 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 {
|
|
9
|
-
import {
|
|
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
|
-
//
|
|
27
|
+
// ----------------------- internal constants ------------------------ //
|
|
27
28
|
//*********************************************************************//
|
|
28
29
|
|
|
29
30
|
/// @notice The fidelity of the decimal returned in the NFT image.
|
|
30
|
-
uint256
|
|
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
|
|
38
|
+
ITypeface public immutable override TYPEFACE;
|
|
38
39
|
|
|
39
40
|
//*********************************************************************//
|
|
40
41
|
// -------------------------- constructor ---------------------------- //
|
|
41
42
|
//*********************************************************************//
|
|
42
43
|
|
|
43
|
-
constructor(ITypeface
|
|
44
|
-
|
|
44
|
+
constructor(ITypeface typeface) {
|
|
45
|
+
TYPEFACE = typeface;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
//*********************************************************************//
|
|
48
|
-
//
|
|
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
|
|
54
|
-
/// @param
|
|
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
|
|
57
|
+
function tokenUriOf(address nft, uint256 tokenId) external view override returns (string memory) {
|
|
57
58
|
// Keep a reference to the hook.
|
|
58
|
-
IDefifaHook
|
|
59
|
+
IDefifaHook hook = IDefifaHook(nft);
|
|
59
60
|
|
|
60
61
|
// Get the game ID.
|
|
61
|
-
uint256
|
|
62
|
+
uint256 gameId = hook.PROJECT_ID();
|
|
62
63
|
|
|
63
64
|
// Keep a reference to the game phase text.
|
|
64
|
-
string memory
|
|
65
|
+
string memory gamePhaseText;
|
|
65
66
|
|
|
66
67
|
// Keep a reference to the rarity text;
|
|
67
|
-
string memory
|
|
68
|
+
string memory rarityText;
|
|
68
69
|
|
|
69
70
|
// Keep a reference to the rarity text;
|
|
70
|
-
string memory
|
|
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
|
|
75
|
+
string memory title = ERC721(address(hook)).name();
|
|
75
76
|
|
|
76
77
|
// Keep a reference to the tier's name.
|
|
77
|
-
string memory
|
|
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
|
|
84
|
+
string memory potText;
|
|
84
85
|
|
|
85
86
|
{
|
|
86
87
|
// Get a reference to the tier.
|
|
87
|
-
JB721Tier memory
|
|
88
|
-
|
|
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
|
-
|
|
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 (
|
|
95
|
-
return JBIpfsDecoder.decode({baseUri:
|
|
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
|
-
|
|
104
|
+
team,
|
|
104
105
|
'", "id": "',
|
|
105
|
-
uint256(
|
|
106
|
+
uint256(tier.id).toString(),
|
|
106
107
|
'","description":"Team: ',
|
|
107
|
-
|
|
108
|
+
team,
|
|
108
109
|
", ID: ",
|
|
109
|
-
uint256(
|
|
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
|
|
117
|
+
DefifaGamePhase gamePhase = hook.gamePhaseReporter().currentGamePhaseOf(gameId);
|
|
117
118
|
|
|
118
119
|
// Keep a reference to the game pot.
|
|
119
|
-
(uint256
|
|
120
|
-
|
|
120
|
+
(uint256 gamePot, address gamePotToken, uint256 gamePotDecimals) =
|
|
121
|
+
hook.gamePotReporter().currentGamePotOf({gameId: gameId, includeCommitments: false});
|
|
121
122
|
|
|
122
123
|
// Include the amount redeemed.
|
|
123
|
-
|
|
124
|
+
gamePot = gamePot + hook.amountRedeemed();
|
|
124
125
|
|
|
125
126
|
// Set the pot text.
|
|
126
|
-
|
|
127
|
-
|
|
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 (
|
|
134
|
-
|
|
135
|
-
} else if (
|
|
136
|
-
|
|
137
|
-
} else if (
|
|
138
|
-
|
|
139
|
-
} else if (
|
|
140
|
-
|
|
141
|
-
} else if (
|
|
142
|
-
|
|
143
|
-
} else if (
|
|
144
|
-
|
|
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
|
|
146
|
+
uint256 totalMinted = hook.currentSupplyOfTier(tier.id);
|
|
149
147
|
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
abi.encodePacked(
|
|
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
|
-
|
|
153
|
+
rarityText = string(
|
|
156
154
|
abi.encodePacked(
|
|
157
|
-
|
|
155
|
+
totalMinted.toString(), totalMinted == 1 ? " card in existence" : " cards in existence"
|
|
158
156
|
)
|
|
159
157
|
);
|
|
160
158
|
}
|
|
161
159
|
|
|
162
|
-
if (
|
|
163
|
-
uint256
|
|
164
|
-
x:
|
|
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
|
-
|
|
164
|
+
valueText = !hook.cashOutWeightIsSet()
|
|
167
165
|
? "Awaiting scorecard..."
|
|
168
166
|
: _formatBalance({
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
amount: potPortion,
|
|
168
|
+
token: gamePotToken,
|
|
169
|
+
decimals: gamePotDecimals,
|
|
170
|
+
fidelity: _IMG_DECIMAL_FIDELITY
|
|
173
171
|
});
|
|
174
172
|
} else {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
194
|
+
gameId.toString(),
|
|
197
195
|
" | POT: ",
|
|
198
|
-
|
|
196
|
+
potText,
|
|
199
197
|
" | CARDS: ",
|
|
200
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
213
|
-
? _getSubstring(
|
|
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(
|
|
218
|
-
? _getSubstring(
|
|
219
|
-
: bytes(_getSubstring(
|
|
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(
|
|
223
|
-
? _getSubstring(
|
|
224
|
-
: bytes(_getSubstring(
|
|
225
|
-
? _getSubstring(
|
|
226
|
-
: _getSubstring(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
247
|
-
/// @param
|
|
248
|
-
/// @param
|
|
249
|
-
/// @param
|
|
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
|
|
253
|
-
address
|
|
254
|
-
uint256
|
|
255
|
-
uint256
|
|
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
|
|
263
|
+
bool isEth = token == JBConstants.NATIVE_TOKEN;
|
|
262
264
|
|
|
263
|
-
uint256
|
|
265
|
+
uint256 fixedPoint = 10 ** decimals;
|
|
264
266
|
|
|
265
267
|
// Convert amount to a decimal format
|
|
266
|
-
string memory
|
|
268
|
+
string memory integerPart = (amount / fixedPoint).toString();
|
|
267
269
|
|
|
268
|
-
uint256
|
|
269
|
-
uint256
|
|
270
|
-
uint256
|
|
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
|
|
274
|
-
while (bytes(
|
|
275
|
-
|
|
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
|
|
280
|
-
? string(abi.encodePacked("\u039E",
|
|
281
|
-
: string(abi.encodePacked(
|
|
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
|
|
287
|
-
/// @param
|
|
288
|
-
/// @param
|
|
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
|
|
292
|
-
uint256
|
|
293
|
-
uint256
|
|
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
|
|
300
|
-
if (
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
if (
|
|
304
|
-
bytes memory
|
|
305
|
-
for (uint256
|
|
306
|
-
|
|
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
|
-
++
|
|
310
|
+
++i;
|
|
309
311
|
}
|
|
310
312
|
}
|
|
311
|
-
return string(
|
|
313
|
+
return string(result);
|
|
312
314
|
}
|
|
313
315
|
}
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
35
|
-
/// @return The
|
|
36
|
-
function
|
|
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
|
|
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
|
|
58
|
+
function GOVERNOR() external view returns (IDefifaGovernor);
|
|
57
59
|
|
|
58
|
-
/// @notice The
|
|
59
|
-
/// @return The
|
|
60
|
-
function
|
|
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
|
|
66
|
+
function REGISTRY() external view returns (IJBAddressRegistry);
|
|
65
67
|
|
|
66
|
-
/// @notice The
|
|
67
|
-
/// @return The
|
|
68
|
-
function
|
|
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
|
|
71
|
-
/// @return The
|
|
72
|
-
function
|
|
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
|
|
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
|
-
|
|
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.
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
import {DefifaGamePhase} from "
|
|
4
|
+
import {DefifaGamePhase} from "../enums/DefifaGamePhase.sol";
|
|
5
5
|
|
|
6
6
|
interface IDefifaGamePhaseReporter {
|
|
7
7
|
function currentGamePhaseOf(uint256 gameId) external view returns (DefifaGamePhase);
|