change Shard, add share assembler, unit test for assembler

This commit is contained in:
CounterFire2023 2023-12-01 11:18:08 +08:00
parent 6c3321f2be
commit 1e33162ec5
6 changed files with 24458 additions and 26 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,95 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IBEERC1155.sol";
import "../interfaces/IAsset.sol";
import "../core/HasSignature.sol";
import "../utils/TimeChecker.sol";
import "../market/MallBase.sol";
import "../utils/UInt.sol";
contract ShardAssembler is Ownable, TimeChecker, HasSignature, MallBase {
using SafeERC20 for IERC20;
using UInt for uint256;
IAsset public hero;
IBEERC1155 public shard;
struct AssembleParams {
uint256 tokenId;
address payToken;
uint256 tokenAmount;
uint256 startTime;
uint256 saltNonce;
}
event ShardAssembled(
address indexed to,
uint256 indexed tokenId,
address indexed payToken,
uint256 tokenAmount
);
// generate constructor
constructor(address[2] memory _addresses){
hero = IAsset(_addresses[0]);
shard = IBEERC1155(_addresses[1]);
}
function assembleShard(
AssembleParams calldata params,
uint256[] memory ids,
uint256[] memory amounts,
bytes calldata signature
) external signatureValid(signature) timeValid(params.startTime) {
require(ids.length > 0, "ShardAssembler: ids length must > 0");
require(params.tokenId > 0, "ShardAssembler: tokenId can not be 0");
require(erc20Supported[params.payToken], "ShardAssembler: payToken not supported");
address user = _msgSender();
uint256[] memory signArray = new uint256[](ids.length * 2);
for (uint256 i = 0; i < ids.length; ++i) {
signArray[i * 2] = ids[i];
signArray[i * 2 + 1] = amounts[i];
}
bytes32 criteriaMessageHash =
getMessageHash(params, user, address(hero), address(this), signArray);
checkSigner(executor, criteriaMessageHash, signature);
shard.burnBatch(user, ids, amounts);
hero.batchMint(user, params.tokenId.asSingletonArray());
_useSignature(signature);
// transfer token to this contract
IERC20(params.payToken).transferFrom(user, feeToAddress, params.tokenAmount);
emit ShardAssembled(user, params.tokenId, params.payToken, params.tokenAmount);
}
function getMessageHash(
AssembleParams memory params,
address user,
address nftAddress,
address contractAddress,
uint256[] memory _ids
) internal pure returns (bytes32) {
bytes memory encoded = abi.encodePacked(
user,
nftAddress,
contractAddress,
params.tokenId,
params.payToken,
params.tokenAmount,
params.startTime,
params.saltNonce
);
uint256 len = _ids.length;
for (uint256 i = 0; i < len; ++i) {
encoded = bytes.concat(encoded, abi.encodePacked(_ids[i]));
}
return keccak256(encoded);
}
}

View File

@ -1,11 +1,78 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import "./BEBase1155.sol";
contract BEShard is ERC1155, AccessControl, ERC1155Burnable, ERC1155Supply {
bytes32 public constant URI_SETTER_ROLE = keccak256("URI_SETTER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
/**
* for Hero and Weapon shard
*/
contract BEShard is BEBase1155 {
constructor()
ERC1155("https://market.cebg.games/api/nft/info/{id}")
{
_setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(URI_SETTER_ROLE, msg.sender);
_setupRole(MINTER_ROLE, msg.sender);
}
function setURI(string memory newuri) public onlyRole(URI_SETTER_ROLE) {
_setURI(newuri);
}
function mint(address account, uint256 id, uint256 amount, bytes memory data)
public
onlyRole(MINTER_ROLE)
{
_mint(account, id, amount, data);
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
public
onlyRole(MINTER_ROLE)
{
_mintBatch(to, ids, amounts, data);
}
// The following functions are overrides required by Solidity.
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC1155, AccessControl)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
)
internal
override(ERC1155, ERC1155Supply)
{
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
/**
* @dev Grant mint role to address
*/
function setMintRole(address to) external {
grantRole(MINTER_ROLE, to);
}
/**
* @dev Remove mint role to address
*/
function removeMintRole(address to) external {
revokeRole(MINTER_ROLE, to);
}
}

View File

@ -0,0 +1,35 @@
let Shard = artifacts.require("tokens/erc1155/BEShard");
let ShardAssembler = artifacts.require("logic/ShardAssembler");
let Nft = artifacts.require("tokens/erc721/NFT");
let FT = artifacts.require("tokens/erc20/FT");
const base = require("../scripts/base");
module.exports = async function (deployer, network, accounts) {
const config = require(`../config/config_${network}`);
let cfgs = base.loadData({ network });
await deployer.deploy(Shard);
const shardInstance = await Shard.deployed();
if (shardInstance) {
console.log("BEShard successfully deployed: ", shardInstance.address);
}
const heroInstance = await Nft.at(cfgs.find((c) => c.name === "HERO").address);
await deployer.deploy(ShardAssembler, [heroInstance.address, shardInstance.address]);
const shardAssemblerInstance = await ShardAssembler.deployed();
if (shardAssemblerInstance) {
console.log("ShardAssembler successfully deployed: ", shardAssemblerInstance.address);
}
await shardInstance.setMintRole(config.admins.admin);
await heroInstance.setMintRole(shardAssemblerInstance.address);
console.log(`success set mint role for: ${heroInstance.address}`);
await shardInstance.setApprovalForAll(shardAssemblerInstance.address, true);
console.log(`success set approve for all for: ${shardInstance.address}`);
await shardAssemblerInstance.updateExecutor(config.admins.admin);
console.log(`success set executor for: ${shardAssemblerInstance.address}`);
const coinInstance = await FT.at(cfgs.find((c) => c.name === "CEC").address);
await shardAssemblerInstance.addERC20Support(coinInstance.address);
console.log(`success add erc20 support for: ${shardAssemblerInstance.address}`);
await shardAssemblerInstance.setFeeToAddress(config.admins.admin);
console.log(`success set fee to address for: ${shardAssemblerInstance.address}`);
};

View File

@ -3,126 +3,126 @@
"name": "BEBadge",
"type": "erc721",
"json": "assets/contracts/BEBadge.json",
"address": "0x9683c54eccfd8F7F0613Cd5AC7c3119ABa93ee36"
"address": "0x22FDaF881f724E2c9F93271a94A47725D217F77e"
},
{
"name": "BEMultiSigWallet",
"type": "logic",
"json": "assets/contracts/BEMultiSigWallet.json",
"address": "0xB28eAd4e87100aa438da90ce87dF9BAd201C391d"
"address": "0xfeD8697704b43C630f97F5178f6A89D2567E578b"
},
{
"name": "NftDistributor",
"type": "logic",
"json": "assets/contracts/NftDistributor.json",
"address": "0xC4F938c122907B39Eab6d1aD883E08808fB84985"
"address": "0x579fa19F24e3Be4cC311541B027030C793A25ee4"
},
{
"name": "CEC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xE449cB74e7f8f79d7E487EeC05E233C6c225db26"
"address": "0x31F0ed70DBAA0Ec06Bb215218FF40ab0e95b7B47"
},
{
"name": "CEG",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0x3276D767418bC9f1Aa3EC3C62e0805A2A2B0536c"
"address": "0x16d3e5B6C5d4BF1d4494592c3bA13882Ff5Df3b7"
},
{
"name": "BEUSDC",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0x60Ba00C1af6196Ff72E6B08e3fC1aAC76856d80d"
"address": "0xd573a6D24dB1625504Ea380FeC911C48Fc649Ae3"
},
{
"name": "BEUSDT",
"type": "erc20",
"json": "assets/contracts/FT.json",
"address": "0xc97A85581b2a5Af7c69838B783311332F522a451"
"address": "0x7d2B633c78e4d52807a6615CB6092BA1173Bd88A"
},
{
"name": "HERO",
"type": "erc721",
"json": "assets/contracts/NFT.json",
"address": "0x139348A7e57859d2F63274C7642Fbf9f1470B1a2"
"address": "0xbaFCbd63E5C21997138b2F20BD66180E5523Dc2f"
},
{
"name": "PLANET",
"type": "erc721",
"json": "assets/contracts/NFT.json",
"address": "0xD5fD5A9Ad06735AbB3Ff7A88D494556AdfFa2fcC"
"address": "0xF7f7bedAb92e44e88620654df63f7D409743a07e"
},
{
"name": "TestHERO",
"type": "erc721",
"json": "assets/contracts/NFT.json",
"address": "0x33Ecac595cFad08721aF95d53eC61Ec46b92071b"
"address": "0x2eFD5d3Ed0E2fd7fe17EC997110274f8cA968649"
},
{
"name": "UserMinterFactory",
"type": "logic",
"json": "assets/contracts/UserMinterFactory.json",
"address": "0x20889131ed057B57c5A7F66A349DbE66D3776038"
"address": "0xa56f6d75EE57a5969C0c05C19C064a4bCd7706C9"
},
{
"name": "BENftMarket",
"type": "logic",
"json": "assets/contracts/BENftMarket.json",
"address": "0xA31b1edf33E4fbAd25f515F2466dAD36a31239DA"
"address": "0xC5f17FBe7C9cD403F8b3799615F7550634422304"
},
{
"name": "BENftMall",
"type": "logic",
"json": "assets/contracts/BENftMall.json",
"address": "0x1c83bBb8c4A17aEE9b31452C24a784B989CEaBAA"
"address": "0xC5145E44b8CD72B583DEc18A1fb3b3B2510863F3"
},
{
"name": "BETokenMall",
"type": "logic",
"json": "assets/contracts/BETokenMall.json",
"address": "0xdEa731D41EeB6d52B3A003CdB8257337C94Dc3BE"
"address": "0xE3DC8F2c717C4cCb01913916a5274d1923d3C284"
},
{
"name": "GameItemMarket",
"type": "logic",
"json": "assets/contracts/GameItemMarket.json",
"address": "0xb8b88759e6FDc80309d29857d136E2b5eA01f7B0"
"address": "0xF7f49D559b8544cda8F7a2825e3d22daab8Eb976"
},
{
"name": "GameItemMall",
"type": "logic",
"json": "assets/contracts/GameItemMall.json",
"address": "0xAF5F30fB42DfE78B5726fF9c3EA2f9E15b6C0F8e"
"address": "0x6ca9e3df42C6A5e9F62385436A88b5b237Ec0bac"
},
{
"name": "Gacha",
"type": "erc721",
"json": "assets/contracts/BEBadge.json",
"address": "0xAa4367D3B2B745245edeCE3ae894053206152a63"
"address": "0x078276663CDAC67aF17f88A749C2aD03690176A2"
},
{
"name": "ClaimGachaFactory",
"type": "logic",
"json": "assets/contracts/ClaimBoxFactory.json",
"address": "0xCA510c9b2767E2DC39E7a38B24Ec26279ec3fAC1"
"address": "0x566aD19DbC9C1f2aED80DA37d9cca40Dc31A9f9A"
},
{
"name": "JSONMetadata",
"type": "logic",
"json": "assets/contracts/JSONMetadata.json",
"address": "0xb8739c95fF0EA21B6A4EDc91159080aFd5882847"
"address": "0xa779f75B02b66731B60be0f01EE60Cbd3ec939F3"
},
{
"name": "ERC721Staking",
"type": "logic",
"json": "assets/contracts/ERC721Staking.json",
"address": "0x193a9148CbC2cc57F4A4876D5CB451C24DEe50A1"
"address": "0xc1CF4029ba66950EdC24E62acF276dBFbdEE3358"
},
{
"name": "EvolveProxy",
"type": "logic",
"json": "assets/contracts/EvolveProxy.json",
"address": "0xA03eb8AA2D43169F94f7F199382c1a4ae585d59f"
"address": "0x38A716384689150B47cB63Ade766a9fdf6C9BF18"
}
]

77
test/shard.test.js Normal file
View File

@ -0,0 +1,77 @@
const ShardAssembler = artifacts.require("ShardAssembler");
const NFT = artifacts.require("NFT");
const Shard = artifacts.require("BEShard");
const FT = artifacts.require("FT");
contract("ShardAssembler", (accounts) => {
const admin = accounts[0];
const user = accounts[1];
const executor = accounts[0];
let hero;
let shardAssembler;
let shard;
let payToken;
let ids = [1, 2, 3];
let amounts = [2, 2, 2];
beforeEach(async () => {
hero = await NFT.new("CRYPTO ELITE'S HERO", "HERO", 0);
// Deploy the mock Shard contract
shard = await Shard.new();
// Deploy the ShardAssembler contract
shardAssembler = await ShardAssembler.new([hero.address, shard.address]);
payToken = await FT.new("FT", "FT", 0);
await payToken.setMintRole(admin);
await payToken.mint(user, '100000000000000000000000000');
await hero.setMintRole(shardAssembler.address);
await shard.setMintRole(admin);
await shard.setApprovalForAll(shardAssembler.address, true);
await shardAssembler.updateExecutor(executor);
await shardAssembler.setFeeToAddress(admin);
await shardAssembler.addERC20Support(payToken.address);
});
it("should mint shards correctly", async () => {
await shard.mintBatch(user, ids, amounts, []);
const userShardBalance = await shard.balanceOfBatch([user, user, user], ids);
assert.equal(userShardBalance[0], amounts[0], "User's shard balance is incorrect");
});
it("should assemble shards correctly", async () => {
// Call the assembleShard function
await shard.mintBatch(user, ids, amounts, []);
let tokenAmount = 1;
let tokenId = '100';
const startTime = Date.now() / 1000 | 0;
const saltNonce = (Math.random() * 1000) | 0;
let signArr = [];
for (let i = 0; i < ids.length; i++) {
signArr.push(ids[i]);
signArr.push(amounts[i]);
}
let signStr = web3.utils.soliditySha3.apply(this,
[user, hero.address, shardAssembler.address, tokenId, payToken.address,
tokenAmount,
startTime, saltNonce, ...signArr]);
let signature = await web3.eth.sign(signStr, executor);
signature = signature.replace(/00$/, "1b").replace(/01$/, "1c");
await payToken.approve(shardAssembler.address, tokenAmount, { from: user });
await shard.setApprovalForAll(shardAssembler.address, true, { from: user });
await shardAssembler.assembleShard(
[tokenId, payToken.address, tokenAmount, startTime, saltNonce],
ids,
amounts,
signature,
{ from: user }
);
// Get the user's hero balance
const userHeroBalance = await hero.balanceOf(user);
// Assert that the user's hero balance has increased by 1
assert.equal(userHeroBalance, 1, "User's hero balance is incorrect");
});
// Add more test cases as needed
});