Skip to content

test: basic marketplace land integration test #1349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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