change Shard, add share assembler, unit test for assembler
This commit is contained in:
parent
6c3321f2be
commit
1e33162ec5
24158
build/contracts/ShardAssembler.json
Normal file
24158
build/contracts/ShardAssembler.json
Normal file
File diff suppressed because one or more lines are too long
95
contracts/logic/ShardAssembler.sol
Normal file
95
contracts/logic/ShardAssembler.sol
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
35
migrations/10_deploy_shard.js
Normal file
35
migrations/10_deploy_shard.js
Normal 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}`);
|
||||
};
|
@ -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
77
test/shard.test.js
Normal 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
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user