Skip to content

Commit

Permalink
Merge pull request #1349 from thesandboxgame/feat/marketplace-land-in…
Browse files Browse the repository at this point in the history
…tegration-tests

test: basic marketplace land integration test
  • Loading branch information
adjisb authored Aug 8, 2024
2 parents a63c971 + ff9e0ad commit 3e128b1
Show file tree
Hide file tree
Showing 10 changed files with 361 additions and 10 deletions.
30 changes: 30 additions & 0 deletions packages/deploy/deploy/000_core/001_deploy_polygon_land.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {DeployFunction} from 'hardhat-deploy/types';
import {HardhatRuntimeEnvironment} from 'hardhat/types';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts} = hre;
const {deploy} = deployments;

const {deployer, upgradeAdmin} = await getNamedAccounts();

const TRUSTED_FORWARDER_V2 = await deployments.get('TRUSTED_FORWARDER_V2');

await deploy('PolygonLand', {
from: deployer,
contract:
'@sandbox-smart-contracts/core/src/solc_0.8/polygon/child/land/PolygonLandV2.sol:PolygonLandV2',
proxy: {
owner: upgradeAdmin,
proxyContract: 'OpenZeppelinTransparentProxy',
execute: {
methodName: 'initialize',
args: [TRUSTED_FORWARDER_V2.address],
},
upgradeIndex: 0,
},
log: true,
});
};
export default func;
func.tags = ['PolygonLand', 'PolygonLand_deploy', 'L2'];
func.dependencies = ['TRUSTED_FORWARDER_V2'];
28 changes: 28 additions & 0 deletions packages/deploy/deploy/000_core/002_setup_polygon_land.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {DeployFunction} from 'hardhat-deploy/types';
import {HardhatRuntimeEnvironment} from 'hardhat/types';

export const royaltyAmount = 500;

const func: DeployFunction = async function (
hre: HardhatRuntimeEnvironment
): Promise<void> {
const {deployments, getNamedAccounts} = hre;
const {execute, read, catchUnknownSigner} = deployments;

const {sandAdmin} = await getNamedAccounts();
const currentAdmin = await read('PolygonLand', {}, 'getAdmin');
if (currentAdmin !== sandAdmin) {
await catchUnknownSigner(
execute(
'PolygonLand',
{from: currentAdmin, log: true},
'changeAdmin',
sandAdmin
)
);
}
};

export default func;
func.tags = ['PolygonLand', 'PolygonLand_setup', 'L2'];
func.dependencies = ['PolygonLand_deploy'];
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployer, assetAdmin} = await getNamedAccounts();
await deploy('AuthSuperValidator', {
from: deployer,
contract: 'AuthSuperValidator',
contract:
'@sandbox-smart-contracts/asset/contracts/AuthSuperValidator.sol:AuthSuperValidator',
args: [assetAdmin],
log: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ func.tags = ['Exchange', 'Exchange_deploy'];
func.dependencies = [
'RoyaltiesRegistry_deploy',
'OrderValidator_deploy',
'OrderValidator_setup',
'TRUSTED_FORWARDER_V2',
];
1 change: 1 addition & 0 deletions packages/deploy/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const importedPackages = {
'@sandbox-smart-contracts/dependency-operator-filter': 'contracts/',
'@sandbox-smart-contracts/dependency-royalty-management': 'contracts/',
'@sandbox-smart-contracts/core': [
'/src/solc_0.8/polygon/child/land/PolygonLandV2.sol',
'/src/solc_0.8/polygon/child/sand/PolygonSand.sol',
'/src/solc_0.8/test/FakeChildChainManager.sol',
],
Expand Down
131 changes: 131 additions & 0 deletions packages/deploy/integration_test/marketplaceLand/exchange.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import {getContract, withSnapshot} from '../../utils/testUtils';
import {expect} from 'chai';
import {ethers} from 'hardhat';
import {BigNumber} from 'ethers';
import {AssetERC20, AssetERC721, OrderDefault, signOrder} from './orders';

const setupTest = withSnapshot(
['Exchange', 'PolygonSand', 'PolygonLand'],
async (hre) => {
const {sandAdmin} = await hre.getNamedAccounts();
const [user1, user2, minter] = await hre.getUnnamedAccounts();

const sandContract = await getContract(hre, 'PolygonSand');

const sandAdminSigner = await ethers.getSigner(sandAdmin);
const landContract = await getContract(hre, 'PolygonLand', sandAdminSigner);
await landContract.setMinter(minter, true);
const landContractAsMinter = await landContract.connect(
await ethers.getSigner(minter)
);

const childChainManager = await getContract(
hre,
'CHILD_CHAIN_MANAGER',
sandAdminSigner
);
await childChainManager.setPolygonAsset(landContract.address);

const exchangeAsUser2 = await getContract(
hre,
'Exchange',
await ethers.getSigner(user2)
);
const orderValidatorAsAdmin = await getContract(
hre,
'OrderValidator',
await ethers.getSigner(sandAdmin)
);
const TSB_ROLE = await orderValidatorAsAdmin.TSB_ROLE();
// enable land in whitelist
await orderValidatorAsAdmin.grantRole(
TSB_ROLE,
landContractAsMinter.address
);
return {
landContract,
landContractAsMinter,
sandContract,
exchangeAsUser2,
orderValidatorAsAdmin,
sandAdmin,
user1,
user2,
minter,
mintSand: async (user: string, amount: BigNumber) =>
childChainManager.callSandDeposit(
sandContract.address,
user,
ethers.utils.defaultAbiCoder.encode(['uint256'], [amount])
),
};
}
);

describe('Marketplace Land <-> Sand exchange', function () {
it('simple exchange', async function () {
const {
sandContract,
landContractAsMinter,
exchangeAsUser2,
orderValidatorAsAdmin,
user1,
user2,
mintSand,
} = await setupTest();
const oneEth = ethers.utils.parseEther('1');
const landTokenId = 0;
const size = 1;
await landContractAsMinter.mintQuad(user1, size, 0, 0, '0x');
expect(await landContractAsMinter.balanceOf(user1)).to.be.equal(
size * size
);

await mintSand(user2, oneEth);
expect(await sandContract.balanceOf(user2)).to.be.equal(oneEth);

await sandContract
.connect(await ethers.getSigner(user2))
.approve(exchangeAsUser2.address, oneEth);
await landContractAsMinter
.connect(await ethers.getSigner(user1))
.approve(exchangeAsUser2.address, landTokenId);

const makerAsset = await AssetERC721(landContractAsMinter, landTokenId);
const takerAsset = await AssetERC20(sandContract, oneEth);
const orderLeft = OrderDefault(
user1,
makerAsset,
ethers.constants.AddressZero,
takerAsset,
1,
0,
0
);
const orderRight = await OrderDefault(
user2,
takerAsset,
ethers.constants.AddressZero,
makerAsset,
1,
0,
0
);
const makerSig = await signOrder(orderLeft, user1, orderValidatorAsAdmin);
const takerSig = await signOrder(orderRight, user2, orderValidatorAsAdmin);
const tx = await exchangeAsUser2.matchOrders([
{
orderLeft,
signatureLeft: makerSig,
orderRight,
signatureRight: takerSig,
},
]);
const receipt = await tx.wait();
console.log(receipt.gasUsed.toString());
expect(await landContractAsMinter.balanceOf(user2)).to.be.equal(
size * size
);
expect(await sandContract.balanceOf(user1)).to.be.equal(oneEth);
});
});
159 changes: 159 additions & 0 deletions packages/deploy/integration_test/marketplaceLand/orders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import {ethers} from 'hardhat';
import {BigNumber, BigNumberish, Contract} from 'ethers';

export const ASSET_TYPE_TYPEHASH = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes('AssetType(uint256 assetClass,bytes data)')
);

export function hashAssetType(a: AssetType) {
if (a.assetClass === AssetClassType.INVALID_ASSET_CLASS) {
throw new Error('Invalid assetClass' + a.assetClass);
}
return ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
['bytes32', 'uint256', 'bytes32'],
[ASSET_TYPE_TYPEHASH, a.assetClass, ethers.utils.keccak256(a.data)]
)
);
}

export function hashKey(order: Order): string {
const encoded = ethers.utils.defaultAbiCoder.encode(
['address', 'bytes32', 'bytes32', 'uint256'],
[
order.maker,
hashAssetType(order.makeAsset.assetType),
hashAssetType(order.takeAsset.assetType),
order.salt,
]
);
return ethers.utils.keccak256(encoded);
}

export async function signOrder(
order: Order,
account: string,
verifyingContract: Contract
) {
const network = await verifyingContract.provider.getNetwork();
return await ethers.provider.send('eth_signTypedData_v4', [
account,
{
types: {
EIP712Domain: [
{
name: 'name',
type: 'string',
},
{
name: 'version',
type: 'string',
},
{
name: 'chainId',
type: 'uint256',
},
{
name: 'verifyingContract',
type: 'address',
},
],
AssetType: [
{name: 'assetClass', type: 'uint256'},
{name: 'data', type: 'bytes'},
],
Asset: [
{name: 'assetType', type: 'AssetType'},
{name: 'value', type: 'uint256'},
],
Order: [
{name: 'maker', type: 'address'},
{name: 'makeAsset', type: 'Asset'},
{name: 'taker', type: 'address'},
{name: 'takeAsset', type: 'Asset'},
{name: 'salt', type: 'uint256'},
{name: 'start', type: 'uint256'},
{name: 'end', type: 'uint256'},
],
},
primaryType: 'Order',
domain: {
name: 'The Sandbox Marketplace',
version: '1.0.0',
chainId: network.chainId,
verifyingContract: verifyingContract.address,
},
message: order,
},
]);
}

export type Order = {
maker: string;
makeAsset: Asset;
taker: string;
takeAsset: Asset;
salt: string;
start: string;
end: string;
};

export const OrderDefault = (
maker: string,
makeAsset: Asset,
taker: string,
takeAsset: Asset,
salt: BigNumberish,
start: BigNumberish,
end: BigNumberish
): Order => ({
maker: maker,
makeAsset,
taker: taker,
takeAsset,
salt: BigNumber.from(salt).toString(),
start: BigNumber.from(start).toString(),
end: BigNumber.from(end).toString(),
});

export enum AssetClassType {
INVALID_ASSET_CLASS = '0x0',
ERC20_ASSET_CLASS = '0x1',
ERC721_ASSET_CLASS = '0x2',
ERC1155_ASSET_CLASS = '0x3',
}

export type AssetType = {
assetClass: AssetClassType;
data: string;
};
export type Asset = {
assetType: AssetType;
value: string;
};
export const AssetERC20 = async (
tokenContract: Contract,
value: BigNumberish
): Promise<Asset> => ({
assetType: {
assetClass: AssetClassType.ERC20_ASSET_CLASS,
data: ethers.utils.defaultAbiCoder.encode(
['address'],
[tokenContract.address]
),
},
value: BigNumber.from(value).toString(),
});
export const AssetERC721 = async (
tokenContract: Contract,
tokenId: BigNumberish
): Promise<Asset> => ({
assetType: {
assetClass: AssetClassType.ERC721_ASSET_CLASS,
data: ethers.utils.defaultAbiCoder.encode(
['address', 'uint256'],
[tokenContract.address, BigNumber.from(tokenId)]
),
},
value: BigNumber.from(1).toString(),
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {getContract, withSnapshot} from '../../utils/testUtils';
import {getContract, withSnapshot} from '../utils/testUtils';
import {expect} from 'chai';

const setupTest = withSnapshot(['SignedMultiGiveaway'], async (hre) => {
Expand Down
Loading

0 comments on commit 3e128b1

Please sign in to comment.