@ballkidz/defifa 0.0.35 → 0.0.37

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 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.35",
3
+ "version": "0.0.37",
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.50",
34
+ "@bananapus/721-hook-v6": "^0.0.51",
35
35
  "@bananapus/address-registry-v6": "^0.0.25",
36
- "@bananapus/core-v6": "^0.0.53",
36
+ "@bananapus/core-v6": "^0.0.54",
37
37
  "@bananapus/permission-ids-v6": "^0.0.25",
38
38
  "@openzeppelin/contracts": "5.6.1",
39
39
  "@prb/math": "4.1.1",
@@ -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
- _directory: core.directory, _defifaToken: defifaToken, _baseProtocolToken: baseProtocolToken
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
- _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
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));
@@ -273,31 +273,32 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
273
273
  // -------------------------- constructor ---------------------------- //
274
274
  //*********************************************************************//
275
275
 
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.
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 _hookCodeOrigin,
285
- IJB721TokenUriResolver _tokenUriResolver,
286
- IDefifaGovernor _governor,
287
- IJBController _controller,
288
- IJBAddressRegistry _registry,
289
- uint256 _defifaProjectId,
290
- uint256 _baseProtocolProjectId,
291
- IJB721TiersHookStore _hookStore
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 = _hookCodeOrigin;
294
- TOKEN_URI_RESOLVER = _tokenUriResolver;
295
- GOVERNOR = _governor;
296
- CONTROLLER = _controller;
297
- REGISTRY = _registry;
298
- DEFIFA_PROJECT_ID = _defifaProjectId;
299
- BASE_PROTOCOL_PROJECT_ID = _baseProtocolProjectId;
300
- HOOK_STORE = _hookStore;
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
- encodedIPFSUri: defifaTier.encodedIPFSUri,
549
+ encodedIPFSUri: defifaTier.encodedIpfsUri,
549
550
  category: 0,
550
551
  discountPercent: 0,
551
552
  flags: JB721TierConfigFlags({
@@ -687,7 +688,6 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
687
688
  ownerMustSendPayouts: true,
688
689
  holdFees: false,
689
690
  scopeCashOutsToLocalBalances: true,
690
- pauseCrossProjectFeeFreeInflows: false,
691
691
  useDataHookForPay: true,
692
692
  useDataHookForCashOut: true,
693
693
  dataHook: metadata.dataHook,
@@ -851,7 +851,6 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
851
851
  ownerMustSendPayouts: false,
852
852
  holdFees: false,
853
853
  scopeCashOutsToLocalBalances: true,
854
- pauseCrossProjectFeeFreeInflows: false,
855
854
  useDataHookForPay: true,
856
855
  useDataHookForCashOut: true,
857
856
  dataHook: dataHook,
@@ -895,7 +894,6 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
895
894
  ownerMustSendPayouts: false,
896
895
  holdFees: false,
897
896
  scopeCashOutsToLocalBalances: true,
898
- pauseCrossProjectFeeFreeInflows: false,
899
897
  useDataHookForPay: true,
900
898
  useDataHookForCashOut: true,
901
899
  dataHook: dataHook,
@@ -956,7 +954,6 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
956
954
  ownerMustSendPayouts: true,
957
955
  holdFees: false,
958
956
  scopeCashOutsToLocalBalances: true,
959
- pauseCrossProjectFeeFreeInflows: false,
960
957
  useDataHookForPay: true,
961
958
  useDataHookForCashOut: true,
962
959
  dataHook: dataHook,
@@ -1012,7 +1009,6 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
1012
1009
  ownerMustSendPayouts: false,
1013
1010
  holdFees: false,
1014
1011
  scopeCashOutsToLocalBalances: true,
1015
- pauseCrossProjectFeeFreeInflows: false,
1016
1012
  useDataHookForPay: true,
1017
1013
  useDataHookForCashOut: true,
1018
1014
  dataHook: metadata.dataHook,
@@ -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;
@@ -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.
@@ -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 _directory,
435
- IERC20 _defifaToken,
436
- IERC20 _baseProtocolToken
434
+ IJBDirectory directory,
435
+ IERC20 defifaToken,
436
+ IERC20 baseProtocolToken
437
437
  )
438
- JB721Hook(_directory)
438
+ JB721Hook(directory)
439
439
  Ownable(msg.sender)
440
440
  {
441
- if (address(_defifaToken) == address(_baseProtocolToken)) {
441
+ if (address(defifaToken) == address(baseProtocolToken)) {
442
442
  revert DefifaHook_IdenticalTokens({
443
- defifaToken: address(_defifaToken), baseProtocolToken: address(_baseProtocolToken)
443
+ defifaToken: address(defifaToken), baseProtocolToken: address(baseProtocolToken)
444
444
  });
445
445
  }
446
446
 
447
447
  CODE_ORIGIN = address(this);
448
- DEFIFA_TOKEN = _defifaToken;
449
- BASE_PROTOCOL_TOKEN = _baseProtocolToken;
448
+ DEFIFA_TOKEN = defifaToken;
449
+ BASE_PROTOCOL_TOKEN = baseProtocolToken;
450
450
  }
451
451
 
452
452
  //*********************************************************************//
@@ -1135,7 +1135,7 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
1135
1135
  hookStore.tierOfTokenId({hook: address(this), tokenId: tokenId, includeResolvedUri: false});
1136
1136
 
1137
1137
  // Record the transfers and keep a reference to where the token is coming from.
1138
- from = super._update(to, tokenId, auth);
1138
+ from = super._update({to: to, tokenId: tokenId, auth: auth});
1139
1139
 
1140
1140
  // Transfers must not be paused (when not minting or burning).
1141
1141
  if (from != address(0)) {
@@ -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";
@@ -65,15 +65,14 @@ contract DefifaTokenUriResolver is IDefifaTokenUriResolver, IJB721TokenUriResolv
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 rarity text;
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
- // TODO: Somehow make the `IDefifaHook` have the `name` function.
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;
@@ -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 encodedIPFSUri The URI to use for each token within the tier.
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 encodedIPFSUri;
15
+ bytes32 encodedIpfsUri;
16
16
  bool shouldUseReservedTokenBeneficiaryAsDefault;
17
17
  }