From ff9e0ad241c0b5f9c40a17a7c5dd3d739af84685 Mon Sep 17 00:00:00 2001 From: Andres Adjimann Date: Mon, 11 Dec 2023 20:24:18 -0300 Subject: [PATCH] test: basic marketplace land integration test --- .../000_core/001_deploy_polygon_land.ts | 30 ++++ .../deploy/000_core/002_setup_polygon_land.ts | 28 +++ .../401_deploy_asset_auth_validator.ts | 3 +- .../500_marketplace/504_deploy_exchange.ts | 1 + packages/deploy/hardhat.config.ts | 1 + .../marketplaceLand/exchange.test.ts | 131 +++++++++++++++ .../marketplaceLand/orders.ts | 159 ++++++++++++++++++ .../{multiGiveaway => }/multiGiveaway.test.ts | 2 +- .../deploy/test/asset/AssetCreate.test.ts | 8 +- .../deploy/test/asset/AssetReveal.test.ts | 8 +- 10 files changed, 361 insertions(+), 10 deletions(-) create mode 100644 packages/deploy/deploy/000_core/001_deploy_polygon_land.ts create mode 100644 packages/deploy/deploy/000_core/002_setup_polygon_land.ts create mode 100644 packages/deploy/integration_test/marketplaceLand/exchange.test.ts create mode 100644 packages/deploy/integration_test/marketplaceLand/orders.ts rename packages/deploy/runtime_test/{multiGiveaway => }/multiGiveaway.test.ts (94%) diff --git a/packages/deploy/deploy/000_core/001_deploy_polygon_land.ts b/packages/deploy/deploy/000_core/001_deploy_polygon_land.ts new file mode 100644 index 0000000000..6b5f0799e1 --- /dev/null +++ b/packages/deploy/deploy/000_core/001_deploy_polygon_land.ts @@ -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']; diff --git a/packages/deploy/deploy/000_core/002_setup_polygon_land.ts b/packages/deploy/deploy/000_core/002_setup_polygon_land.ts new file mode 100644 index 0000000000..41dcdda95c --- /dev/null +++ b/packages/deploy/deploy/000_core/002_setup_polygon_land.ts @@ -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 { + 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']; diff --git a/packages/deploy/deploy/400_asset/401_deploy_asset_auth_validator.ts b/packages/deploy/deploy/400_asset/401_deploy_asset_auth_validator.ts index 332a7586be..9aba6effcf 100644 --- a/packages/deploy/deploy/400_asset/401_deploy_asset_auth_validator.ts +++ b/packages/deploy/deploy/400_asset/401_deploy_asset_auth_validator.ts @@ -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, }); diff --git a/packages/deploy/deploy/500_marketplace/504_deploy_exchange.ts b/packages/deploy/deploy/500_marketplace/504_deploy_exchange.ts index 1599ea3f04..9c55902b9d 100644 --- a/packages/deploy/deploy/500_marketplace/504_deploy_exchange.ts +++ b/packages/deploy/deploy/500_marketplace/504_deploy_exchange.ts @@ -45,5 +45,6 @@ func.tags = ['Exchange', 'Exchange_deploy']; func.dependencies = [ 'RoyaltiesRegistry_deploy', 'OrderValidator_deploy', + 'OrderValidator_setup', 'TRUSTED_FORWARDER_V2', ]; diff --git a/packages/deploy/hardhat.config.ts b/packages/deploy/hardhat.config.ts index ba67e11bb8..e880cd4110 100644 --- a/packages/deploy/hardhat.config.ts +++ b/packages/deploy/hardhat.config.ts @@ -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', ], diff --git a/packages/deploy/integration_test/marketplaceLand/exchange.test.ts b/packages/deploy/integration_test/marketplaceLand/exchange.test.ts new file mode 100644 index 0000000000..6cfd8e9d64 --- /dev/null +++ b/packages/deploy/integration_test/marketplaceLand/exchange.test.ts @@ -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); + }); +}); diff --git a/packages/deploy/integration_test/marketplaceLand/orders.ts b/packages/deploy/integration_test/marketplaceLand/orders.ts new file mode 100644 index 0000000000..d42604e0a6 --- /dev/null +++ b/packages/deploy/integration_test/marketplaceLand/orders.ts @@ -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 => ({ + 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 => ({ + assetType: { + assetClass: AssetClassType.ERC721_ASSET_CLASS, + data: ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256'], + [tokenContract.address, BigNumber.from(tokenId)] + ), + }, + value: BigNumber.from(1).toString(), +}); diff --git a/packages/deploy/runtime_test/multiGiveaway/multiGiveaway.test.ts b/packages/deploy/runtime_test/multiGiveaway.test.ts similarity index 94% rename from packages/deploy/runtime_test/multiGiveaway/multiGiveaway.test.ts rename to packages/deploy/runtime_test/multiGiveaway.test.ts index 852a4bd0aa..4dcf895274 100644 --- a/packages/deploy/runtime_test/multiGiveaway/multiGiveaway.test.ts +++ b/packages/deploy/runtime_test/multiGiveaway.test.ts @@ -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) => { diff --git a/packages/deploy/test/asset/AssetCreate.test.ts b/packages/deploy/test/asset/AssetCreate.test.ts index 0fdd78c51a..58de6742c7 100644 --- a/packages/deploy/test/asset/AssetCreate.test.ts +++ b/packages/deploy/test/asset/AssetCreate.test.ts @@ -7,21 +7,21 @@ const setupTest = deployments.createFixture( await getNamedAccounts(); await deployments.fixture(); const Asset = await deployments.get('Asset'); - const AssetContract = await ethers.getContractAt('Asset', Asset.address); + const AssetContract = await ethers.getContractAt(Asset.abi, Asset.address); const AssetCreate = await deployments.get('AssetCreate'); const AssetCreateContract = await ethers.getContractAt( - 'AssetCreate', + AssetCreate.abi, AssetCreate.address ); const Catalyst = await deployments.get('Catalyst'); const CatalystContract = await ethers.getContractAt( - 'Catalyst', + Catalyst.abi, Catalyst.address ); const TRUSTED_FORWARDER = await deployments.get('TRUSTED_FORWARDER_V2'); const AuthSuperValidator = await deployments.get('AuthSuperValidator'); const AuthSuperValidatorContract = await ethers.getContractAt( - 'AuthSuperValidator', + AuthSuperValidator.abi, AuthSuperValidator.address ); diff --git a/packages/deploy/test/asset/AssetReveal.test.ts b/packages/deploy/test/asset/AssetReveal.test.ts index 4a16c2a573..babe18e2b7 100644 --- a/packages/deploy/test/asset/AssetReveal.test.ts +++ b/packages/deploy/test/asset/AssetReveal.test.ts @@ -7,21 +7,21 @@ const setupTest = deployments.createFixture( await getNamedAccounts(); await deployments.fixture('Asset'); const Asset = await deployments.get('Asset'); - const AssetContract = await ethers.getContractAt('Asset', Asset.address); + const AssetContract = await ethers.getContractAt(Asset.abi, Asset.address); const AssetReveal = await deployments.get('AssetReveal'); const AssetRevealContract = await ethers.getContractAt( - 'AssetReveal', + AssetReveal.abi, AssetReveal.address ); const Catalyst = await deployments.get('Catalyst'); const CatalystContract = await ethers.getContractAt( - 'Catalyst', + Catalyst.abi, Catalyst.address ); const TRUSTED_FORWARDER = await deployments.get('TRUSTED_FORWARDER_V2'); const AuthSuperValidator = await deployments.get('AuthSuperValidator'); const AuthSuperValidatorContract = await ethers.getContractAt( - 'AuthSuperValidator', + AuthSuperValidator.abi, AuthSuperValidator.address );