@ballkidz/defifa 0.0.1
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/.gas-snapshot +2 -0
- package/CRYPTO_ECON.md +955 -0
- package/CRYPTO_ECON.pdf +0 -0
- package/CRYPTO_ECON.tex +800 -0
- package/README.md +119 -0
- package/SKILLS.md +177 -0
- package/deployments/defifa-v5/arbitrum_sepolia/DefifaDelegate.json +4867 -0
- package/deployments/defifa-v5/arbitrum_sepolia/DefifaDeployer.json +1719 -0
- package/deployments/defifa-v5/arbitrum_sepolia/DefifaGovernor.json +1535 -0
- package/deployments/defifa-v5/arbitrum_sepolia/DefifaTokenUriResolver.json +295 -0
- package/deployments/defifa-v5/base_sepolia/DefifaDelegate.json +4875 -0
- package/deployments/defifa-v5/base_sepolia/DefifaDeployer.json +1725 -0
- package/deployments/defifa-v5/base_sepolia/DefifaGovernor.json +1543 -0
- package/deployments/defifa-v5/base_sepolia/DefifaTokenUriResolver.json +301 -0
- package/deployments/defifa-v5/optimism_sepolia/DefifaDelegate.json +4875 -0
- package/deployments/defifa-v5/optimism_sepolia/DefifaDeployer.json +1725 -0
- package/deployments/defifa-v5/optimism_sepolia/DefifaGovernor.json +1543 -0
- package/deployments/defifa-v5/optimism_sepolia/DefifaTokenUriResolver.json +301 -0
- package/deployments/defifa-v5/sepolia/DefifaDelegate.json +4875 -0
- package/deployments/defifa-v5/sepolia/DefifaDeployer.json +1725 -0
- package/deployments/defifa-v5/sepolia/DefifaGovernor.json +1543 -0
- package/deployments/defifa-v5/sepolia/DefifaTokenUriResolver.json +301 -0
- package/foundry.lock +17 -0
- package/foundry.toml +35 -0
- package/package.json +33 -0
- package/remappings.txt +6 -0
- package/script/Deploy.s.sol +109 -0
- package/script/helpers/DefifaDeploymentLib.sol +83 -0
- package/slither-ci.config.json +10 -0
- package/sphinx.lock +521 -0
- package/src/DefifaDeployer.sol +894 -0
- package/src/DefifaGovernor.sol +490 -0
- package/src/DefifaHook.sol +1056 -0
- package/src/DefifaProjectOwner.sol +63 -0
- package/src/DefifaTokenUriResolver.sol +312 -0
- package/src/enums/DefifaGamePhase.sol +11 -0
- package/src/enums/DefifaScorecardState.sol +10 -0
- package/src/interfaces/IDefifaDeployer.sol +108 -0
- package/src/interfaces/IDefifaGamePhaseReporter.sol +8 -0
- package/src/interfaces/IDefifaGamePotReporter.sol +8 -0
- package/src/interfaces/IDefifaGovernor.sol +132 -0
- package/src/interfaces/IDefifaHook.sol +228 -0
- package/src/interfaces/IDefifaTokenUriResolver.sol +10 -0
- package/src/libraries/DefifaFontImporter.sol +19 -0
- package/src/libraries/DefifaHookLib.sol +358 -0
- package/src/structs/DefifaAttestations.sol +9 -0
- package/src/structs/DefifaDelegation.sol +9 -0
- package/src/structs/DefifaLaunchProjectData.sol +59 -0
- package/src/structs/DefifaOpsData.sol +20 -0
- package/src/structs/DefifaScorecard.sol +9 -0
- package/src/structs/DefifaTierCashOutWeight.sol +9 -0
- package/src/structs/DefifaTierParams.sol +16 -0
- package/test/DefifaFeeAccounting.t.sol +559 -0
- package/test/DefifaGovernor.t.sol +1333 -0
- package/test/DefifaMintCostInvariant.t.sol +299 -0
- package/test/DefifaNoContest.t.sol +922 -0
- package/test/DefifaSecurity.t.sol +717 -0
- package/test/SVG.t.sol +164 -0
- package/test/deployScript.t.sol +144 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
|
|
5
|
+
import {DefifaDeployer} from "./DefifaDeployer.sol";
|
|
6
|
+
import {IJBPermissions, JBPermissionsData} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
7
|
+
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
8
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
9
|
+
|
|
10
|
+
/// @notice A contract that can be sent a project to be burned, while still allowing defifa permissions.
|
|
11
|
+
/// @dev Once the project NFT is transferred here, it cannot be recovered. This contract permanently
|
|
12
|
+
/// holds the project NFT and grants SET_SPLIT_GROUPS permission to the Defifa deployer.
|
|
13
|
+
contract DefifaProjectOwner is IERC721Receiver {
|
|
14
|
+
/// @notice The contract where operator permissions are stored.
|
|
15
|
+
IJBPermissions public immutable PERMISSIONS;
|
|
16
|
+
|
|
17
|
+
/// @notice The contract from which projects are minted.
|
|
18
|
+
IJBProjects public immutable PROJECTS;
|
|
19
|
+
|
|
20
|
+
/// @notice The Defifa deployer.
|
|
21
|
+
DefifaDeployer public immutable DEPLOYER;
|
|
22
|
+
|
|
23
|
+
/// @param permissions The contract where operator permissions are stored.
|
|
24
|
+
/// @param projects The contract from which projects are minted.
|
|
25
|
+
/// @param deployer The Defifa deployer which will receive permissions to set splits.
|
|
26
|
+
constructor(IJBPermissions permissions, IJBProjects projects, DefifaDeployer deployer) {
|
|
27
|
+
PERMISSIONS = permissions;
|
|
28
|
+
PROJECTS = projects;
|
|
29
|
+
DEPLOYER = deployer;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// @notice Give the defifa deployer permission to set splits on this contract's behalf.
|
|
33
|
+
function onERC721Received(
|
|
34
|
+
address operator,
|
|
35
|
+
address from,
|
|
36
|
+
uint256 tokenId,
|
|
37
|
+
bytes calldata data
|
|
38
|
+
)
|
|
39
|
+
external
|
|
40
|
+
returns (bytes4)
|
|
41
|
+
{
|
|
42
|
+
data;
|
|
43
|
+
from;
|
|
44
|
+
operator;
|
|
45
|
+
|
|
46
|
+
// Make sure the 721 received is the JBProjects contract.
|
|
47
|
+
if (msg.sender != address(PROJECTS)) revert();
|
|
48
|
+
|
|
49
|
+
// Set the correct permission.
|
|
50
|
+
uint8[] memory permissionIds = new uint8[](1);
|
|
51
|
+
permissionIds[0] = JBPermissionIds.SET_SPLIT_GROUPS;
|
|
52
|
+
|
|
53
|
+
// Give the defifa deployer contract permission to set splits on this contract's behalf.
|
|
54
|
+
PERMISSIONS.setPermissionsFor({
|
|
55
|
+
account: address(this),
|
|
56
|
+
permissionsData: JBPermissionsData({
|
|
57
|
+
operator: address(DEPLOYER), projectId: uint64(tokenId), permissionIds: permissionIds
|
|
58
|
+
})
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return IERC721Receiver.onERC721Received.selector;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
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
|
+
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";
|
|
13
|
+
|
|
14
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
15
|
+
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
16
|
+
import {ERC721} from "@bananapus/721-hook-v6/src/abstract/ERC721.sol";
|
|
17
|
+
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
18
|
+
import {JBIpfsDecoder} from "@bananapus/721-hook-v6/src/libraries/JBIpfsDecoder.sol";
|
|
19
|
+
|
|
20
|
+
/// @title DefifaTokenUriResolver
|
|
21
|
+
/// @notice Standard Token URIs for Defifa games.
|
|
22
|
+
contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolver {
|
|
23
|
+
using Strings for uint256;
|
|
24
|
+
|
|
25
|
+
//*********************************************************************//
|
|
26
|
+
// -------------------- private constant properties ------------------ //
|
|
27
|
+
//*********************************************************************//
|
|
28
|
+
|
|
29
|
+
/// @notice The fidelity of the decimal returned in the NFT image.
|
|
30
|
+
uint256 private constant _IMG_DECIMAL_FIDELITY = 5;
|
|
31
|
+
|
|
32
|
+
//*********************************************************************//
|
|
33
|
+
// --------------- public immutable stored properties ---------------- //
|
|
34
|
+
//*********************************************************************//
|
|
35
|
+
|
|
36
|
+
/// @notice The typeface of the SVGs.
|
|
37
|
+
ITypeface public immutable override typeface;
|
|
38
|
+
|
|
39
|
+
//*********************************************************************//
|
|
40
|
+
// -------------------------- constructor ---------------------------- //
|
|
41
|
+
//*********************************************************************//
|
|
42
|
+
|
|
43
|
+
constructor(ITypeface _typeface) {
|
|
44
|
+
typeface = _typeface;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//*********************************************************************//
|
|
48
|
+
// ---------------------- external transactions ---------------------- //
|
|
49
|
+
//*********************************************************************//
|
|
50
|
+
|
|
51
|
+
/// @notice The metadata URI of the provided token ID.
|
|
52
|
+
/// @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.
|
|
55
|
+
/// @return The token URI corresponding with the tier.
|
|
56
|
+
function tokenUriOf(address _nft, uint256 _tokenId) external view override returns (string memory) {
|
|
57
|
+
// Keep a reference to the hook.
|
|
58
|
+
IDefifaHook _hook = IDefifaHook(_nft);
|
|
59
|
+
|
|
60
|
+
// Get the game ID.
|
|
61
|
+
uint256 _gameId = _hook.PROJECT_ID();
|
|
62
|
+
|
|
63
|
+
// Keep a reference to the game phase text.
|
|
64
|
+
string memory _gamePhaseText;
|
|
65
|
+
|
|
66
|
+
// Keep a reference to the rarity text;
|
|
67
|
+
string memory _rarityText;
|
|
68
|
+
|
|
69
|
+
// Keep a reference to the rarity text;
|
|
70
|
+
string memory _valueText;
|
|
71
|
+
|
|
72
|
+
// Keep a reference to the game's name.
|
|
73
|
+
// TODO: Somehow make the `IDefifaHook` have the `name` function.
|
|
74
|
+
string memory _title = ERC721(address(_hook)).name();
|
|
75
|
+
|
|
76
|
+
// Keep a reference to the tier's name.
|
|
77
|
+
string memory _team;
|
|
78
|
+
|
|
79
|
+
// Keep a reference to the SVG parts.
|
|
80
|
+
string[] memory parts = new string[](4);
|
|
81
|
+
|
|
82
|
+
// Keep a reference to the pot.
|
|
83
|
+
string memory _potText;
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
// Get a reference to the tier.
|
|
87
|
+
JB721Tier memory _tier =
|
|
88
|
+
_hook.store().tierOfTokenId({hook: address(_hook), tokenId: _tokenId, includeResolvedUri: false});
|
|
89
|
+
|
|
90
|
+
// Set the tier's name.
|
|
91
|
+
_team = _hook.tierNameOf(_tier.id);
|
|
92
|
+
|
|
93
|
+
// 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});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
parts[0] = string("data:application/json;base64,");
|
|
99
|
+
|
|
100
|
+
parts[1] = string(
|
|
101
|
+
abi.encodePacked(
|
|
102
|
+
'{"name":"',
|
|
103
|
+
_team,
|
|
104
|
+
'", "id": "',
|
|
105
|
+
uint256(_tier.id).toString(),
|
|
106
|
+
'","description":"Team: ',
|
|
107
|
+
_team,
|
|
108
|
+
", ID: ",
|
|
109
|
+
uint256(_tier.id).toString(),
|
|
110
|
+
'.","image":"data:image/svg+xml;base64,'
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
{
|
|
115
|
+
// Get a reference to the game phase.
|
|
116
|
+
DefifaGamePhase _gamePhase = _hook.gamePhaseReporter().currentGamePhaseOf(_gameId);
|
|
117
|
+
|
|
118
|
+
// Keep a reference to the game pot.
|
|
119
|
+
(uint256 _gamePot, address _gamePotToken, uint256 _gamePotDecimals) =
|
|
120
|
+
_hook.gamePotReporter().currentGamePotOf({gameId: _gameId, includeCommitments: false});
|
|
121
|
+
|
|
122
|
+
// Include the amount redeemed.
|
|
123
|
+
_gamePot = _gamePot + _hook.amountRedeemed();
|
|
124
|
+
|
|
125
|
+
// Set the pot text.
|
|
126
|
+
_potText = _formatBalance({
|
|
127
|
+
_amount: _gamePot,
|
|
128
|
+
_token: _gamePotToken,
|
|
129
|
+
_decimals: _gamePotDecimals,
|
|
130
|
+
_fidelity: _IMG_DECIMAL_FIDELITY
|
|
131
|
+
});
|
|
132
|
+
|
|
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.";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Keep a reference to the number of tokens outstanding from this tier.
|
|
148
|
+
uint256 _totalMinted = _hook.currentSupplyOfTier(_tier.id);
|
|
149
|
+
|
|
150
|
+
if (_gamePhase == DefifaGamePhase.MINT) {
|
|
151
|
+
_rarityText = string(
|
|
152
|
+
abi.encodePacked(_totalMinted.toString(), _totalMinted == 1 ? " card so far" : " cards so far")
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
_rarityText = string(
|
|
156
|
+
abi.encodePacked(
|
|
157
|
+
_totalMinted.toString(), _totalMinted == 1 ? " card in existence" : " cards in existence"
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
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()
|
|
166
|
+
? "Awaiting scorecard..."
|
|
167
|
+
: _formatBalance({
|
|
168
|
+
_amount: _potPortion,
|
|
169
|
+
_token: _gamePotToken,
|
|
170
|
+
_decimals: _gamePotDecimals,
|
|
171
|
+
_fidelity: _IMG_DECIMAL_FIDELITY
|
|
172
|
+
});
|
|
173
|
+
} else {
|
|
174
|
+
_valueText = _formatBalance({
|
|
175
|
+
_amount: _tier.price,
|
|
176
|
+
_token: _gamePotToken,
|
|
177
|
+
_decimals: _gamePotDecimals,
|
|
178
|
+
_fidelity: _IMG_DECIMAL_FIDELITY
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
parts[2] = Base64.encode(
|
|
184
|
+
abi.encodePacked(
|
|
185
|
+
'<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">',
|
|
186
|
+
'<style>@font-face{font-family:"Capsules-500";src:url(data:font/truetype;charset=utf-8;base64,',
|
|
187
|
+
DefifaFontImporter.getSkinnyFontSource(typeface),
|
|
188
|
+
');format("opentype");}',
|
|
189
|
+
'@font-face{font-family:"Capsules-700";src:url(data:font/truetype;charset=utf-8;base64,',
|
|
190
|
+
DefifaFontImporter.getBeefyFontSource(typeface),
|
|
191
|
+
');format("opentype");}',
|
|
192
|
+
"text{white-space:pre-wrap; width:100%; }</style>",
|
|
193
|
+
'<rect width="100%" height="100%" fill="#181424"/>',
|
|
194
|
+
'<text x="10" y="30" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">GAME: ',
|
|
195
|
+
_gameId.toString(),
|
|
196
|
+
" | POT: ",
|
|
197
|
+
_potText,
|
|
198
|
+
" | CARDS: ",
|
|
199
|
+
_hook.store().totalSupplyOf(address(_hook)).toString(),
|
|
200
|
+
"</text>",
|
|
201
|
+
'<text x="10" y="50" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #ed017c;">',
|
|
202
|
+
_gamePhaseText,
|
|
203
|
+
"</text>",
|
|
204
|
+
'<text x="10" y="85" style="font-size:26px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">',
|
|
205
|
+
_getSubstring(_title, 0, 30),
|
|
206
|
+
"</text>",
|
|
207
|
+
'<text x="10" y="120" style="font-size:26px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">',
|
|
208
|
+
_getSubstring(_title, 30, 60),
|
|
209
|
+
"</text>",
|
|
210
|
+
'<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)
|
|
213
|
+
: "",
|
|
214
|
+
"</text>",
|
|
215
|
+
'<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) : "",
|
|
219
|
+
"</text>",
|
|
220
|
+
'<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),
|
|
226
|
+
"</text>",
|
|
227
|
+
'<text x="10" y="430" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">TOKEN ID: ',
|
|
228
|
+
_tokenId.toString(),
|
|
229
|
+
"</text>",
|
|
230
|
+
'<text x="10" y="455" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">RARITY: ',
|
|
231
|
+
_rarityText,
|
|
232
|
+
"</text>",
|
|
233
|
+
'<text x="10" y="480" style="font-size:16px; font-family: Capsules-500; font-weight:500; fill: #c0b3f1;">BACKED BY: ',
|
|
234
|
+
_valueText,
|
|
235
|
+
"</text>",
|
|
236
|
+
"</svg>"
|
|
237
|
+
)
|
|
238
|
+
);
|
|
239
|
+
parts[3] = string('"}');
|
|
240
|
+
// slither-disable-next-line encode-packed-collision
|
|
241
|
+
return string.concat(parts[0], Base64.encode(abi.encodePacked(parts[1], parts[2], parts[3])));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/// @notice Gets a substring.
|
|
245
|
+
/// @dev If the first character is a space, it is not included.
|
|
246
|
+
/// @param _str The string to get a substring of.
|
|
247
|
+
/// @param _startIndex The first index of the substring from within the string.
|
|
248
|
+
/// @param _endIndex The last index of the string from within the string.
|
|
249
|
+
/// @return substring The substring.
|
|
250
|
+
function _getSubstring(
|
|
251
|
+
string memory _str,
|
|
252
|
+
uint256 _startIndex,
|
|
253
|
+
uint256 _endIndex
|
|
254
|
+
)
|
|
255
|
+
internal
|
|
256
|
+
pure
|
|
257
|
+
returns (string memory substring)
|
|
258
|
+
{
|
|
259
|
+
bytes memory _strBytes = bytes(_str);
|
|
260
|
+
if (_startIndex >= _strBytes.length) return "";
|
|
261
|
+
if (_endIndex > _strBytes.length) _endIndex = _strBytes.length;
|
|
262
|
+
_startIndex = _strBytes[_startIndex] == bytes1(0x20) ? _startIndex + 1 : _startIndex;
|
|
263
|
+
if (_startIndex >= _endIndex) return "";
|
|
264
|
+
bytes memory _result = new bytes(_endIndex - _startIndex);
|
|
265
|
+
for (uint256 _i = _startIndex; _i < _endIndex;) {
|
|
266
|
+
_result[_i - _startIndex] = _strBytes[_i];
|
|
267
|
+
unchecked {
|
|
268
|
+
++_i;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return string(_result);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// @notice Formats a balance from a fixed point number to a string.
|
|
275
|
+
/// @param _amount The fixed point amount.
|
|
276
|
+
/// @param _token The token the amount is in.
|
|
277
|
+
/// @param _decimals The number of decimals in the fixed point amount.
|
|
278
|
+
/// @param _fidelity The number of decimals that should be returned in the formatted string.
|
|
279
|
+
/// @return The formatted balance.
|
|
280
|
+
function _formatBalance(
|
|
281
|
+
uint256 _amount,
|
|
282
|
+
address _token,
|
|
283
|
+
uint256 _decimals,
|
|
284
|
+
uint256 _fidelity
|
|
285
|
+
)
|
|
286
|
+
internal
|
|
287
|
+
view
|
|
288
|
+
returns (string memory)
|
|
289
|
+
{
|
|
290
|
+
bool _isEth = _token == JBConstants.NATIVE_TOKEN;
|
|
291
|
+
|
|
292
|
+
uint256 _fixedPoint = 10 ** _decimals;
|
|
293
|
+
|
|
294
|
+
// Convert amount to a decimal format
|
|
295
|
+
string memory _integerPart = (_amount / _fixedPoint).toString();
|
|
296
|
+
|
|
297
|
+
uint256 _remainder = _amount % _fixedPoint;
|
|
298
|
+
uint256 _scaledRemainder = _remainder * (10 ** _fidelity);
|
|
299
|
+
uint256 _decimalPart = _scaledRemainder / _fixedPoint;
|
|
300
|
+
|
|
301
|
+
// Pad with zeros if necessary
|
|
302
|
+
string memory _decimalPartStr = _decimalPart.toString();
|
|
303
|
+
while (bytes(_decimalPartStr).length < _fidelity) {
|
|
304
|
+
_decimalPartStr = string(abi.encodePacked("0", _decimalPartStr));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Concatenate the strings
|
|
308
|
+
return _isEth
|
|
309
|
+
? string(abi.encodePacked("\u039E", _integerPart, ".", _decimalPartStr))
|
|
310
|
+
: string(abi.encodePacked(_integerPart, ".", _decimalPartStr, " ", IERC20Metadata(_token).symbol()));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {DefifaLaunchProjectData} from "../structs/DefifaLaunchProjectData.sol";
|
|
5
|
+
import {DefifaOpsData} from "../structs/DefifaOpsData.sol";
|
|
6
|
+
import {IDefifaHook} from "./IDefifaHook.sol";
|
|
7
|
+
import {IDefifaGovernor} from "./IDefifaGovernor.sol";
|
|
8
|
+
|
|
9
|
+
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
10
|
+
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
11
|
+
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
12
|
+
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
13
|
+
|
|
14
|
+
/// @notice Deploys and manages Defifa prediction games, including lifecycle phase transitions
|
|
15
|
+
/// and commitment fulfillment.
|
|
16
|
+
interface IDefifaDeployer {
|
|
17
|
+
event LaunchGame(
|
|
18
|
+
uint256 indexed gameId,
|
|
19
|
+
IDefifaHook indexed hook,
|
|
20
|
+
IDefifaGovernor indexed governor,
|
|
21
|
+
IJB721TokenUriResolver tokenUriResolver,
|
|
22
|
+
address caller
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
event QueuedRefundPhase(uint256 indexed gameId, address caller);
|
|
26
|
+
|
|
27
|
+
event QueuedScoringPhase(uint256 indexed gameId, address caller);
|
|
28
|
+
|
|
29
|
+
event QueuedNoContest(uint256 indexed gameId, address caller);
|
|
30
|
+
|
|
31
|
+
event FulfilledCommitments(uint256 indexed gameId, uint256 pot, address caller);
|
|
32
|
+
|
|
33
|
+
event DistributeToSplit(JBSplit split, uint256 amount, address caller);
|
|
34
|
+
|
|
35
|
+
/// @notice The split group ID used for distributing game pot funds.
|
|
36
|
+
/// @return The split group.
|
|
37
|
+
function splitGroup() external view returns (uint256);
|
|
38
|
+
|
|
39
|
+
/// @notice The Juicebox project ID of the Defifa project.
|
|
40
|
+
/// @return The project ID.
|
|
41
|
+
function defifaProjectId() external view returns (uint256);
|
|
42
|
+
|
|
43
|
+
/// @notice The Juicebox project ID of the base protocol project.
|
|
44
|
+
/// @return The project ID.
|
|
45
|
+
function baseProtocolProjectId() external view returns (uint256);
|
|
46
|
+
|
|
47
|
+
/// @notice The code origin address used as an implementation for hook clones.
|
|
48
|
+
/// @return The code origin address.
|
|
49
|
+
function hookCodeOrigin() external view returns (address);
|
|
50
|
+
|
|
51
|
+
/// @notice The token URI resolver used for game NFT metadata.
|
|
52
|
+
/// @return The token URI resolver contract.
|
|
53
|
+
function tokenUriResolver() external view returns (IJB721TokenUriResolver);
|
|
54
|
+
|
|
55
|
+
/// @notice The governor contract used for scorecard governance.
|
|
56
|
+
/// @return The governor contract.
|
|
57
|
+
function governor() external view returns (IDefifaGovernor);
|
|
58
|
+
|
|
59
|
+
/// @notice The Juicebox controller used to manage projects.
|
|
60
|
+
/// @return The controller contract.
|
|
61
|
+
function controller() external view returns (IJBController);
|
|
62
|
+
|
|
63
|
+
/// @notice The address registry used for content-addressable deployment lookups.
|
|
64
|
+
/// @return The address registry contract.
|
|
65
|
+
function registry() external view returns (IJBAddressRegistry);
|
|
66
|
+
|
|
67
|
+
/// @notice The fee divisor for Defifa fees (100 / fee percent).
|
|
68
|
+
/// @return The fee divisor.
|
|
69
|
+
function DEFIFA_FEE_DIVISOR() external view returns (uint256);
|
|
70
|
+
|
|
71
|
+
/// @notice The fee divisor for base protocol fees (100 / fee percent).
|
|
72
|
+
/// @return The fee divisor.
|
|
73
|
+
function BASE_PROTOCOL_FEE_DIVISOR() external view returns (uint256);
|
|
74
|
+
|
|
75
|
+
/// @notice The timing parameters for a game.
|
|
76
|
+
/// @param gameId The ID of the game.
|
|
77
|
+
/// @return The mint duration, start time, and refund period.
|
|
78
|
+
function timesFor(uint256 gameId) external view returns (uint48, uint24, uint24);
|
|
79
|
+
|
|
80
|
+
/// @notice The token address for a game.
|
|
81
|
+
/// @param gameId The ID of the game.
|
|
82
|
+
/// @return The token address.
|
|
83
|
+
function tokenOf(uint256 gameId) external view returns (address);
|
|
84
|
+
|
|
85
|
+
/// @notice The safety parameters for a game.
|
|
86
|
+
/// @param gameId The ID of the game.
|
|
87
|
+
/// @return minParticipation The minimum participation threshold.
|
|
88
|
+
/// @return scorecardTimeout The scorecard timeout duration.
|
|
89
|
+
function safetyParamsOf(uint256 gameId) external view returns (uint256 minParticipation, uint32 scorecardTimeout);
|
|
90
|
+
|
|
91
|
+
/// @notice Whether the next game phase needs to be queued.
|
|
92
|
+
/// @param gameId The ID of the game.
|
|
93
|
+
/// @return True if the next phase needs queueing.
|
|
94
|
+
function nextPhaseNeedsQueueing(uint256 gameId) external view returns (bool);
|
|
95
|
+
|
|
96
|
+
/// @notice Launch a new Defifa game.
|
|
97
|
+
/// @param launchProjectData The configuration for launching the game.
|
|
98
|
+
/// @return gameId The ID of the newly launched game.
|
|
99
|
+
function launchGameWith(DefifaLaunchProjectData calldata launchProjectData) external returns (uint256 gameId);
|
|
100
|
+
|
|
101
|
+
/// @notice Fulfill the commitments of a game by distributing the pot.
|
|
102
|
+
/// @param gameId The ID of the game.
|
|
103
|
+
function fulfillCommitmentsOf(uint256 gameId) external;
|
|
104
|
+
|
|
105
|
+
/// @notice Trigger a no-contest outcome for a game.
|
|
106
|
+
/// @param gameId The ID of the game.
|
|
107
|
+
function triggerNoContestFor(uint256 gameId) external;
|
|
108
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
interface IDefifaGamePotReporter {
|
|
5
|
+
function fulfilledCommitmentsOf(uint256 gameId) external view returns (uint256);
|
|
6
|
+
|
|
7
|
+
function currentGamePotOf(uint256 gameId, bool includeCommitments) external view returns (uint256, address, uint256);
|
|
8
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
import {DefifaScorecardState} from "../enums/DefifaScorecardState.sol";
|
|
5
|
+
import {DefifaTierCashOutWeight} from "../structs/DefifaTierCashOutWeight.sol";
|
|
6
|
+
import {IDefifaHook} from "./IDefifaHook.sol";
|
|
7
|
+
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
8
|
+
|
|
9
|
+
/// @notice Manages the ratification of Defifa scorecards through attestation-based governance.
|
|
10
|
+
interface IDefifaGovernor {
|
|
11
|
+
event GameInitialized(
|
|
12
|
+
uint256 indexed gameId, uint256 attestationStartTime, uint256 attestationGracePeriod, address caller
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
event ScorecardSubmitted(
|
|
16
|
+
uint256 indexed gameId,
|
|
17
|
+
uint256 indexed scorecardId,
|
|
18
|
+
DefifaTierCashOutWeight[] tierWeights,
|
|
19
|
+
bool isDefaultAttestationDelegate,
|
|
20
|
+
address caller
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
event ScorecardAttested(uint256 indexed gameId, uint256 indexed scorecardId, uint256 weight, address caller);
|
|
24
|
+
|
|
25
|
+
event ScorecardRatified(uint256 indexed gameId, uint256 indexed scorecardId, address caller);
|
|
26
|
+
|
|
27
|
+
/// @notice The maximum tier ID that contributes attestation power.
|
|
28
|
+
/// @return The maximum attestation power tier.
|
|
29
|
+
function MAX_ATTESTATION_POWER_TIER() external view returns (uint256);
|
|
30
|
+
|
|
31
|
+
/// @notice The Juicebox controller used to manage projects.
|
|
32
|
+
/// @return The controller contract.
|
|
33
|
+
function controller() external view returns (IJBController);
|
|
34
|
+
|
|
35
|
+
/// @notice The scorecard proposal submitted by the default attestation delegate for a game.
|
|
36
|
+
/// @param gameId The ID of the game.
|
|
37
|
+
/// @return The scorecard ID.
|
|
38
|
+
function defaultAttestationDelegateProposalOf(uint256 gameId) external view returns (uint256);
|
|
39
|
+
|
|
40
|
+
/// @notice The ID of the ratified scorecard for a game.
|
|
41
|
+
/// @param gameId The ID of the game.
|
|
42
|
+
/// @return The ratified scorecard ID, or 0 if none.
|
|
43
|
+
function ratifiedScorecardIdOf(uint256 gameId) external view returns (uint256);
|
|
44
|
+
|
|
45
|
+
/// @notice Compute the scorecard ID for a given hook and tier weights.
|
|
46
|
+
/// @param gameHook The game hook address.
|
|
47
|
+
/// @param tierWeights The tier cash out weights.
|
|
48
|
+
/// @return The scorecard ID.
|
|
49
|
+
function scorecardIdOf(address gameHook, DefifaTierCashOutWeight[] calldata tierWeights) external returns (uint256);
|
|
50
|
+
|
|
51
|
+
/// @notice The state of a scorecard.
|
|
52
|
+
/// @param gameId The ID of the game.
|
|
53
|
+
/// @param scorecardId The ID of the scorecard.
|
|
54
|
+
/// @return The scorecard state.
|
|
55
|
+
function stateOf(uint256 gameId, uint256 scorecardId) external view returns (DefifaScorecardState);
|
|
56
|
+
|
|
57
|
+
/// @notice Get the attestation weight for an account at a specific timestamp.
|
|
58
|
+
/// @param gameId The ID of the game.
|
|
59
|
+
/// @param account The account to check.
|
|
60
|
+
/// @param timestamp The timestamp to check.
|
|
61
|
+
/// @return attestationPower The attestation power.
|
|
62
|
+
function getAttestationWeight(
|
|
63
|
+
uint256 gameId,
|
|
64
|
+
address account,
|
|
65
|
+
uint48 timestamp
|
|
66
|
+
)
|
|
67
|
+
external
|
|
68
|
+
view
|
|
69
|
+
returns (uint256 attestationPower);
|
|
70
|
+
|
|
71
|
+
/// @notice The number of attestations for a scorecard.
|
|
72
|
+
/// @param gameId The ID of the game.
|
|
73
|
+
/// @param scorecardId The ID of the scorecard.
|
|
74
|
+
/// @return The attestation count.
|
|
75
|
+
function attestationCountOf(uint256 gameId, uint256 scorecardId) external view returns (uint256);
|
|
76
|
+
|
|
77
|
+
/// @notice Whether an account has attested to a specific scorecard.
|
|
78
|
+
/// @param gameId The ID of the game.
|
|
79
|
+
/// @param scorecardId The ID of the scorecard.
|
|
80
|
+
/// @param account The account to check.
|
|
81
|
+
/// @return True if the account has attested.
|
|
82
|
+
function hasAttestedTo(uint256 gameId, uint256 scorecardId, address account) external view returns (bool);
|
|
83
|
+
|
|
84
|
+
/// @notice The timestamp when attestation begins for a game.
|
|
85
|
+
/// @param gameId The ID of the game.
|
|
86
|
+
/// @return The attestation start time.
|
|
87
|
+
function attestationStartTimeOf(uint256 gameId) external view returns (uint256);
|
|
88
|
+
|
|
89
|
+
/// @notice The grace period after attestation starts during which attestation is still allowed.
|
|
90
|
+
/// @param gameId The ID of the game.
|
|
91
|
+
/// @return The grace period in seconds.
|
|
92
|
+
function attestationGracePeriodOf(uint256 gameId) external view returns (uint256);
|
|
93
|
+
|
|
94
|
+
/// @notice The quorum required to ratify a scorecard.
|
|
95
|
+
/// @param gameId The ID of the game.
|
|
96
|
+
/// @return The quorum threshold.
|
|
97
|
+
function quorum(uint256 gameId) external view returns (uint256);
|
|
98
|
+
|
|
99
|
+
/// @notice Initialize a game's governance parameters.
|
|
100
|
+
/// @param gameId The ID of the game.
|
|
101
|
+
/// @param attestationStartTime The timestamp when attestation begins.
|
|
102
|
+
/// @param attestationGracePeriod The grace period duration in seconds.
|
|
103
|
+
function initializeGame(uint256 gameId, uint256 attestationStartTime, uint256 attestationGracePeriod) external;
|
|
104
|
+
|
|
105
|
+
/// @notice Submit a scorecard for attestation.
|
|
106
|
+
/// @param gameId The ID of the game.
|
|
107
|
+
/// @param tierWeights The tier cash out weights.
|
|
108
|
+
/// @return The scorecard ID.
|
|
109
|
+
function submitScorecardFor(
|
|
110
|
+
uint256 gameId,
|
|
111
|
+
DefifaTierCashOutWeight[] calldata tierWeights
|
|
112
|
+
)
|
|
113
|
+
external
|
|
114
|
+
returns (uint256);
|
|
115
|
+
|
|
116
|
+
/// @notice Attest to a submitted scorecard.
|
|
117
|
+
/// @param gameId The ID of the game.
|
|
118
|
+
/// @param scorecardId The ID of the scorecard to attest to.
|
|
119
|
+
/// @return weight The attestation weight applied.
|
|
120
|
+
function attestToScorecardFrom(uint256 gameId, uint256 scorecardId) external returns (uint256 weight);
|
|
121
|
+
|
|
122
|
+
/// @notice Ratify a scorecard that has reached quorum.
|
|
123
|
+
/// @param gameId The ID of the game.
|
|
124
|
+
/// @param tierWeights The tier cash out weights (must match the scorecard).
|
|
125
|
+
/// @return The scorecard ID that was ratified.
|
|
126
|
+
function ratifyScorecardFrom(
|
|
127
|
+
uint256 gameId,
|
|
128
|
+
DefifaTierCashOutWeight[] calldata tierWeights
|
|
129
|
+
)
|
|
130
|
+
external
|
|
131
|
+
returns (uint256);
|
|
132
|
+
}
|