@ballkidz/defifa 0.0.10 → 0.0.12
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 +26 -15
- package/ARCHITECTURE.md +35 -3
- package/AUDIT_INSTRUCTIONS.md +127 -45
- package/CHANGE_LOG.md +107 -0
- package/CRYPTO_ECON.md +2 -2
- package/README.md +120 -2
- package/RISKS.md +21 -4
- package/SKILLS.md +174 -59
- package/STYLE_GUIDE.md +2 -2
- package/USER_JOURNEYS.md +482 -139
- package/foundry.toml +1 -1
- package/package.json +7 -7
- package/script/Deploy.s.sol +1 -1
- package/script/helpers/DefifaDeploymentLib.sol +2 -2
- package/src/DefifaDeployer.sol +2 -2
- package/src/DefifaGovernor.sol +1 -1
- package/src/DefifaHook.sol +7 -6
- package/src/DefifaProjectOwner.sol +1 -1
- package/src/DefifaTokenUriResolver.sol +1 -1
- package/src/libraries/DefifaFontImporter.sol +1 -1
- package/src/libraries/DefifaHookLib.sol +1 -1
- package/test/DefifaAdversarialQuorum.t.sol +1 -1
- package/test/DefifaAuditLowGuards.t.sol +1 -1
- package/test/DefifaFeeAccounting.t.sol +1 -1
- package/test/DefifaGovernor.t.sol +1 -1
- package/test/DefifaHookRegressions.t.sol +39 -1
- package/test/DefifaMintCostInvariant.t.sol +1 -1
- package/test/DefifaNoContest.t.sol +1 -1
- package/test/DefifaSecurity.t.sol +1 -1
- package/test/DefifaUSDC.t.sol +1 -1
- package/test/Fork.t.sol +1 -1
- package/test/SVG.t.sol +1 -1
- package/test/TestAuditGaps.sol +1 -1
- package/test/TestQALastMile.t.sol +1 -1
- package/test/audit/CodexAttestationDoubleCount.t.sol +217 -0
- package/test/deployScript.t.sol +1 -1
- package/test/regression/AttestationDelegateBeneficiary.t.sol +272 -0
- package/test/regression/FulfillmentBlocksRatification.t.sol +1 -1
- package/test/regression/GracePeriodBypass.t.sol +1 -1
package/foundry.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ballkidz/defifa",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0"
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
"url": "https://github.com/BallKidz/defifa-collection-deployer"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
17
|
-
"@bananapus/address-registry-v6": "^0.0.
|
|
18
|
-
"@bananapus/core-v6": "^0.0.
|
|
19
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
20
|
-
"@croptop/core-v6": "^0.0.
|
|
16
|
+
"@bananapus/721-hook-v6": "^0.0.21",
|
|
17
|
+
"@bananapus/address-registry-v6": "^0.0.15",
|
|
18
|
+
"@bananapus/core-v6": "^0.0.27",
|
|
19
|
+
"@bananapus/permission-ids-v6": "^0.0.14",
|
|
20
|
+
"@croptop/core-v6": "^0.0.22",
|
|
21
21
|
"@openzeppelin/contracts": "^5.6.1",
|
|
22
22
|
"@prb/math": "^4.1.1",
|
|
23
|
-
"@rev-net/core-v6": "^0.0.
|
|
23
|
+
"@rev-net/core-v6": "^0.0.17",
|
|
24
24
|
"scripty.sol": "^2.1.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
package/script/Deploy.s.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {stdJson} from "forge-std/Script.sol";
|
|
5
5
|
import {Vm} from "forge-std/Vm.sol";
|
|
@@ -22,7 +22,7 @@ library DefifaDeploymentLib {
|
|
|
22
22
|
// Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
|
|
23
23
|
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
|
|
24
24
|
Vm internal constant VM = Vm(VM_ADDRESS);
|
|
25
|
-
string constant PROJECT_NAME = "defifa-
|
|
25
|
+
string constant PROJECT_NAME = "defifa-v6";
|
|
26
26
|
|
|
27
27
|
function getDeployment(string memory path) internal returns (DefifaDeployment memory deployment) {
|
|
28
28
|
// Get chainId for which we need to get the deployment.
|
package/src/DefifaDeployer.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
5
5
|
import {
|
|
@@ -331,7 +331,7 @@ contract DefifaDeployer is IDefifaDeployer, IDefifaGamePhaseReporter, IDefifaGam
|
|
|
331
331
|
|
|
332
332
|
// Send only the fee portion as payouts. The remaining balance stays as surplus for cash-outs.
|
|
333
333
|
// Wrapped in try-catch so the final ruleset is always queued even if payout fails.
|
|
334
|
-
// slither-disable-next-line unused-return
|
|
334
|
+
// slither-disable-next-line unused-return,reentrancy-no-eth
|
|
335
335
|
try _terminal.sendPayoutsOf({
|
|
336
336
|
projectId: gameId,
|
|
337
337
|
token: _token,
|
package/src/DefifaGovernor.sol
CHANGED
package/src/DefifaHook.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol";
|
|
5
5
|
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
@@ -947,9 +947,10 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
947
947
|
// Decode the metadata.
|
|
948
948
|
(address _attestationDelegate, uint16[] memory _tierIdsToMint) = abi.decode(metadata, (address, uint16[]));
|
|
949
949
|
|
|
950
|
-
// Set the
|
|
950
|
+
// Set the beneficiary as the attestation delegate by default.
|
|
951
951
|
if (_attestationDelegate == address(0)) {
|
|
952
|
-
_attestationDelegate =
|
|
952
|
+
_attestationDelegate =
|
|
953
|
+
defaultAttestationDelegate != address(0) ? defaultAttestationDelegate : context.beneficiary;
|
|
953
954
|
}
|
|
954
955
|
|
|
955
956
|
// Make sure something is being minted.
|
|
@@ -965,18 +966,18 @@ contract DefifaHook is JB721Hook, Ownable, IDefifaHook {
|
|
|
965
966
|
uint256 _tierId = _tierIds[_i];
|
|
966
967
|
|
|
967
968
|
// Get a reference to the old delegate.
|
|
968
|
-
address _oldDelegate = _tierDelegation[context.
|
|
969
|
+
address _oldDelegate = _tierDelegation[context.beneficiary][_tierId];
|
|
969
970
|
|
|
970
971
|
// If there's either a new delegate or old delegate, set delegation and transfer units.
|
|
971
972
|
if (_attestationDelegate != address(0) || _oldDelegate != address(0)) {
|
|
972
973
|
// Switch delegates if needed.
|
|
973
974
|
if (_attestationDelegate != address(0) && _attestationDelegate != _oldDelegate) {
|
|
974
|
-
_delegateTier({_account: context.
|
|
975
|
+
_delegateTier({_account: context.beneficiary, _delegatee: _attestationDelegate, _tierId: _tierId});
|
|
975
976
|
}
|
|
976
977
|
|
|
977
978
|
// Transfer the attestation units.
|
|
978
979
|
_transferTierAttestationUnits({
|
|
979
|
-
_from: address(0), _to: context.
|
|
980
|
+
_from: address(0), _to: context.beneficiary, _tierId: _tierId, _amount: _attestationAmounts[_i]
|
|
980
981
|
});
|
|
981
982
|
}
|
|
982
983
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {IJBPermissions, JBPermissionsData} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
5
5
|
import {IJBProjects} from "@bananapus/core-v6/src/interfaces/IJBProjects.sol";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
5
5
|
|
|
@@ -312,6 +312,44 @@ contract DefifaHookRegressions is JBTest, TestBaseWorkflow {
|
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
/// @notice Paying for another account mints the NFT to the beneficiary and defaults attestation power to the
|
|
316
|
+
/// beneficiary (not the payer) when no explicit delegate is provided.
|
|
317
|
+
/// @dev When the metadata leaves the attestation delegate as address(0) and defaultAttestationDelegate is unset,
|
|
318
|
+
/// the source code falls back to context.beneficiary. This proves that NFT ownership and attestation power
|
|
319
|
+
/// both land on the beneficiary by default, and the payer retains neither.
|
|
320
|
+
function test_attestationUnitsFollowBeneficiaryByDefault() public {
|
|
321
|
+
DefifaLaunchProjectData memory defifaData = _getBasicLaunchData(2);
|
|
322
|
+
(uint256 projectId, DefifaHook nft, DefifaGovernor _governor) = _createProject(defifaData);
|
|
323
|
+
|
|
324
|
+
address payer = address(bytes20(keccak256("payer")));
|
|
325
|
+
address beneficiary = address(bytes20(keccak256("beneficiary")));
|
|
326
|
+
|
|
327
|
+
// Phase 1: Mint.
|
|
328
|
+
vm.warp(defifaData.start - defifaData.mintPeriodDuration - defifaData.refundPeriodDuration);
|
|
329
|
+
|
|
330
|
+
vm.deal(payer, 1 ether);
|
|
331
|
+
|
|
332
|
+
uint16[] memory rawMetadata = new uint16[](1);
|
|
333
|
+
rawMetadata[0] = 1;
|
|
334
|
+
bytes memory metadata = _buildPayMetadata(abi.encode(address(0), rawMetadata));
|
|
335
|
+
|
|
336
|
+
vm.prank(payer);
|
|
337
|
+
jbMultiTerminal().pay{value: 1 ether}(
|
|
338
|
+
projectId, JBConstants.NATIVE_TOKEN, 1 ether, beneficiary, 0, "", metadata
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
assertEq(nft.balanceOf(beneficiary), 1, "beneficiary should receive the NFT");
|
|
342
|
+
assertEq(nft.balanceOf(payer), 0, "payer should not receive the NFT");
|
|
343
|
+
|
|
344
|
+
vm.warp(_tsReader.timestamp() + 1);
|
|
345
|
+
|
|
346
|
+
uint256 payerWeight = _governor.getAttestationWeight(projectId, payer, uint48(block.timestamp));
|
|
347
|
+
uint256 beneficiaryWeight = _governor.getAttestationWeight(projectId, beneficiary, uint48(block.timestamp));
|
|
348
|
+
|
|
349
|
+
assertEq(payerWeight, 0, "payer receives no attestation power when delegate defaults to beneficiary");
|
|
350
|
+
assertGt(beneficiaryWeight, 0, "beneficiary receives the default attestation power");
|
|
351
|
+
}
|
|
352
|
+
|
|
315
353
|
// ----- Internal helpers ------
|
|
316
354
|
|
|
317
355
|
function _getBasicLaunchData(uint8 nTiers) internal returns (DefifaLaunchProjectData memory) {
|
package/test/DefifaUSDC.t.sol
CHANGED
package/test/Fork.t.sol
CHANGED
package/test/SVG.t.sol
CHANGED
package/test/TestAuditGaps.sol
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {TestBaseWorkflow} from "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
5
|
+
|
|
6
|
+
import {DefifaGovernor} from "../../src/DefifaGovernor.sol";
|
|
7
|
+
import {DefifaDeployer} from "../../src/DefifaDeployer.sol";
|
|
8
|
+
import {DefifaHook} from "../../src/DefifaHook.sol";
|
|
9
|
+
import {DefifaTokenUriResolver} from "../../src/DefifaTokenUriResolver.sol";
|
|
10
|
+
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
11
|
+
|
|
12
|
+
import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
|
|
13
|
+
import {JBRulesetMetadataResolver} from "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
|
|
14
|
+
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
15
|
+
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
16
|
+
|
|
17
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
18
|
+
import {ITypeface} from "lib/typeface/contracts/interfaces/ITypeface.sol";
|
|
19
|
+
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
20
|
+
import {DefifaLaunchProjectData} from "../../src/structs/DefifaLaunchProjectData.sol";
|
|
21
|
+
import {DefifaTierParams} from "../../src/structs/DefifaTierParams.sol";
|
|
22
|
+
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
23
|
+
import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
|
|
24
|
+
import {JBRulesetConfig} from "@bananapus/core-v6/src/structs/JBRulesetConfig.sol";
|
|
25
|
+
import {JBRulesetMetadata} from "@bananapus/core-v6/src/structs/JBRulesetMetadata.sol";
|
|
26
|
+
import {JBSplitGroup} from "@bananapus/core-v6/src/structs/JBSplitGroup.sol";
|
|
27
|
+
import {JBFundAccessLimitGroup} from "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
|
|
28
|
+
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
29
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
30
|
+
import {JBCurrencyIds} from "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
|
|
31
|
+
import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
32
|
+
|
|
33
|
+
contract CodexAttestationDoubleCount is JBTest, TestBaseWorkflow {
|
|
34
|
+
using JBRulesetMetadataResolver for JBRuleset;
|
|
35
|
+
|
|
36
|
+
uint256 internal _protocolFeeProjectId;
|
|
37
|
+
uint256 internal _defifaProjectId;
|
|
38
|
+
|
|
39
|
+
DefifaDeployer internal deployer;
|
|
40
|
+
DefifaHook internal hook;
|
|
41
|
+
DefifaGovernor internal governor;
|
|
42
|
+
|
|
43
|
+
uint256 internal _mintPhaseStart;
|
|
44
|
+
|
|
45
|
+
function setUp() public virtual override {
|
|
46
|
+
super.setUp();
|
|
47
|
+
|
|
48
|
+
JBAccountingContext[] memory _tokens = new JBAccountingContext[](1);
|
|
49
|
+
_tokens[0] = JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH});
|
|
50
|
+
|
|
51
|
+
JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
|
|
52
|
+
terminalConfigs[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: _tokens});
|
|
53
|
+
|
|
54
|
+
JBRulesetConfig[] memory rulesetConfigs = new JBRulesetConfig[](1);
|
|
55
|
+
rulesetConfigs[0] = JBRulesetConfig({
|
|
56
|
+
mustStartAtOrAfter: 0,
|
|
57
|
+
duration: 10 days,
|
|
58
|
+
weight: 1e18,
|
|
59
|
+
weightCutPercent: 0,
|
|
60
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
61
|
+
metadata: JBRulesetMetadata({
|
|
62
|
+
reservedPercent: 0,
|
|
63
|
+
cashOutTaxRate: 0,
|
|
64
|
+
baseCurrency: JBCurrencyIds.ETH,
|
|
65
|
+
pausePay: false,
|
|
66
|
+
pauseCreditTransfers: false,
|
|
67
|
+
allowOwnerMinting: false,
|
|
68
|
+
allowSetCustomToken: false,
|
|
69
|
+
allowTerminalMigration: false,
|
|
70
|
+
allowSetTerminals: false,
|
|
71
|
+
allowSetController: false,
|
|
72
|
+
allowAddAccountingContext: false,
|
|
73
|
+
allowAddPriceFeed: false,
|
|
74
|
+
ownerMustSendPayouts: false,
|
|
75
|
+
holdFees: false,
|
|
76
|
+
useTotalSurplusForCashOuts: false,
|
|
77
|
+
useDataHookForPay: true,
|
|
78
|
+
useDataHookForCashOut: true,
|
|
79
|
+
dataHook: address(0),
|
|
80
|
+
metadata: 0
|
|
81
|
+
}),
|
|
82
|
+
splitGroups: new JBSplitGroup[](0),
|
|
83
|
+
fundAccessLimitGroups: new JBFundAccessLimitGroup[](0)
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
address projectOwner = address(bytes20(keccak256("projectOwner")));
|
|
87
|
+
|
|
88
|
+
_protocolFeeProjectId =
|
|
89
|
+
jbController().launchProjectFor(address(projectOwner), "", rulesetConfigs, terminalConfigs, "");
|
|
90
|
+
vm.prank(projectOwner);
|
|
91
|
+
address _nanaToken =
|
|
92
|
+
address(jbController().deployERC20For(_protocolFeeProjectId, "Bananapus", "NANA", bytes32(0)));
|
|
93
|
+
|
|
94
|
+
_defifaProjectId =
|
|
95
|
+
jbController().launchProjectFor(address(projectOwner), "", rulesetConfigs, terminalConfigs, "");
|
|
96
|
+
vm.prank(projectOwner);
|
|
97
|
+
address _defifaToken = address(jbController().deployERC20For(_defifaProjectId, "Defifa", "DEFIFA", bytes32(0)));
|
|
98
|
+
|
|
99
|
+
hook = new DefifaHook(jbDirectory(), IERC20(_defifaToken), IERC20(_nanaToken));
|
|
100
|
+
governor = new DefifaGovernor(jbController(), address(this));
|
|
101
|
+
deployer = new DefifaDeployer(
|
|
102
|
+
address(hook),
|
|
103
|
+
new DefifaTokenUriResolver(ITypeface(address(0))),
|
|
104
|
+
governor,
|
|
105
|
+
jbController(),
|
|
106
|
+
new JBAddressRegistry(),
|
|
107
|
+
_defifaProjectId,
|
|
108
|
+
_protocolFeeProjectId
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
hook.transferOwnership(address(deployer));
|
|
112
|
+
governor.transferOwnership(address(deployer));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function test_attestationUnitsDuplicateAfterBeneficiaryTransfer() external {
|
|
116
|
+
address payer = address(bytes20(keccak256("payer")));
|
|
117
|
+
address beneficiary = address(bytes20(keccak256("beneficiary")));
|
|
118
|
+
address recipient = address(bytes20(keccak256("recipient")));
|
|
119
|
+
|
|
120
|
+
(uint256 projectId, DefifaHook nft) = _launchGame();
|
|
121
|
+
vm.warp(_mintPhaseStart);
|
|
122
|
+
|
|
123
|
+
vm.deal(payer, 1 ether);
|
|
124
|
+
uint16[] memory tierIds = new uint16[](1);
|
|
125
|
+
tierIds[0] = 1;
|
|
126
|
+
bytes memory metadata = _buildPayMetadata(abi.encode(address(0), tierIds));
|
|
127
|
+
|
|
128
|
+
vm.prank(payer);
|
|
129
|
+
jbMultiTerminal().pay{value: 1 ether}({
|
|
130
|
+
projectId: projectId,
|
|
131
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
132
|
+
amount: 1 ether,
|
|
133
|
+
beneficiary: beneficiary,
|
|
134
|
+
minReturnedTokens: 0,
|
|
135
|
+
memo: "",
|
|
136
|
+
metadata: metadata
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
uint256 totalUnitsBefore = nft.getTierTotalAttestationUnitsOf(1);
|
|
140
|
+
uint256 beneficiaryUnitsBefore = nft.getTierAttestationUnitsOf(beneficiary, 1);
|
|
141
|
+
assertGt(totalUnitsBefore, 0, "tier should have nonzero attestation units");
|
|
142
|
+
assertEq(beneficiaryUnitsBefore, totalUnitsBefore, "beneficiary receives delegated units after mint");
|
|
143
|
+
|
|
144
|
+
uint256 tokenId = 1_000_000_001;
|
|
145
|
+
vm.prank(beneficiary);
|
|
146
|
+
nft.transferFrom(beneficiary, recipient, tokenId);
|
|
147
|
+
|
|
148
|
+
uint256 beneficiaryUnitsAfter = nft.getTierAttestationUnitsOf(beneficiary, 1);
|
|
149
|
+
uint256 recipientUnitsAfter = nft.getTierAttestationUnitsOf(recipient, 1);
|
|
150
|
+
uint256 totalUnitsAfter = nft.getTierTotalAttestationUnitsOf(1);
|
|
151
|
+
|
|
152
|
+
// After the fix: attestation units go to beneficiary on mint, then move to recipient on transfer.
|
|
153
|
+
// Total units stay constant, beneficiary loses units, recipient gains them.
|
|
154
|
+
assertEq(totalUnitsAfter, totalUnitsBefore, "total tier units stay constant");
|
|
155
|
+
assertEq(beneficiaryUnitsAfter, 0, "beneficiary loses attestation units after transferring NFT");
|
|
156
|
+
assertEq(recipientUnitsAfter, totalUnitsAfter, "recipient receives full attestation units from transfer");
|
|
157
|
+
// No double-counting: sum of individual units equals total.
|
|
158
|
+
assertEq(
|
|
159
|
+
beneficiaryUnitsAfter + recipientUnitsAfter,
|
|
160
|
+
totalUnitsAfter,
|
|
161
|
+
"no double-counting: sum of individual units equals total"
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function _launchGame() internal returns (uint256 projectId, DefifaHook nft) {
|
|
166
|
+
DefifaTierParams[] memory tierParams = new DefifaTierParams[](2);
|
|
167
|
+
for (uint256 i = 0; i < 2; i++) {
|
|
168
|
+
tierParams[i] = DefifaTierParams({
|
|
169
|
+
reservedRate: 1001,
|
|
170
|
+
reservedTokenBeneficiary: address(0),
|
|
171
|
+
encodedIPFSUri: bytes32(0),
|
|
172
|
+
shouldUseReservedTokenBeneficiaryAsDefault: false,
|
|
173
|
+
name: "DEFIFA"
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
DefifaLaunchProjectData memory d = DefifaLaunchProjectData({
|
|
178
|
+
name: "DEFIFA",
|
|
179
|
+
projectUri: "",
|
|
180
|
+
contractUri: "",
|
|
181
|
+
baseUri: "",
|
|
182
|
+
tierPrice: 1 ether,
|
|
183
|
+
token: JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: JBCurrencyIds.ETH}),
|
|
184
|
+
mintPeriodDuration: 1 days,
|
|
185
|
+
start: uint48(block.timestamp + 3 days),
|
|
186
|
+
refundPeriodDuration: 1 days,
|
|
187
|
+
store: new JB721TiersHookStore(),
|
|
188
|
+
splits: new JBSplit[](0),
|
|
189
|
+
attestationStartTime: 0,
|
|
190
|
+
attestationGracePeriod: 100_381,
|
|
191
|
+
defaultAttestationDelegate: address(0),
|
|
192
|
+
tiers: tierParams,
|
|
193
|
+
defaultTokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
194
|
+
terminal: jbMultiTerminal(),
|
|
195
|
+
minParticipation: 0,
|
|
196
|
+
scorecardTimeout: 0
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
_mintPhaseStart = d.start - d.mintPeriodDuration - d.refundPeriodDuration;
|
|
200
|
+
|
|
201
|
+
projectId = deployer.launchGameWith(d);
|
|
202
|
+
|
|
203
|
+
JBRuleset memory fc = jbRulesets().currentOf(projectId);
|
|
204
|
+
if (fc.dataHook() == address(0)) {
|
|
205
|
+
(fc,) = jbRulesets().latestQueuedOf(projectId);
|
|
206
|
+
}
|
|
207
|
+
nft = DefifaHook(fc.dataHook());
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function _buildPayMetadata(bytes memory metadata) internal view returns (bytes memory) {
|
|
211
|
+
bytes[] memory data = new bytes[](1);
|
|
212
|
+
data[0] = metadata;
|
|
213
|
+
bytes4[] memory ids = new bytes4[](1);
|
|
214
|
+
ids[0] = metadataHelper().getId("pay", address(hook));
|
|
215
|
+
return metadataHelper().createMetadata(ids, data);
|
|
216
|
+
}
|
|
217
|
+
}
|
package/test/deployScript.t.sol
CHANGED