Skip to content

Commit

Permalink
Merge branch 'asset-l2-integration' of https://github.com/thesandboxg…
Browse files Browse the repository at this point in the history
…ame/sandbox-smart-contracts into integration-tests
  • Loading branch information
wojciech-turek committed Aug 11, 2023
2 parents 5a52ca8 + 8ea8af0 commit 7c3eb39
Show file tree
Hide file tree
Showing 79 changed files with 17,654 additions and 1,408 deletions.
4 changes: 4 additions & 0 deletions .nxignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ secret/
# We don't want the CI to run over all the packages, we decided to ignore it for now
yarn.lock
.yarn/*

# We don't do anything with docs right now, specifically we don't want to run over the core package.
/**/documentation
/**/docs
72 changes: 60 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,68 @@
# TSB contracts
<p align="center"><a href="https://sandbox.game"><img width="300" title="TSB" src='brand-assets/tsb_pride.png' /></a></p>

This workspace uses [Nx, a Smart, fast and extensible build system.](https://nx.dev)
<h1 align="center">The Sandbox Smart Contracts</h1>

## Understand this workspace
<a href="https://github.com/thesandboxgame/sandbox-smart-contracts/actions"><img alt="Build Status" src="https://github.com/thesandboxgame/sandbox-smart-contracts/actions/workflows/main.yml/badge.svg"/></a>
![Node Version](https://img.shields.io/badge/node-18.x-green)
[![Discord](https://img.shields.io/discord/497312527093334036.svg?label=Discord&logo=discord)](<https://discord.gg/vAe4zvY>)

[The Sandbox](https://sandbox.game) is a user-generated content (UGC) gaming platform, that will empower creators through digital ownership and monetization of 3D voxel creations made and shared by users around the world.

This mono-repo contains The Sandbox smart contracts, underpinning The Sandbox metaverse. Our key token contracts were conceived inside the core package. You can find out more about some of our early blockchain features in [this article](https://medium.com/sandbox-game/blockchain-features-in-the-sandbox-7db91fcc615c). Since core was created, we have added new contracts and updates via new packages - see the below Architecture Overview for more information.

## Learn more

- [Website](www.sandbox.game)
- [Discord](https://discordapp.com/invite/vAe4zvY)
- [Telegram](https://t.me/sandboxgame)
- [Medium](https://medium.com/sandbox-game)

## Prerequisites

- ⚙️ [NodeJS](https://nodejs.org/)
- 🧰 [Yarn](https://yarnpkg.com/)

## Tech stack and getting started

This workspace uses [Nx: a smart, fast and extensible build system.](https://nx.dev)
Run `nx graph` to see a diagram of the dependencies of the projects.

To add a package simply add it inside the packages directory, optionally every package can implement
the following targets that will be run by the root `package.json` scripts and on the CI:
To add a package simply add it inside the packages directory. Every package can implement the following targets that will be run by the root `package.json` scripts and on the CI:

- `test`: run unit tests inside your new package.
- `coverage`: run coverage. Note: CI expects to have at least 80% coverage.
- `lint`: lint your code.
- `format`: format the code with prettier.

See [`packages/example-hardhat`](./packages/example-hardhat) for an example template package for smart contract development with Hardhat.

Refer to each package's readme for more information about that package. Note that deployment for all new packages is carried out via `packages/deploy`.
Each package should follow the [audit best practices](./audit-best-practices.md) where applicable.

### Developer quickstart

```bash
yarn install
```

## Architecture Overview

- 📦 This mono-repository contains a suite of smart contract packages.
- ⚖️ The mono-repository is released under [MIT license](./LICENSE). Note, that the packages may contain their own licenses.

| Package | Version | License | Description |
|---------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`@sandbox-smart-contracts/packages/core`](./packages/core) | [![npm](https://img.shields.io/npm/v/@sandbox-smart-contracts/core)](https://www.npmjs.com/package/@sandbox-smart-contracts/core) | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 🗝️ Core smart contracts (pre 2023) |
| [`@sandbox-smart-contracts/packages/deploy`](./packages/deploy) | N/A | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 🚀 Deploy all packages (except core) |
| [`@sandbox-smart-contracts/packages/example-hardhat`](./packages/example-hardhat) | N/A | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 👷 Development template using Hardhat |
| [`@sandbox-smart-contracts/packages/giveaway`](./packages/giveaway) | [![npm](https://img.shields.io/npm/v/@sandbox-smart-contracts/giveaway)](https://www.npmjs.com/package/@sandbox-smart-contracts/giveaway) | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 🎁 Instant Giveaway smart contract claims |
| [`@sandbox-smart-contracts/packages/dependency-metatx`](./packages/dependency-metatx) | [![npm](https://img.shields.io/npm/v/@sandbox-smart-contracts/dependency-metatx)](https://www.npmjs.com/package/@sandbox-smart-contracts/dependency-metatx) | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 🌐 Dependency: ERC2771 handler |
| [`@sandbox-smart-contracts/packages/dependency-royalty-management`](./packages/dependency-royalty-management) | [![npm](https://img.shields.io/npm/v/@sandbox-smart-contracts/dependency-royalty-management)](https://www.npmjs.com/package/@sandbox-smart-contracts/royalty-management) | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 🎨 Dependency: The Sandbox Royalty Implementation in partnership with [Manifold's royalty-registry](https://github.com/manifoldxyz/royalty-registry-solidity/tree/main) |
| [`@sandbox-smart-contracts/packages/dependency-operator-filter`](./packages/dependency-operator-filter) | [![npm](https://img.shields.io/npm/v/@sandbox-smart-contracts/dependency-operator-filter)](https://www.npmjs.com/package/@sandbox-smart-contracts/operator-filter) | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 🤝 Dependency: The Sandbox's implementation for OpenSea's operator filter |
| [`@sandbox-smart-contracts/packages/asset`](./packages/asset) | [![npm](https://img.shields.io/npm/v/@sandbox-smart-contracts/asset)](https://www.npmjs.com/package/@sandbox-smart-contracts/asset) | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://spdx.org/licenses/MIT.html) | 🚗 Asset contract upgrade for L2 deployment featuring tiers, abilities, operator-filter and creator royalties |

- test
- coverage: run coverate, CI expect to have at least 80% coverage on packages.
- lint
- format: format the code with prettier.
- deploy: to test hardhat deployments.
## Contributing

## Further help
### Bug bounties

Visit the [Nx Documentation](https://nx.dev) to learn more.
Submit a bug at [Immunefi](https://immunefi.com/bounty/thesandbox/) to help secure our smart contracts!
Binary file added brand-assets/tsb_3d.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added brand-assets/tsb_main.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added brand-assets/tsb_pride.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
185 changes: 179 additions & 6 deletions packages/asset/test/Asset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
RoyaltyMultiRecipientsInterfaceId,
RoyaltyUGCInterfaceId,
} from './utils/interfaceIds';
import {BigNumber} from 'ethers';
const zeroAddress = '0x0000000000000000000000000000000000000000';

describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function () {
Expand Down Expand Up @@ -1002,15 +1003,15 @@ describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function (
const {
operatorFilterRegistry,
assetAdmin,
trustedForwarder,
TrustedForwarder,
filterOperatorSubscription,
RoyaltyManagerContract,
} = await setupOperatorFilter();
const AssetFactory = await ethers.getContractFactory('Asset');
const Asset = await upgrades.deployProxy(
AssetFactory,
[
trustedForwarder.address,
TrustedForwarder.address,
assetAdmin.address,
'ipfs://',
filterOperatorSubscription.address,
Expand Down Expand Up @@ -1045,15 +1046,15 @@ describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function (
it('should revert when registry is set zero and subscription is set zero', async function () {
const {
assetAdmin,
trustedForwarder,
TrustedForwarder,
filterOperatorSubscription,
RoyaltyManagerContract,
} = await setupOperatorFilter();
const AssetFactory = await ethers.getContractFactory('Asset');
const Asset = await upgrades.deployProxy(
AssetFactory,
[
trustedForwarder.address,
TrustedForwarder.address,
assetAdmin.address,
'ipfs://',
filterOperatorSubscription.address,
Expand All @@ -1076,7 +1077,7 @@ describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function (
it('should revert when registry is set and subscription is set by non-admin', async function () {
const {
assetAdmin,
trustedForwarder,
TrustedForwarder,
filterOperatorSubscription,
RoyaltyManagerContract,
operatorFilterRegistry,
Expand All @@ -1087,7 +1088,49 @@ describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function (
const Asset = await upgrades.deployProxy(
AssetFactory,
[
trustedForwarder.address,
TrustedForwarder.address,
assetAdmin.address,
'ipfs://',
filterOperatorSubscription.address,
RoyaltyManagerContract.address,
],
{
initializer: 'initialize',
}
);

await expect(
Asset.connect(user1).setOperatorRegistry(
operatorFilterRegistry.address
)
).to.be.revertedWith(
`AccessControl: account ${user1.address.toLocaleLowerCase()} is missing role ${defaultAdminRole}`
);

await expect(
Asset.connect(user1).registerAndSubscribe(
filterOperatorSubscription.address,
true
)
).to.be.revertedWith(
`AccessControl: account ${user1.address.toLocaleLowerCase()} is missing role ${defaultAdminRole}`
);
});
it('should not revert when registry is set and subscription is set by admin through trusted forwarder', async function () {
const {
assetAdmin,
TrustedForwarder,
filterOperatorSubscription,
RoyaltyManagerContract,
operatorFilterRegistry,
defaultAdminRole,
user1,
} = await setupOperatorFilter();
const AssetFactory = await ethers.getContractFactory('Asset');
const Asset = await upgrades.deployProxy(
AssetFactory,
[
TrustedForwarder.address,
assetAdmin.address,
'ipfs://',
filterOperatorSubscription.address,
Expand Down Expand Up @@ -1651,6 +1694,30 @@ describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function (
).to.be.revertedWith;
});

it('it should not be able to setApprovalForAll trusted forwarder if black listed', async function () {
const {
operatorFilterRegistryAsSubscription,
filterOperatorSubscription,
users,
TrustedForwarder,
} = await setupOperatorFilter();

const TrustedForwarderCodeHash =
await operatorFilterRegistryAsSubscription.codeHashOf(
TrustedForwarder.address
);

await operatorFilterRegistryAsSubscription.updateCodeHash(
filterOperatorSubscription.address,
TrustedForwarderCodeHash,
true
);

await expect(
users[1].Asset.setApprovalForAll(TrustedForwarder.address, true)
).to.be.revertedWithCustomError;
});

it('it should be able to setApprovalForAll blacklisted market places after they are removed from the blacklist ', async function () {
const {
mockMarketPlace1,
Expand Down Expand Up @@ -1755,6 +1822,54 @@ describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function (
).to.be.revertedWithCustomError;
});

it('it should be able to transfer through trusted forwarder after it is blacklisted', async function () {
const {
Asset,
users,
operatorFilterRegistryAsSubscription,
filterOperatorSubscription,
TrustedForwarder,
} = await setupOperatorFilter();
await Asset.mintWithoutMinterRole(users[0].address, 1, 2);

await operatorFilterRegistryAsSubscription.updateOperator(
filterOperatorSubscription.address,
TrustedForwarder.address,
true
);

const data = await Asset.connect(
users[0].Asset.signer
).populateTransaction[
'safeTransferFrom(address,address,uint256,uint256,bytes)'
](users[0].address, users[1].address, 1, 1, '0x');

await TrustedForwarder.execute({...data, value: BigNumber.from(0)});

expect(await Asset.balanceOf(users[1].address, 1)).to.be.equal(1);
});

it('it should not be able to transfer through trusted forwarder after if sender is blacklisted', async function () {
const {Asset, users, TrustedForwarder, mockMarketPlace1} =
await setupOperatorFilter();
await Asset.mintWithoutMinterRole(users[0].address, 1, 2);

await users[0].Asset.setApprovalForAllWithoutFilter(
mockMarketPlace1.address,
true
);

const data = await Asset.connect(
ethers.provider.getSigner(mockMarketPlace1.address)
).populateTransaction[
'safeTransferFrom(address,address,uint256,uint256,bytes)'
](users[0].address, users[1].address, 1, 1, '0x');

await expect(
TrustedForwarder.execute({...data, value: BigNumber.from(0)})
).to.be.revertedWith('Call execution failed');
});

it('it should be able to transfer through non blacklisted market places', async function () {
const {mockMarketPlace3, Asset, users} = await setupOperatorFilter();
await Asset.mintWithoutMinterRole(users[0].address, 1, 1);
Expand Down Expand Up @@ -2068,6 +2183,64 @@ describe('Base Asset Contract (/packages/asset/contracts/Asset.sol)', function (
expect(await Asset.balanceOf(users[1].address, 1)).to.be.equal(1);
expect(await Asset.balanceOf(users[1].address, 2)).to.be.equal(1);
});
it('should be able to batch transfer through trusted forwarder if it is black listed', async function () {
const {
Asset,
users,
operatorFilterRegistryAsSubscription,
filterOperatorSubscription,
TrustedForwarder,
} = await setupOperatorFilter();
const TrustedForwarderCodeHash =
await operatorFilterRegistryAsSubscription.codeHashOf(
TrustedForwarder.address
);
await Asset.mintWithoutMinterRole(users[0].address, 1, 1);
await Asset.mintWithoutMinterRole(users[0].address, 2, 1);

await operatorFilterRegistryAsSubscription.updateCodeHash(
filterOperatorSubscription.address,
TrustedForwarderCodeHash,
true
);

await operatorFilterRegistryAsSubscription.updateOperator(
filterOperatorSubscription.address,
TrustedForwarder.address,
true
);
const data = await Asset.connect(
users[0].Asset.signer
).populateTransaction[
'safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)'
](users[0].address, users[1].address, [1, 2], [1, 1], '0x');

await TrustedForwarder.execute({...data, value: BigNumber.from(0)});
expect(await Asset.balanceOf(users[1].address, 1)).to.be.equal(1);
expect(await Asset.balanceOf(users[1].address, 2)).to.be.equal(1);
});
it('should not be able to batch transfer through trusted forwarder if the sender is black listed', async function () {
const {Asset, users, TrustedForwarder, mockMarketPlace1} =
await setupOperatorFilter();

await Asset.mintWithoutMinterRole(users[0].address, 1, 1);
await Asset.mintWithoutMinterRole(users[0].address, 2, 1);

await users[0].Asset.setApprovalForAllWithoutFilter(
mockMarketPlace1.address,
true
);

const data = await Asset.connect(
ethers.provider.getSigner(mockMarketPlace1.address)
).populateTransaction[
'safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)'
](users[0].address, users[1].address, [1, 2], [1, 1], '0x');

await expect(
TrustedForwarder.execute({...data, value: BigNumber.from(0)})
).to.be.revertedWith('Call execution failed');
});
});
});
});
Loading

1 comment on commit 7c3eb39

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage for this commit

97.23%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
packages/asset/contracts
   Asset.sol94.53%89.58%96.43%98.08%108, 193, 298, 298–299, 70
   AssetCreate.sol93.42%75%100%100%125, 127, 160, 266, 64
   AssetReveal.sol96.60%87.50%100%100%138, 170, 365, 406, 68
   AuthSuperValidator.sol100%100%100%100%
   Catalyst.sol94.81%91.94%95.24%98.08%133, 135, 148, 160, 231, 88
packages/asset/contracts/interfaces
   IAsset.sol100%100%100%100%
   IAssetCreate.sol100%100%100%100%
   IAssetReveal.sol100%100%100%100%
   ICatalyst.sol100%100%100%100%
   ITokenUtils.sol100%100%100%100%
packages/asset/contracts/libraries
   TokenIdUtils.sol100%100%100%100%
packages/dependency-metatx/contracts
   ERC2771Handler.sol100%100%100%100%
   ERC2771HandlerAbstract.sol100%100%100%100%
   ERC2771HandlerUpgradeable.sol95.45%83.33%100%100%43
packages/dependency-metatx/contracts/test
   ERC2771HandlerTest.sol100%100%100%100%
   ERC2771HandlerUpgradeableTest.sol100%100%100%100%
   MockTrustedForwarder.sol0%0%0%0%15, 18–19, 19, 19–20
packages/dependency-operator-filter/contracts
   OperatorFiltererUpgradeable.sol88.64%85%100%90%14, 48–49, 58–59
   OperatorFilterSubscription.sol60%50%100%50%18–19
packages/dependency-operator-filter/contracts/interfaces
   IOperatorFilterRegistry.sol100%100%100%100%
packages/dependency-royalty-management/contracts
   MultiRoyaltyDistributor.sol88.71%62.50%100%97.44%101, 108, 41, 41, 41, 41, 70
   RoyaltyDistributor.sol90.91%50%100%100%44
   RoyaltyManager.sol96%86.36%100%100%100, 44, 94
   RoyaltySplitter.sol90.91%75%92.31%95.89%107, 147, 167, 176, 191, 228, 56, 63, 78
packages/dependency-royalty-management/contracts/interfaces
   IERC20Approve.sol100%100%100%100%
   IMultiRoyaltyDistributor.sol100%100%100%100%
   IMultiRoyaltyRecipients.sol100%100%100%100%
   IRoyaltyManager.sol100%100%100%100%
   IRoyaltyUGC.sol100%100%100%100%

Please sign in to comment.