@ballkidz/defifa 0.0.36 → 0.0.38
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/RISKS.md +8 -0
- package/package.json +4 -4
- package/script/Deploy.s.sol +9 -9
- package/src/DefifaDeployer.sol +25 -24
- package/src/DefifaGovernor.sol +2 -2
- package/src/DefifaHook.sol +37 -30
- package/src/DefifaTokenUriResolver.sol +8 -9
- package/src/structs/DefifaTierParams.sol +2 -2
package/RISKS.md
CHANGED
|
@@ -103,6 +103,14 @@ This means **a single bad split cannot block the others from being paid**, and t
|
|
|
103
103
|
|
|
104
104
|
`fulfilledCommitmentsOf[gameId]` always equals the requested commitment amount whenever any portion was attempted, regardless of whether every split succeeded. `currentGamePotOf(gameId, true)` adds this back to the remaining balance so the displayed pot represents the original pre-commitment value, but the actual on-terminal balance may be higher than the displayed pot when splits silently failed — by exactly the failed split amounts, which remain redeemable by game players. The bound therefore stays one-sided: real balance ≥ reported pot.
|
|
105
105
|
|
|
106
|
+
### 8.9 `DefifaProjectOwner.onERC721Received` accepts any project NFT and grants `SET_SPLIT_GROUPS` on its ID
|
|
107
|
+
|
|
108
|
+
`DefifaProjectOwner.onERC721Received` (line 53) checks `msg.sender == address(PROJECTS)` but explicitly discards the `from` argument (`from;` at line 63). It then calls `PERMISSIONS.setPermissionsFor` to grant `SET_SPLIT_GROUPS` to the deployer for the received `tokenId`, regardless of whether the transfer was a mint or a stray transfer of an existing project NFT.
|
|
109
|
+
|
|
110
|
+
Anyone holding any project NFT can `safeTransferFrom` it to this contract and trigger a real on-chain permission grant: the DEPLOYER address gains `SET_SPLIT_GROUPS` authority on whatever projectId was transferred. The grant is dormant in current code because `DefifaDeployer` only invokes `SET_SPLIT_GROUPS` on its own `DEFIFA_PROJECT_ID` lifecycle, but it is a real grant and would activate if any future deployer code path acts on caller-supplied project IDs. The same shape exists in `CTProjectOwner` (croptop) for the `ADJUST_721_TIERS` permission and was previously fixed for `JBOmnichainDeployer.onERC721Received` (finding 72).
|
|
111
|
+
|
|
112
|
+
Accepted because the grants are dormant against the current deployer surface and the receiving contract's project (the Defifa fee/governance project) is never the recipient of a hostile transfer in canonical flows. Anyone integrating `DefifaProjectOwner` into a deployer that operates on arbitrary projectIds must add the `require(from == address(0))` guard before relying on it.
|
|
113
|
+
|
|
106
114
|
### 8.8 Reserve minting is blocked during NO_CONTEST
|
|
107
115
|
|
|
108
116
|
`DefifaHook.mintReservesFor` reverts with `DefifaHook_ReservedTokenMintingBlockedInNoContest` once `currentGamePhaseOf` reports `NO_CONTEST`. Reserve mints inflate `totalMintCost` so reserved recipients can claim a proportional share of fee tokens. Without this block, a game that failed `minParticipation` could be revived back into a SCORING-eligible state via free notional face value (reserves bump `totalMintCost` over the threshold) before `triggerNoContestFor()` latches the failure into a refund ruleset. The block tightens the §8.4 trust assumption that pending reserves are folded into governance and fee accounting: that remains true while the game is live, but once a game has already failed participation the reserve channel is closed so the no-contest outcome is final.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ballkidz/defifa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.38",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "25.9.0"
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"remappings.txt"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
35
|
-
"@bananapus/address-registry-v6": "^0.0.
|
|
36
|
-
"@bananapus/core-v6": "^0.0.
|
|
34
|
+
"@bananapus/721-hook-v6": "^0.0.52",
|
|
35
|
+
"@bananapus/address-registry-v6": "^0.0.26",
|
|
36
|
+
"@bananapus/core-v6": "^0.0.55",
|
|
37
37
|
"@bananapus/permission-ids-v6": "^0.0.25",
|
|
38
38
|
"@openzeppelin/contracts": "5.6.1",
|
|
39
39
|
"@prb/math": "4.1.1",
|
package/script/Deploy.s.sol
CHANGED
|
@@ -106,20 +106,20 @@ contract DeployMainnet is Script, Sphinx {
|
|
|
106
106
|
|
|
107
107
|
function deploy() public sphinx {
|
|
108
108
|
DefifaHook hook = new DefifaHook{salt: _salt}({
|
|
109
|
-
|
|
109
|
+
directory: core.directory, defifaToken: defifaToken, baseProtocolToken: baseProtocolToken
|
|
110
110
|
});
|
|
111
111
|
DefifaTokenUriResolver tokenUriResolver = new DefifaTokenUriResolver{salt: _salt}(_typeface);
|
|
112
112
|
DefifaGovernor governor = new DefifaGovernor{salt: _salt}({controller: core.controller, owner: safeAddress()});
|
|
113
113
|
JB721TiersHookStore hookStore = new JB721TiersHookStore{salt: _salt}();
|
|
114
114
|
DefifaDeployer deployer = new DefifaDeployer{salt: _salt}({
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
115
|
+
hookCodeOrigin: address(hook),
|
|
116
|
+
tokenUriResolver: tokenUriResolver,
|
|
117
|
+
governor: governor,
|
|
118
|
+
controller: core.controller,
|
|
119
|
+
registry: registry.registry,
|
|
120
|
+
defifaProjectId: _defifaProjectId,
|
|
121
|
+
baseProtocolProjectId: _baseProtocolProjectId,
|
|
122
|
+
hookStore: hookStore
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
governor.transferOwnership(address(deployer));
|
package/src/DefifaDeployer.sol
CHANGED
|
@@ -273,31 +273,32 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
273
273
|
// -------------------------- constructor ---------------------------- //
|
|
274
274
|
//*********************************************************************//
|
|
275
275
|
|
|
276
|
-
/// @param
|
|
277
|
-
/// @param
|
|
278
|
-
/// @param
|
|
279
|
-
/// @param
|
|
280
|
-
/// @param
|
|
281
|
-
/// @param
|
|
282
|
-
/// @param
|
|
276
|
+
/// @param hookCodeOrigin The code of the Defifa hook.
|
|
277
|
+
/// @param tokenUriResolver The standard default token URI resolver.
|
|
278
|
+
/// @param governor The Defifa governor.
|
|
279
|
+
/// @param controller The controller to use to launch the game from.
|
|
280
|
+
/// @param registry The contract storing references to the deployer of each hook.
|
|
281
|
+
/// @param defifaProjectId The ID of the project that should take the fee from the games.
|
|
282
|
+
/// @param baseProtocolProjectId The ID of the protocol project that'll receive fees from fulfilling commitments.
|
|
283
|
+
/// @param hookStore The store used by Defifa hooks.
|
|
283
284
|
constructor(
|
|
284
|
-
address
|
|
285
|
-
IJB721TokenUriResolver
|
|
286
|
-
IDefifaGovernor
|
|
287
|
-
IJBController
|
|
288
|
-
IJBAddressRegistry
|
|
289
|
-
uint256
|
|
290
|
-
uint256
|
|
291
|
-
IJB721TiersHookStore
|
|
285
|
+
address hookCodeOrigin,
|
|
286
|
+
IJB721TokenUriResolver tokenUriResolver,
|
|
287
|
+
IDefifaGovernor governor,
|
|
288
|
+
IJBController controller,
|
|
289
|
+
IJBAddressRegistry registry,
|
|
290
|
+
uint256 defifaProjectId,
|
|
291
|
+
uint256 baseProtocolProjectId,
|
|
292
|
+
IJB721TiersHookStore hookStore
|
|
292
293
|
) {
|
|
293
|
-
HOOK_CODE_ORIGIN =
|
|
294
|
-
TOKEN_URI_RESOLVER =
|
|
295
|
-
GOVERNOR =
|
|
296
|
-
CONTROLLER =
|
|
297
|
-
REGISTRY =
|
|
298
|
-
DEFIFA_PROJECT_ID =
|
|
299
|
-
BASE_PROTOCOL_PROJECT_ID =
|
|
300
|
-
HOOK_STORE =
|
|
294
|
+
HOOK_CODE_ORIGIN = hookCodeOrigin;
|
|
295
|
+
TOKEN_URI_RESOLVER = tokenUriResolver;
|
|
296
|
+
GOVERNOR = governor;
|
|
297
|
+
CONTROLLER = controller;
|
|
298
|
+
REGISTRY = registry;
|
|
299
|
+
DEFIFA_PROJECT_ID = defifaProjectId;
|
|
300
|
+
BASE_PROTOCOL_PROJECT_ID = baseProtocolProjectId;
|
|
301
|
+
HOOK_STORE = hookStore;
|
|
301
302
|
/// @dev Uses the deployer address as group ID. Game scoring rulesets use uint160(token) as group ID.
|
|
302
303
|
SPLIT_GROUP = uint256(uint160(address(this)));
|
|
303
304
|
}
|
|
@@ -545,7 +546,7 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
545
546
|
votingUnits: 0,
|
|
546
547
|
reserveFrequency: defifaTier.reservedRate,
|
|
547
548
|
reserveBeneficiary: defifaTier.reservedTokenBeneficiary,
|
|
548
|
-
|
|
549
|
+
encodedIpfsUri: defifaTier.encodedIpfsUri,
|
|
549
550
|
category: 0,
|
|
550
551
|
discountPercent: 0,
|
|
551
552
|
flags: JB721TierConfigFlags({
|
package/src/DefifaGovernor.sol
CHANGED
|
@@ -407,10 +407,10 @@ contract DefifaGovernor is Ownable, IDefifaGovernor {
|
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
// maxShare² in totalCashOutWeight scale (nonlinear: gentle for moderate, steep for extreme).
|
|
410
|
-
uint256 maxShareSquared = mulDiv(maxWeight, maxWeight, totalCashOutWeight);
|
|
410
|
+
uint256 maxShareSquared = mulDiv({x: maxWeight, y: maxWeight, denominator: totalCashOutWeight});
|
|
411
411
|
|
|
412
412
|
// Penalty fills headroom proportional to concentration².
|
|
413
|
-
adjustedQuorum += mulDiv(headroom, maxShareSquared, totalCashOutWeight);
|
|
413
|
+
adjustedQuorum += mulDiv({x: headroom, y: maxShareSquared, denominator: totalCashOutWeight});
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
scorecard.quorumSnapshot = adjustedQuorum;
|
package/src/DefifaHook.sol
CHANGED
|
@@ -390,7 +390,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
390
390
|
/// @return The token URI corresponding with the tier or the tokenUriResolver URI.
|
|
391
391
|
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
|
|
392
392
|
// Use the resolver.
|
|
393
|
-
return store.tokenUriResolverOf(address(this)).tokenUriOf(address(this), tokenId);
|
|
393
|
+
return store.tokenUriResolverOf(address(this)).tokenUriOf({nft: address(this), tokenId: tokenId});
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
/// @notice The amount of $DEFIFA and $BASE_PROTOCOL tokens claimable for a set of token IDs.
|
|
@@ -403,7 +403,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
403
403
|
returns (uint256 defifaTokenAmount, uint256 baseProtocolTokenAmount)
|
|
404
404
|
{
|
|
405
405
|
// If the game isn't complete, we do not have any tokens to claim.
|
|
406
|
-
if (_currentGamePhaseOf(
|
|
406
|
+
if (_currentGamePhaseOf(projectId) != DefifaGamePhase.COMPLETE) return (0, 0);
|
|
407
407
|
|
|
408
408
|
// Include unminted reserves in the denominator. Once reserves are pending, their future recipients are
|
|
409
409
|
// entitled to fee-token claims as if the reserve NFTs had already been minted; otherwise paid holders could
|
|
@@ -431,22 +431,22 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
431
431
|
|
|
432
432
|
/// @dev The initial owner is msg.sender; ownership is transferred to the governor after initialization.
|
|
433
433
|
constructor(
|
|
434
|
-
IJBDirectory
|
|
435
|
-
IERC20
|
|
436
|
-
IERC20
|
|
434
|
+
IJBDirectory directory,
|
|
435
|
+
IERC20 defifaToken,
|
|
436
|
+
IERC20 baseProtocolToken
|
|
437
437
|
)
|
|
438
|
-
JB721Hook(
|
|
438
|
+
JB721Hook(directory)
|
|
439
439
|
Ownable(msg.sender)
|
|
440
440
|
{
|
|
441
|
-
if (address(
|
|
441
|
+
if (address(defifaToken) == address(baseProtocolToken)) {
|
|
442
442
|
revert DefifaHook_IdenticalTokens({
|
|
443
|
-
defifaToken: address(
|
|
443
|
+
defifaToken: address(defifaToken), baseProtocolToken: address(baseProtocolToken)
|
|
444
444
|
});
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
CODE_ORIGIN = address(this);
|
|
448
|
-
DEFIFA_TOKEN =
|
|
449
|
-
BASE_PROTOCOL_TOKEN =
|
|
448
|
+
DEFIFA_TOKEN = defifaToken;
|
|
449
|
+
BASE_PROTOCOL_TOKEN = baseProtocolToken;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
//*********************************************************************//
|
|
@@ -462,12 +462,14 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
462
462
|
virtual
|
|
463
463
|
override(IJBPayHook, JB721Hook)
|
|
464
464
|
{
|
|
465
|
-
uint256
|
|
465
|
+
uint256 localProjectId = projectId;
|
|
466
466
|
|
|
467
467
|
// Make sure the caller is a terminal of the project, and that the call is being made on behalf of an
|
|
468
468
|
// interaction with the correct project.
|
|
469
|
-
if (msg.value != 0 || !_isProjectTerminal(
|
|
470
|
-
revert JB721Hook_InvalidPay({
|
|
469
|
+
if (msg.value != 0 || !_isProjectTerminal(localProjectId) || context.projectId != localProjectId) {
|
|
470
|
+
revert JB721Hook_InvalidPay({
|
|
471
|
+
caller: msg.sender, contextProjectId: context.projectId, projectId: localProjectId
|
|
472
|
+
});
|
|
471
473
|
}
|
|
472
474
|
|
|
473
475
|
// Process the payment.
|
|
@@ -519,7 +521,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
519
521
|
if (address(store) != address(0)) revert();
|
|
520
522
|
|
|
521
523
|
// Initialize the superclass.
|
|
522
|
-
_initialize({
|
|
524
|
+
_initialize({initialProjectId: _gameId, name: _name, symbol: _symbol});
|
|
523
525
|
|
|
524
526
|
// Store stuff.
|
|
525
527
|
rulesets = _rulesets;
|
|
@@ -566,14 +568,14 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
566
568
|
function mintReservesFor(uint256 tierId, uint256 count) public override {
|
|
567
569
|
// Minting reserves must not be paused.
|
|
568
570
|
if (JB721TiersRulesetMetadataResolver.mintPendingReservesPaused(_rulesetMetadata())) {
|
|
569
|
-
revert DefifaHook_ReservedTokenMintingPaused({projectId:
|
|
571
|
+
revert DefifaHook_ReservedTokenMintingPaused({projectId: projectId, tierId: tierId});
|
|
570
572
|
}
|
|
571
573
|
|
|
572
574
|
// Block reserve minting while the game is in NO_CONTEST. Reserve mints inflate `totalMintCost` (so reserved
|
|
573
575
|
// recipients can claim fee tokens), which would otherwise let a game that failed `minParticipation` revive
|
|
574
576
|
// back to SCORING via free notional face value before `triggerNoContestFor` latches the failure.
|
|
575
|
-
if (_currentGamePhaseOf(
|
|
576
|
-
revert DefifaHook_ReservedTokenMintingBlockedInNoContest({projectId:
|
|
577
|
+
if (_currentGamePhaseOf(projectId) == DefifaGamePhase.NO_CONTEST) {
|
|
578
|
+
revert DefifaHook_ReservedTokenMintingBlockedInNoContest({projectId: projectId, tierId: tierId});
|
|
577
579
|
}
|
|
578
580
|
|
|
579
581
|
// Cache the store reference in a local variable to avoid repeated SLOAD.
|
|
@@ -657,9 +659,11 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
657
659
|
{
|
|
658
660
|
// Make sure the caller is a terminal of the project, and that the call is being made on behalf of an
|
|
659
661
|
// interaction with the correct project.
|
|
660
|
-
|
|
662
|
+
uint256 localProjectId = projectId;
|
|
663
|
+
|
|
664
|
+
if (msg.value != 0 || !_isProjectTerminal(localProjectId) || context.projectId != localProjectId) {
|
|
661
665
|
revert JB721Hook_InvalidCashOut({
|
|
662
|
-
caller: msg.sender, contextProjectId: context.projectId, projectId:
|
|
666
|
+
caller: msg.sender, contextProjectId: context.projectId, projectId: localProjectId, msgValue: msg.value
|
|
663
667
|
});
|
|
664
668
|
}
|
|
665
669
|
|
|
@@ -683,7 +687,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
683
687
|
uint256 tokenId;
|
|
684
688
|
|
|
685
689
|
// Keep track of whether the cashOut is happening during the complete phase.
|
|
686
|
-
bool isComplete = _currentGamePhaseOf(
|
|
690
|
+
bool isComplete = _currentGamePhaseOf(localProjectId) == DefifaGamePhase.COMPLETE;
|
|
687
691
|
|
|
688
692
|
// Cache the store reference in a local variable to avoid repeated SLOAD in the loop.
|
|
689
693
|
IJB721TiersHookStore hookStore = store;
|
|
@@ -779,15 +783,16 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
779
783
|
/// @param tierWeights The tier weights to set.
|
|
780
784
|
function setTierCashOutWeightsTo(DefifaTierCashOutWeight[] memory tierWeights) external override onlyOwner {
|
|
781
785
|
// Get a reference to the game phase.
|
|
782
|
-
|
|
786
|
+
uint256 localProjectId = projectId;
|
|
787
|
+
DefifaGamePhase gamePhase = _currentGamePhaseOf(localProjectId);
|
|
783
788
|
|
|
784
789
|
// Make sure the game has ended.
|
|
785
790
|
if (gamePhase != DefifaGamePhase.SCORING) {
|
|
786
|
-
revert DefifaHook_GameIsntScoringYet({projectId:
|
|
791
|
+
revert DefifaHook_GameIsntScoringYet({projectId: localProjectId, phase: gamePhase});
|
|
787
792
|
}
|
|
788
793
|
|
|
789
794
|
// Make sure the cashOut weights haven't already been set.
|
|
790
|
-
if (cashOutWeightIsSet) revert DefifaHook_CashoutWeightsAlreadySet({projectId:
|
|
795
|
+
if (cashOutWeightIsSet) revert DefifaHook_CashoutWeightsAlreadySet({projectId: localProjectId});
|
|
791
796
|
|
|
792
797
|
// Validate weights and build the array. Reverts on invalid input.
|
|
793
798
|
_tierCashOutWeights =
|
|
@@ -807,9 +812,10 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
807
812
|
if (delegatee == address(0)) revert DefifaHook_DelegateAddressZero({tierId: tierId});
|
|
808
813
|
|
|
809
814
|
// Make sure the current game phase is the minting phase.
|
|
810
|
-
|
|
815
|
+
uint256 localProjectId = projectId;
|
|
816
|
+
DefifaGamePhase gamePhase = _currentGamePhaseOf(localProjectId);
|
|
811
817
|
if (gamePhase != DefifaGamePhase.MINT) {
|
|
812
|
-
revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId:
|
|
818
|
+
revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId: localProjectId, phase: gamePhase});
|
|
813
819
|
}
|
|
814
820
|
|
|
815
821
|
_delegateTier({account: msg.sender, delegatee: delegatee, tierId: tierId});
|
|
@@ -819,9 +825,10 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
819
825
|
/// @param delegations An array of tiers to set delegates for.
|
|
820
826
|
function setTierDelegatesTo(DefifaDelegation[] memory delegations) external virtual override {
|
|
821
827
|
// Make sure the current game phase is the minting phase.
|
|
822
|
-
|
|
828
|
+
uint256 localProjectId = projectId;
|
|
829
|
+
DefifaGamePhase gamePhase = _currentGamePhaseOf(localProjectId);
|
|
823
830
|
if (gamePhase != DefifaGamePhase.MINT) {
|
|
824
|
-
revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId:
|
|
831
|
+
revert DefifaHook_DelegateChangesUnavailableInThisPhase({projectId: localProjectId, phase: gamePhase});
|
|
825
832
|
}
|
|
826
833
|
|
|
827
834
|
// Keep a reference to the number of tier delegates.
|
|
@@ -1135,7 +1142,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1135
1142
|
hookStore.tierOfTokenId({hook: address(this), tokenId: tokenId, includeResolvedUri: false});
|
|
1136
1143
|
|
|
1137
1144
|
// Record the transfers and keep a reference to where the token is coming from.
|
|
1138
|
-
from = super._update(to, tokenId, auth);
|
|
1145
|
+
from = super._update({to: to, tokenId: tokenId, auth: auth});
|
|
1139
1146
|
|
|
1140
1147
|
// Transfers must not be paused (when not minting or burning).
|
|
1141
1148
|
if (from != address(0)) {
|
|
@@ -1143,7 +1150,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1143
1150
|
if (tier.flags.transfersPausable) {
|
|
1144
1151
|
// If transfers are paused and the NFT isn't being transferred to the zero address, revert.
|
|
1145
1152
|
if (to != address(0) && JB721TiersRulesetMetadataResolver.transfersPaused(_rulesetMetadata())) {
|
|
1146
|
-
revert DefifaHook_TransfersPaused({projectId:
|
|
1153
|
+
revert DefifaHook_TransfersPaused({projectId: projectId, tokenId: tokenId, from: from, to: to});
|
|
1147
1154
|
}
|
|
1148
1155
|
}
|
|
1149
1156
|
|
|
@@ -1199,6 +1206,6 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
1199
1206
|
/// @notice Returns the current ruleset metadata for this project.
|
|
1200
1207
|
/// @return The packed ruleset metadata.
|
|
1201
1208
|
function _rulesetMetadata() internal view returns (uint256) {
|
|
1202
|
-
return JBRulesetMetadataResolver.metadata(rulesets.currentOf(
|
|
1209
|
+
return JBRulesetMetadataResolver.metadata(rulesets.currentOf(projectId));
|
|
1203
1210
|
}
|
|
1204
1211
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
5
|
+
import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
|
|
5
6
|
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
|
|
6
7
|
import {mulDiv} from "@prb/math/src/Common.sol";
|
|
7
8
|
|
|
8
|
-
import {ERC721} from "@bananapus/721-hook-v6/src/abstract/ERC721.sol";
|
|
9
9
|
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
10
10
|
import {JB721Tier} from "@bananapus/721-hook-v6/src/structs/JB721Tier.sol";
|
|
11
11
|
import {JBIpfsDecoder} from "@bananapus/721-hook-v6/src/libraries/JBIpfsDecoder.sol";
|
|
@@ -60,20 +60,19 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
|
|
|
60
60
|
IDefifaHook hook = IDefifaHook(nft);
|
|
61
61
|
|
|
62
62
|
// Get the game ID.
|
|
63
|
-
uint256 gameId = hook.
|
|
63
|
+
uint256 gameId = hook.projectId();
|
|
64
64
|
|
|
65
65
|
// Keep a reference to the game phase text.
|
|
66
66
|
string memory gamePhaseText;
|
|
67
67
|
|
|
68
|
-
// Keep a reference to the rarity text
|
|
68
|
+
// Keep a reference to the rarity text.
|
|
69
69
|
string memory rarityText;
|
|
70
70
|
|
|
71
|
-
// Keep a reference to the
|
|
71
|
+
// Keep a reference to the value text.
|
|
72
72
|
string memory valueText;
|
|
73
73
|
|
|
74
74
|
// Keep a reference to the game's name (escaped for JSON/SVG safety).
|
|
75
|
-
|
|
76
|
-
string memory titleSvg = _escapeSvg(ERC721(address(hook)).name());
|
|
75
|
+
string memory titleSvg = _escapeSvg(IERC721Metadata(address(hook)).name());
|
|
77
76
|
|
|
78
77
|
// Keep a reference to the tier's name.
|
|
79
78
|
string memory teamJson;
|
|
@@ -96,8 +95,8 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
|
|
|
96
95
|
teamSvg = _escapeSvg(rawTeam);
|
|
97
96
|
|
|
98
97
|
// Check to see if the tier has a URI. Return it if it does.
|
|
99
|
-
if (tier.
|
|
100
|
-
return JBIpfsDecoder.decode({baseUri: hook.baseURI(), hexString: tier.
|
|
98
|
+
if (tier.encodedIpfsUri != bytes32(0)) {
|
|
99
|
+
return JBIpfsDecoder.decode({baseUri: hook.baseURI(), hexString: tier.encodedIpfsUri});
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
parts[0] = string("data:application/json;base64,");
|
|
@@ -293,7 +292,7 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
|
|
|
293
292
|
try IERC20Metadata(token).symbol() returns (string memory s) {
|
|
294
293
|
tokenSymbol = _escapeSvg(s);
|
|
295
294
|
} catch {
|
|
296
|
-
tokenSymbol = Strings.toHexString(uint160(token), 20);
|
|
295
|
+
tokenSymbol = Strings.toHexString({value: uint160(token), length: 20});
|
|
297
296
|
}
|
|
298
297
|
|
|
299
298
|
return string(abi.encodePacked(integerPart, ".", decimalPartStr, " ", tokenSymbol));
|
|
@@ -5,13 +5,13 @@ pragma solidity ^0.8.0;
|
|
|
5
5
|
/// @custom:member name The name of the tier.
|
|
6
6
|
/// @custom:member reservedRate The number of minted tokens needed in the tier to allow for minting another reserved
|
|
7
7
|
/// token. @custom:member reservedRateBeneficiary The beneficiary of the reserved tokens for this tier.
|
|
8
|
-
/// @custom:member
|
|
8
|
+
/// @custom:member encodedIpfsUri The URI to use for each token within the tier.
|
|
9
9
|
/// @custom:member shouldUseReservedRateBeneficiaryAsDefault A flag indicating if the `reservedTokenBeneficiary` should
|
|
10
10
|
/// be stored as the default beneficiary for all tiers, saving storage.
|
|
11
11
|
struct DefifaTierParams {
|
|
12
12
|
string name;
|
|
13
13
|
uint16 reservedRate;
|
|
14
14
|
address reservedTokenBeneficiary;
|
|
15
|
-
bytes32
|
|
15
|
+
bytes32 encodedIpfsUri;
|
|
16
16
|
bool shouldUseReservedTokenBeneficiaryAsDefault;
|
|
17
17
|
}
|