Skip to content

Commit cab59b4

Browse files
Merge pull request #1685 from thesandboxgame/passes-audit-h-03
[H-03] Game Passes Audit
2 parents 04e213f + c1b3594 commit cab59b4

File tree

7 files changed

+79
-32
lines changed

7 files changed

+79
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {DeployFunction} from 'hardhat-deploy/types';
2+
import {HardhatRuntimeEnvironment} from 'hardhat/types';
3+
import {DEPLOY_TAGS} from '../../hardhat.config';
4+
5+
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
6+
const {deployments, getNamedAccounts} = hre;
7+
const {deploy} = deployments;
8+
const {
9+
deployer,
10+
treasury,
11+
commonRoyaltyReceiver,
12+
backendAuthWallet,
13+
sandAdmin,
14+
upgradeAdmin,
15+
} = await getNamedAccounts();
16+
17+
const TRUSTED_FORWARDER = await deployments.get('SandboxForwarder');
18+
const sandContract = await deployments.get('PolygonSand');
19+
const BASE_URL = 'https://contracts.sandbox.game';
20+
21+
await deploy('GamePasses', {
22+
from: deployer,
23+
contract:
24+
'@sandbox-smart-contracts/game-passes/contracts/GamePasses.sol:GamePasses',
25+
proxy: {
26+
owner: upgradeAdmin,
27+
proxyContract: 'OpenZeppelinTransparentProxy',
28+
execute: {
29+
methodName: 'initialize',
30+
args: [
31+
{
32+
baseURI: BASE_URL,
33+
royaltyReceiver: commonRoyaltyReceiver,
34+
royaltyFeeNumerator: 500, // 5%
35+
admin: sandAdmin,
36+
operator: deployer,
37+
signer: backendAuthWallet,
38+
paymentToken: sandContract.address,
39+
trustedForwarder: TRUSTED_FORWARDER.address,
40+
defaultTreasury: treasury,
41+
owner: sandAdmin,
42+
},
43+
],
44+
},
45+
upgradeIndex: 0,
46+
},
47+
log: true,
48+
});
49+
};
50+
export default func;
51+
52+
func.tags = [
53+
'GamePass',
54+
'GamePass_deploy',
55+
DEPLOY_TAGS.L2,
56+
DEPLOY_TAGS.L2_PROD,
57+
DEPLOY_TAGS.L2_TEST,
58+
];
59+
func.dependencies = ['SandboxForwarder'];

packages/deploy/hardhat.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ const importedPackages = {
9595
'@sandbox-smart-contracts/sandbox-forwarder': [
9696
'contracts/SandboxForwarder.sol',
9797
],
98+
'@sandbox-smart-contracts/game-passes': ['contracts/GamePasses.sol'],
9899
};
99100

100101
const namedAccounts = {

packages/deploy/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@sandbox-smart-contracts/dependency-operator-filter": "1.0.1",
2424
"@sandbox-smart-contracts/dependency-royalty-management": "1.0.2",
2525
"@sandbox-smart-contracts/faucets": "0.0.1",
26+
"@sandbox-smart-contracts/game-passes": "*",
2627
"@sandbox-smart-contracts/giveaway": "0.0.3",
2728
"@sandbox-smart-contracts/land": "1.0.0-rc.1",
2829
"@sandbox-smart-contracts/land-sale": "1.0.0-rc.1",

packages/game-passes/contracts/GamePasses.sol

+12-20
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,6 @@ contract GamePasses is
311311
* - trustedForwarder: Address of the trusted meta-transaction forwarder.
312312
* - defaultTreasury: Address of the default treasury wallet.
313313
* - owner: Address that will be set as the internal owner.
314-
* - sandContract: Address of the SAND contract.
315314
*/
316315
function initialize(InitParams calldata params) external initializer {
317316
__ERC2771Handler_init(params.trustedForwarder);
@@ -582,11 +581,10 @@ contract GamePasses is
582581
}
583582

584583
_updateAndCheckTotalMinted(mintTokenId, mintAmount);
585-
mintConfig.mintedPerWallet[mintTo] += mintAmount;
584+
_updateAndCheckMaxPerWallet(mintTokenId, mintTo, mintAmount);
586585

587586
_burn(burnFrom, burnTokenId, burnAmount);
588587

589-
_checkMaxPerWallet(mintTokenId, mintTo, mintAmount);
590588
_mint(mintTo, mintTokenId, mintAmount, "");
591589
}
592590

@@ -640,8 +638,7 @@ contract GamePasses is
640638
}
641639

642640
_updateAndCheckTotalMinted(mintTokenIds[i], mintAmounts[i]);
643-
_checkMaxPerWallet(mintTokenIds[i], mintTo, mintAmounts[i]);
644-
mintConfig.mintedPerWallet[mintTo] += mintAmounts[i];
641+
_updateAndCheckMaxPerWallet(mintTokenIds[i], mintTo, mintAmounts[i]);
645642
}
646643

647644
_burnBatch(burnFrom, burnTokenIds, burnAmounts);
@@ -707,11 +704,9 @@ contract GamePasses is
707704
verifyBurnAndMintSignature(request, signature);
708705

709706
_updateAndCheckTotalMinted(mintId, mintAmount);
710-
mintConfig.mintedPerWallet[caller] += mintAmount;
707+
_updateAndCheckMaxPerWallet(mintId, caller, mintAmount);
711708

712709
_burn(caller, burnId, burnAmount);
713-
714-
_checkMaxPerWallet(mintId, caller, mintAmount);
715710
_mint(caller, mintId, mintAmount, "");
716711
}
717712

@@ -1074,12 +1069,11 @@ contract GamePasses is
10741069
/**
10751070
* @notice Returns the metadata URI for a specific token ID
10761071
* @param tokenId ID of the token to get URI for
1077-
* @dev Constructs the URI by concatenating baseURI + tokenId + ".json"
1078-
* @dev Can be overridden by derived contracts to implement different URI logic
1079-
* @return string The complete URI for the token metadata
1072+
* @dev Returns the token-specific metadata string stored in the token configuration
1073+
* @return string The metadata URI for the token
10801074
*/
10811075
function uri(uint256 tokenId) public view virtual override returns (string memory) {
1082-
return string(abi.encodePacked(_coreStorage().baseURI, tokenId.toString(), ".json"));
1076+
return _tokenStorage().tokenConfigs[tokenId].metadata;
10831077
}
10841078

10851079
/**
@@ -1272,11 +1266,9 @@ contract GamePasses is
12721266

12731267
verifySignature(request, signature);
12741268

1275-
_checkMaxPerWallet(request.tokenId, request.caller, request.amount);
1269+
_updateAndCheckMaxPerWallet(request.tokenId, request.caller, request.amount);
12761270
_updateAndCheckTotalMinted(request.tokenId, request.amount);
12771271

1278-
config.mintedPerWallet[request.caller] += request.amount;
1279-
12801272
address treasury = config.treasuryWallet;
12811273
if (treasury == address(0)) {
12821274
treasury = _coreStorage().defaultTreasuryWallet;
@@ -1308,11 +1300,9 @@ contract GamePasses is
13081300
revert TokenNotConfigured(request.tokenIds[i]);
13091301
}
13101302

1311-
_checkMaxPerWallet(request.tokenIds[i], request.caller, request.amounts[i]);
1303+
_updateAndCheckMaxPerWallet(request.tokenIds[i], request.caller, request.amounts[i]);
13121304
_updateAndCheckTotalMinted(request.tokenIds[i], request.amounts[i]);
13131305

1314-
config.mintedPerWallet[request.caller] += request.amounts[i];
1315-
13161306
address treasury = config.treasuryWallet;
13171307
if (treasury == address(0)) {
13181308
treasury = _coreStorage().defaultTreasuryWallet;
@@ -1391,14 +1381,16 @@ contract GamePasses is
13911381
* - Current wallet balance + amount would exceed max per wallet
13921382
* @dev Skips check if maxPerWallet is type(uint256).max (unlimited)
13931383
*/
1394-
function _checkMaxPerWallet(uint256 tokenId, address to, uint256 amount) private view {
1384+
function _updateAndCheckMaxPerWallet(uint256 tokenId, address to, uint256 amount) private {
13951385
TokenConfig storage config = _tokenStorage().tokenConfigs[tokenId];
13961386

1387+
config.mintedPerWallet[to] += amount;
1388+
13971389
if (config.maxPerWallet == 0) {
13981390
revert ExceedsMaxPerWallet(tokenId, to, amount, 0);
13991391
}
14001392

1401-
if (config.maxPerWallet != type(uint256).max && config.mintedPerWallet[to] + amount > config.maxPerWallet) {
1393+
if (config.mintedPerWallet[to] > config.maxPerWallet) {
14021394
revert ExceedsMaxPerWallet(tokenId, to, amount, config.maxPerWallet);
14031395
}
14041396
}

packages/game-passes/test/GamePasses.test.ts

+4-8
Original file line numberDiff line numberDiff line change
@@ -2772,16 +2772,14 @@ describe('GamePasses', function () {
27722772

27732773
describe('URI', function () {
27742774
it('should return correct token URI', async function () {
2775-
const {sandboxPasses, TOKEN_ID_1, BASE_URI} =
2775+
const {sandboxPasses, TOKEN_ID_1, TOKEN_METADATA} =
27762776
await loadFixture(runCreateTestSetup);
27772777

2778-
expect(await sandboxPasses.uri(TOKEN_ID_1)).to.equal(
2779-
`${BASE_URI}${TOKEN_ID_1}.json`,
2780-
);
2778+
expect(await sandboxPasses.uri(TOKEN_ID_1)).to.equal(TOKEN_METADATA);
27812779
});
27822780

27832781
it('should allow admin to update base URI', async function () {
2784-
const {sandboxPasses, admin, TOKEN_ID_1, BASE_URI} =
2782+
const {sandboxPasses, admin, TOKEN_ID_1, BASE_URI, TOKEN_METADATA} =
27852783
await loadFixture(runCreateTestSetup);
27862784

27872785
const newBaseURI = 'https://new-api.example.com/metadata/';
@@ -2791,9 +2789,7 @@ describe('GamePasses', function () {
27912789
.withArgs(admin.address, BASE_URI, newBaseURI);
27922790

27932791
expect(await sandboxPasses.baseURI()).to.equal(newBaseURI);
2794-
expect(await sandboxPasses.uri(TOKEN_ID_1)).to.equal(
2795-
`${newBaseURI}${TOKEN_ID_1}.json`,
2796-
);
2792+
expect(await sandboxPasses.uri(TOKEN_ID_1)).to.equal(TOKEN_METADATA);
27972793
});
27982794

27992795
it('should not allow non-admin to update base URI', async function () {

packages/sandbox-forwarder/package.json

-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
"version": "1.0.0",
44
"description": "Meta-transaction forwarder contract for The Sandbox",
55
"private": true,
6-
"scripts": {
7-
8-
},
96
"dependencies": {
107
"@openzeppelin/contracts": "5.2.0"
118
},

yarn.lock

+2-1
Original file line numberDiff line numberDiff line change
@@ -2961,6 +2961,7 @@ __metadata:
29612961
"@sandbox-smart-contracts/dependency-operator-filter": 1.0.1
29622962
"@sandbox-smart-contracts/dependency-royalty-management": 1.0.2
29632963
"@sandbox-smart-contracts/faucets": 0.0.1
2964+
"@sandbox-smart-contracts/game-passes": "*"
29642965
"@sandbox-smart-contracts/giveaway": 0.0.3
29652966
"@sandbox-smart-contracts/land": 1.0.0-rc.1
29662967
"@sandbox-smart-contracts/land-sale": 1.0.0-rc.1
@@ -3082,7 +3083,7 @@ __metadata:
30823083
languageName: unknown
30833084
linkType: soft
30843085

3085-
"@sandbox-smart-contracts/game-passes@workspace:packages/game-passes":
3086+
"@sandbox-smart-contracts/game-passes@*, @sandbox-smart-contracts/game-passes@workspace:packages/game-passes":
30863087
version: 0.0.0-use.local
30873088
resolution: "@sandbox-smart-contracts/game-passes@workspace:packages/game-passes"
30883089
dependencies:

0 commit comments

Comments
 (0)