@ballkidz/defifa 0.0.40 → 0.0.41

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 CHANGED
@@ -30,6 +30,7 @@
30
30
  | Ratification path caller | Any caller who meets the documented conditions | Per game | Finalizes a valid scorecard |
31
31
  | Fulfillment path caller | Any valid caller once ratified | Per game | Must run the completion commitment path |
32
32
  | `DefifaProjectOwner` holder | Project NFT sent into sink | Per fee project | Irreversible ownership lock |
33
+ | `DefifaDeployer` owner | Constructor `initialOwner` (typically the Defifa multisig) | Global referral-attribution surface | Single privilege: call `setReferralProjectId` to repoint the fee-volume credit |
33
34
 
34
35
  ## Privileged Surfaces
35
36
 
@@ -37,6 +38,7 @@
37
38
  - `submitScorecardFor(...)`, `attestToScorecardFrom(...)`, `revokeAttestationFrom(...)`, and `ratifyScorecardFrom(...)` govern outcome selection
38
39
  - `fulfillCommitmentsOf(...)` turns a ratified scorecard into real settlement
39
40
  - `triggerNoContestFor(...)` moves failed games into the documented recovery path
41
+ - `DefifaDeployer.setReferralProjectId(projectId, chainId)` — `onlyOwner`; retargets the fee-volume referrer credited on every `sendPayoutsOf` call this deployer makes from `fulfillCommitmentsOf`. Stores the packed `(chainId << 48) | projectId` value `JBMultiTerminal` expects on its `referralProjectId` argument. Defaults to `(1, DEFIFA_PROJECT_ID)` at construction so credit lands on the Defifa project on Ethereum mainnet regardless of which chain a game runs on. Passing `(0, 0)` disables the credit entirely. Bounded so the pack is lossless: `projectId <= type(uint48).max`, `chainId <= type(uint208).max`. Does not affect game economics — only where fee-volume attribution accrues.
40
42
 
41
43
  ## Immutable And One-Way
42
44
 
@@ -77,3 +79,4 @@
77
79
  - No one can set cash-out weights twice.
78
80
  - No one can fulfill commitments twice.
79
81
  - No one can recover the project NFT from `DefifaProjectOwner`.
82
+ - The `DefifaDeployer` owner can repoint `referralProjectId` but cannot change anything else about an in-flight or past game — game economics, fees, and settlement remain bounded by the immutable launch configuration.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.41 — Owner-settable referral target on `DefifaDeployer`
4
+
5
+ - `DefifaDeployer` now inherits OpenZeppelin `Ownable`. New constructor arg `address initialOwner` is passed straight to `Ownable(initialOwner)`. **This is a breaking constructor signature change** — `script/Deploy.s.sol` and every test that instantiates `DefifaDeployer` directly was updated to pass `initialOwner` (typically `safeAddress()` in the deploy script; `address(this)` in tests).
6
+ - New `referralProjectId()` view returning the packed `(chainId << 48) | projectId` reference credited as the referrer on every fee-payout `sendPayoutsOf` call from `fulfillCommitmentsOf`.
7
+ - New `setReferralProjectId(uint256 projectId, uint256 chainId)` (`onlyOwner`): takes the two fields unpacked, packs and stores them. Bounded so the pack is lossless — `projectId <= type(uint48).max`, `chainId <= type(uint208).max`. Reverts with `DefifaDeployer_ReferralProjectIdTooLarge` / `DefifaDeployer_ReferralChainIdTooLarge` otherwise. Emits `SetReferralProjectId(referralChainId, referralProjectId, caller)`.
8
+ - Default at construction: `(chainId = 1, projectId = DEFIFA_PROJECT_ID)` — fee-volume credit still lands on the Defifa project on Ethereum mainnet regardless of which chain a game runs on. Owner can repoint this or pass `(0, 0)` to disable referral credit entirely.
9
+ - The inline `(uint256(1) << 48) | DEFIFA_PROJECT_ID` pack inside `fulfillCommitmentsOf` is replaced by a read of the new storage slot. No external behavior change for default deployments.
10
+
3
11
  ## 0.0.35 — Bump v6 deps to nana-core-v6 0.0.53 cohort
4
12
 
5
13
  - `@bananapus/core-v6`: `^0.0.48 → ^0.0.53` ([PR #145](https://github.com/Bananapus/nana-core-v6/pull/145)).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ballkidz/defifa",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "license": "MIT",
5
5
  "engines": {
6
6
  "node": "25.9.0"
@@ -119,7 +119,8 @@ contract DeployMainnet is Script, Sphinx {
119
119
  registry: registry.registry,
120
120
  defifaProjectId: _defifaProjectId,
121
121
  baseProtocolProjectId: _baseProtocolProjectId,
122
- hookStore: hookStore
122
+ hookStore: hookStore,
123
+ initialOwner: safeAddress()
123
124
  });
124
125
 
125
126
  governor.transferOwnership(address(deployer));
@@ -21,6 +21,7 @@ import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAcces
21
21
  import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
22
22
  import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
23
23
  import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
24
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
24
25
  import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
25
26
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
26
27
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
@@ -46,7 +47,7 @@ import {DefifaTierParams} from "./structs/DefifaTierParams.sol";
46
47
  /// the event concludes, a scorecard assigns cash-out weights to each tier. The treasury is distributed proportionally
47
48
  /// to winning NFT holders. Games progress through phases: COUNTDOWN → MINT → REFUND → SCORING → COMPLETE (or
48
49
  /// NO_CONTEST if minimum participation isn't met or scorecard ratification times out).
49
- contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGamePotReporter, IERC721Receiver {
50
+ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGamePotReporter, IERC721Receiver, Ownable {
50
51
  using Strings for uint256;
51
52
  using SafeERC20 for IERC20;
52
53
 
@@ -61,6 +62,8 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
61
62
  error DefifaDeployer_InvalidCurrency(address token, uint256 currency);
62
63
  error DefifaDeployer_NotNoContest(uint256 gameId, DefifaGamePhase phase);
63
64
  error DefifaDeployer_NoContestAlreadyTriggered(uint256 gameId);
65
+ error DefifaDeployer_ReferralChainIdTooLarge(uint256 referralChainId);
66
+ error DefifaDeployer_ReferralProjectIdTooLarge(uint256 referralProjectId);
64
67
  error DefifaDeployer_SplitsDontAddUp(uint256 totalPercent, uint256 maxPercent);
65
68
 
66
69
  //*********************************************************************//
@@ -133,6 +136,12 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
133
136
  /// @dev Once triggered, the game stays in NO_CONTEST and refunds are enabled.
134
137
  mapping(uint256 => bool) public noContestTriggeredFor;
135
138
 
139
+ /// @notice The packed `(referralChainId << 48) | referralProjectId` reference credited as the referrer on
140
+ /// every fee-payout `sendPayoutsOf` call this deployer makes during `fulfillCommitmentsOf`. Defaults to
141
+ /// `(1, DEFIFA_PROJECT_ID)` so fee-volume credit accrues to the Defifa project on Ethereum mainnet
142
+ /// regardless of which chain the game runs on. Settable by the owner via `setReferralProjectId`.
143
+ uint256 public override referralProjectId;
144
+
136
145
  //*********************************************************************//
137
146
  // ------------------------- external views -------------------------- //
138
147
  //*********************************************************************//
@@ -281,6 +290,9 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
281
290
  /// @param defifaProjectId The ID of the project that should take the fee from the games.
282
291
  /// @param baseProtocolProjectId The ID of the protocol project that'll receive fees from fulfilling commitments.
283
292
  /// @param hookStore The store used by Defifa hooks.
293
+ /// @param initialOwner The address granted authority to call `setReferralProjectId`. The contract is otherwise
294
+ /// stateless from an admin perspective — the owner only controls the referrer reference used when crediting
295
+ /// fee-volume on `fulfillCommitmentsOf` payouts.
284
296
  constructor(
285
297
  address hookCodeOrigin,
286
298
  IJB721TokenUriResolver tokenUriResolver,
@@ -289,8 +301,11 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
289
301
  IJBAddressRegistry registry,
290
302
  uint256 defifaProjectId,
291
303
  uint256 baseProtocolProjectId,
292
- IJB721TiersHookStore hookStore
293
- ) {
304
+ IJB721TiersHookStore hookStore,
305
+ address initialOwner
306
+ )
307
+ Ownable(initialOwner)
308
+ {
294
309
  HOOK_CODE_ORIGIN = hookCodeOrigin;
295
310
  TOKEN_URI_RESOLVER = tokenUriResolver;
296
311
  GOVERNOR = governor;
@@ -301,6 +316,10 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
301
316
  HOOK_STORE = hookStore;
302
317
  /// @dev Uses the deployer address as group ID. Game scoring rulesets use uint160(token) as group ID.
303
318
  SPLIT_GROUP = uint256(uint160(address(this)));
319
+
320
+ // Default referrer reference: Defifa project on Ethereum mainnet. Encoded `(chainId << 48) | projectId`
321
+ // per `JBMultiTerminal.currentReferralProjectId`. Owner can retarget via `setReferralProjectId`.
322
+ referralProjectId = (uint256(1) << 48) | defifaProjectId;
304
323
  }
305
324
 
306
325
  //*********************************************************************//
@@ -346,24 +365,18 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
346
365
 
347
366
  // Send only the fee portion as payouts. The remaining balance stays as surplus for cash-outs.
348
367
  // Use the ruleset's baseCurrency — this matches the currency under which payout limits were stored
349
- // at launch time, regardless of whether the token is native ETH or an ERC-20.
350
- // Wrapped in try-catch so the final ruleset is always queued even if payout fails.
351
- // Credit `DEFIFA_PROJECT_ID` as the referrer so the protocol fee volume from every game's commitment
352
- // payout attributes back to the Defifa project itself Defifa is the project that facilitated the
353
- // fee-paying activity, regardless of which `gameId` triggered it.
354
- //
355
- // The referrer reference is encoded as `(referralChainId << 48) | referralProjectId` per `JBMultiTerminal`'s
356
- // `currentReferralProjectId` packing. Defifa lives on Ethereum mainnet, so we hard-code `referralChainId = 1`
357
- // here: this ensures the protocol fee volume credit accrues to Defifa on mainnet regardless of which chain
358
- // the game runs on. (Auto-resolving to `block.chainid` would scatter credit across L2s where Defifa has no
359
- // canonical project ID, so we pin mainnet explicitly.)
368
+ // at launch time, regardless of whether the token is native ETH or an ERC-20. Wrapped in try-catch so
369
+ // the final ruleset is always queued even if payout fails. The configured `referralProjectId` (default
370
+ // `(1, DEFIFA_PROJECT_ID)`, owner-settable via `setReferralProjectId`) credits Defifa with the protocol
371
+ // fee volume from every game's commitment payout. Pinning mainnet by default avoids scattering credit
372
+ // across L2s where Defifa has no canonical project ID.
360
373
  try terminal.sendPayoutsOf({
361
374
  projectId: gameId,
362
375
  token: token,
363
376
  amount: feeAmount,
364
377
  currency: metadata.baseCurrency,
365
378
  minTokensPaidOut: 0,
366
- referralProjectId: (uint256(1) << 48) | DEFIFA_PROJECT_ID
379
+ referralProjectId: referralProjectId
367
380
  }) {}
368
381
  catch (bytes memory reason) {
369
382
  // Payout failed — fee stays in pot. Reset to 0 so currentGamePotOf
@@ -653,6 +666,36 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
653
666
  return IERC721Receiver.onERC721Received.selector;
654
667
  }
655
668
 
669
+ /// @notice Update the referrer reference credited on every fee-payout `sendPayoutsOf` call this deployer
670
+ /// makes during `fulfillCommitmentsOf`.
671
+ /// @dev Stores the packed `(newReferralChainId << 48) | newReferralProjectId` value used by
672
+ /// `JBMultiTerminal.currentReferralProjectId`. Either field may be zero — passing `(0, 0)` disables the
673
+ /// referral credit entirely. Bounded so the pack is lossless: `newReferralProjectId` must fit in `uint48`,
674
+ /// `newReferralChainId` must fit in `uint208` (so the left-shift by 48 doesn't drop high bits).
675
+ /// @param newReferralProjectId The referring project's bare ID on `newReferralChainId`.
676
+ /// @param newReferralChainId The EIP-155 chain ID of the referrer's home chain.
677
+ function setReferralProjectId(
678
+ uint256 newReferralProjectId,
679
+ uint256 newReferralChainId
680
+ )
681
+ external
682
+ override
683
+ onlyOwner
684
+ {
685
+ // Bound the inputs to the on-chain encoding so the pack is lossless. The same shape JBMultiTerminal uses:
686
+ // projectId in bits [47:0], chainId in bits [255:48].
687
+ if (newReferralProjectId > type(uint48).max) {
688
+ revert DefifaDeployer_ReferralProjectIdTooLarge(newReferralProjectId);
689
+ }
690
+ if (newReferralChainId >> 208 != 0) revert DefifaDeployer_ReferralChainIdTooLarge(newReferralChainId);
691
+
692
+ referralProjectId = (newReferralChainId << 48) | newReferralProjectId;
693
+
694
+ emit SetReferralProjectId({
695
+ referralChainId: newReferralChainId, referralProjectId: newReferralProjectId, caller: msg.sender
696
+ });
697
+ }
698
+
656
699
  /// @notice Triggers the no-contest refund mechanism for a game.
657
700
  /// @dev Anyone can call this once the game is in the NO_CONTEST phase. This queues a new ruleset without
658
701
  /// payout limits, making the surplus equal to the balance so users can cash out at their mint price.
@@ -45,6 +45,12 @@ interface IDefifaDeployer {
45
45
  /// @param caller The address that queued the phase transition.
46
46
  event QueuedNoContest(uint256 indexed gameId, address caller);
47
47
 
48
+ /// @notice Emitted when the referrer reference for fee-volume credit is updated.
49
+ /// @param referralChainId The EIP-155 chain ID of the new referrer's home chain.
50
+ /// @param referralProjectId The new referring project's bare project ID on `referralChainId`.
51
+ /// @param caller The address that set the new referrer.
52
+ event SetReferralProjectId(uint256 indexed referralChainId, uint256 indexed referralProjectId, address caller);
53
+
48
54
  /// @notice The fee divisor for base protocol fees (100 / fee percent).
49
55
  /// @return The fee divisor.
50
56
  function BASE_PROTOCOL_FEE_DIVISOR() external view returns (uint256);
@@ -81,6 +87,12 @@ interface IDefifaDeployer {
81
87
  /// @return The address registry contract.
82
88
  function REGISTRY() external view returns (IJBAddressRegistry);
83
89
 
90
+ /// @notice The packed `(referralChainId << 48) | referralProjectId` reference credited as the referrer on
91
+ /// every fee-payout `sendPayoutsOf` call this deployer makes. Defaults to `(1, DEFIFA_PROJECT_ID)` so
92
+ /// credit accrues to Defifa on Ethereum mainnet; owner-settable via `setReferralProjectId`.
93
+ /// @return The packed referrer reference.
94
+ function referralProjectId() external view returns (uint256);
95
+
84
96
  /// @notice The split group ID used for distributing game pot funds.
85
97
  /// @return The split group.
86
98
  function SPLIT_GROUP() external view returns (uint256);
@@ -119,6 +131,16 @@ interface IDefifaDeployer {
119
131
  /// @return gameId The ID of the newly launched game.
120
132
  function launchGameWith(DefifaLaunchProjectData calldata launchProjectData) external returns (uint256 gameId);
121
133
 
134
+ /// @notice Update the referrer reference credited on every fee-payout `sendPayoutsOf` call this deployer
135
+ /// makes during `fulfillCommitmentsOf`.
136
+ /// @dev Stores the packed `(newReferralChainId << 48) | newReferralProjectId` value used by
137
+ /// `JBMultiTerminal.currentReferralProjectId`. Either field may be zero (passing `(0, 0)` disables the
138
+ /// referral credit). Bounded so the pack is lossless: `newReferralProjectId <= type(uint48).max`,
139
+ /// `newReferralChainId <= type(uint208).max`.
140
+ /// @param newReferralProjectId The referring project's bare ID on `newReferralChainId`.
141
+ /// @param newReferralChainId The EIP-155 chain ID of the referrer's home chain.
142
+ function setReferralProjectId(uint256 newReferralProjectId, uint256 newReferralChainId) external;
143
+
122
144
  /// @notice Trigger a no-contest outcome for a game.
123
145
  /// @param gameId The ID of the game.
124
146
  function triggerNoContestFor(uint256 gameId) external;