Skip to content
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

【LocalNouns】都道府県ランダムミント時の割合設定 #158

Merged
merged 11 commits into from
Nov 1, 2023
18 changes: 16 additions & 2 deletions contract/contracts/LocalNounsToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ contract LocalNounsToken is ProviderTokenA2, ILocalNounsToken {
uint256 _prefectureId,
uint256 _amount
) public virtual returns (uint256 tokenId) {
require(msg.sender == minter, 'Sender is not the minter');
require(msg.sender == minter || msg.sender == owner(), 'Sender is not minter nor owner');
require(_prefectureId % 100 <= 47, 'Invalid prefectureId');

for (uint256 i = 0; i < _amount; i++) {
Expand All @@ -87,6 +87,20 @@ contract LocalNounsToken is ProviderTokenA2, ILocalNounsToken {
return _nextTokenId() - 1;
}

function ownerMint(
address[] memory _to,
uint256[] memory _prefectureId,
uint256[] memory _amount
) public onlyOwner returns (uint256 tokenId) {
// 引数の整合性チェック
require(_to.length == _prefectureId.length && _to.length == _amount.length, 'Invalid Arrays length');

for (uint256 i; i < _to.length; i++) {
mintSelectedPrefecture(_to[i], _prefectureId[i], _amount[i]);
}
return _nextTokenId() - 1;
}

function mint() public payable override returns (uint256 tokenId) {
revert('Cannot use this function');
}
Expand Down Expand Up @@ -171,7 +185,7 @@ contract LocalNounsToken is ProviderTokenA2, ILocalNounsToken {
(bool sent, ) = payable(administratorsAddress).call{ value: address(this).balance }('');
require(sent, 'failed to move fund to administratorsAddress contract');
}

// iLocalNounsTokenでERC721のtotalSupplyを使用したいけど、二重継承でエラーになるので個別関数を準備
function totalSupply2() public view returns (uint256) {
return super.totalSupply();
Expand Down
59 changes: 53 additions & 6 deletions contract/contracts/localNouns/LocalNounsProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ contract LocalNounsProvider is IAssetProviderExMint, IERC165, Ownable {
mapping(uint256 => uint256) public tokenIdToPrefectureId;
mapping(uint256 => string) public prefectureName;
mapping(uint256 => uint256) public mintNumberPerPrefecture; // 都道府県ごとのミント数
// mapping(uint256 => uint256) public prefectureRatio; // ランダムミント時に決定される都道府県の割合
uint256 totalPrefectureRatio;

uint256[5] ratioRank = [5, 4, 3, 3, 2];
uint256[5] acumurationRatioRank;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

acumuration -> accumulation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました
4b6a322

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cも2つ必要です。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accumulation
です。

uint256 acumurationRatioRankTotal;
mapping(uint256 => uint256[]) public prefectureRatio;

constructor(
INounsDescriptor _descriptor,
Expand Down Expand Up @@ -94,6 +101,17 @@ contract LocalNounsProvider is IAssetProviderExMint, IERC165, Ownable {
prefectureName[45] = 'Miyazaki';
prefectureName[46] = 'Kagoshima';
prefectureName[47] = 'Okinawa';

prefectureRatio[0] = [13, 14, 27, 23, 11, 12, 28, 1, 40];
prefectureRatio[1] = [22, 8, 34, 26, 4, 15, 20, 21, 10];
prefectureRatio[2] = [9, 33, 7, 24, 43, 46, 47, 25, 35];
prefectureRatio[3] = [29, 38, 42, 2, 3, 17, 44, 45, 6, 16];
prefectureRatio[4] = [37, 5, 30, 19, 41, 18, 36, 39, 32, 31];

for (uint256 i = 0; i < ratioRank.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コストラクタで計算するより値を与えておいたほうが良い気がしますが、こっちのほうがガス代が安い?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘通りです、、、
25178fd

acumurationRatioRankTotal += ratioRank[i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memo
acumurationRatioRankTotal = sum ([5, 4, 3, 3, 2])
acumurationRatioRank = [5, 9, 12, 15, 17]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コードで定義するようにしました。
sumが使えるのですね。覚えておきます。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint256[5] acumulationRatioRank = [5, 9, 12, 15, 17];
uint256 acumulationRatioRankTotal = 17;
ですね(追加されたコードから)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sumは使えません。擬似コードです(メモ用)

acumurationRatioRank[i] = acumurationRatioRankTotal;
}
}

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
Expand Down Expand Up @@ -166,16 +184,45 @@ contract LocalNounsProvider is IAssetProviderExMint, IERC165, Ownable {
);
}

function mint(uint256 prefectureId, uint256 _assetId) external returns (uint256) {
bool randomValueForTest = false;

function setRandomValueForTest(bool _test) public onlyOwner {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここの変更はオーナーしかできないですが、オーナーが変更してtrueのときには、誰でもdeterminePrefectureIdでassetIdを指定できます。
デプロイ後はtrueにしないほうがよいですね。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardhatテストでだけ使用するようにします

randomValueForTest = _test;
}

// テスト用にpublic
function determinePrefectureId(uint256 _assetId) public view returns (uint256) {
uint256 randomValue;
if (randomValueForTest) {
// For TEST
randomValue = _assetId;
} else {
// ブロック番号とアセット番号から計算した値 -> ランダム値
randomValue = uint256(keccak256(abi.encodePacked(block.timestamp, _assetId)));
}

uint256 rank = randomValue % acumurationRatioRankTotal;
for (uint256 i = 0; i < acumurationRatioRank.length; i++) {
if (rank < acumurationRatioRank[i]) {
rank = i;
break;
}
}
return prefectureRatio[rank][randomValue % prefectureRatio[rank].length];
}

function mint(uint256 _prefectureId, uint256 _assetId) external returns (uint256) {
uint256 prefectureId;
// 末尾2桁が00の場合は都道府県をランダムに決定する
if (prefectureId % 100 == 0) {
prefectureId = prefectureId + ((block.number * _assetId) % 46) + 1;
if (_prefectureId % 100 == 0) {
prefectureId = _prefectureId + determinePrefectureId(_assetId);
} else {
prefectureId = _prefectureId;
}

seeds[_assetId] = generateSeed(prefectureId, _assetId);
uint256 prefectureId = prefectureId % 100; // 下2桁:都道府県番号、下3桁より上位:バージョン番号
tokenIdToPrefectureId[_assetId] = prefectureId;
mintNumberPerPrefecture[prefectureId]++;
tokenIdToPrefectureId[_assetId] = prefectureId % 100;
mintNumberPerPrefecture[prefectureId % 100]++;
nextTokenId++;

return _assetId;
Expand Down
71 changes: 60 additions & 11 deletions contract/test/localNouns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ describe('mint functions', function () {
.revertedWith('Cannot use this function');

await expect(token.connect(user1).functions.mintSelectedPrefecture(user1.address, 1, 1))
.revertedWith('Sender is not the minter');
.revertedWith('Sender is not minter nor owner');

await expect(token.connect(user1).functions.ownerMint([user1.address], [1], [1]))
.revertedWith('Ownable: caller is not the owner');

});

Expand Down Expand Up @@ -93,7 +96,7 @@ describe('mint functions', function () {
it('mint from minter', async function () {

const txParams = { value: ethers.utils.parseUnits("0.001", "ether") };
await minter.connect(user1).functions.mintSelectedPrefecture(0, 1,txParams);
await minter.connect(user1).functions.mintSelectedPrefecture(0, 1, txParams);

const [balance] = await token.functions.balanceOf(user1.address);
expect(balance.toNumber()).to.equal(1); // user1は1つ保持
Expand All @@ -105,7 +108,7 @@ describe('mint functions', function () {
expect(totalSupply.toNumber()).to.equal(1); // tokenId=1

const [traits1] = await provider.functions.generateTraits(0);
console.log('mint from minter',traits1);
console.log('mint from minter', traits1);

});

Expand Down Expand Up @@ -137,9 +140,9 @@ describe('mint functions', function () {
const [traits2] = await provider.functions.generateTraits(2);
const [traits3] = await provider.functions.generateTraits(3);
// head,accessoryがランダムなので県のみチェック(head,accessoryは目視)
console.log('multiple mint',traits1);
console.log('multiple mint',traits2);
console.log('multiple mint',traits3);
console.log('multiple mint', traits1);
console.log('multiple mint', traits2);
console.log('multiple mint', traits3);
expect(traits1.includes('{"trait_type": "prefecture" , "value":"Toyama"}')).to.equal(true);
expect(traits2.includes('{"trait_type": "prefecture" , "value":"Toyama"}')).to.equal(true);
expect(traits3.includes('{"trait_type": "prefecture" , "value":"Toyama"}')).to.equal(true);
Expand All @@ -150,6 +153,29 @@ describe('mint functions', function () {

});

it('owner mint', async function () {

const [balance3] = await token.functions.balanceOf(user3.address);
const [balance4] = await token.functions.balanceOf(user4.address);
const [balance5] = await token.functions.balanceOf(user5.address);
const [totalSupply] = await token.functions.totalSupply();

const txParams = { value: 0 };
await token.connect(owner).functions.ownerMint([user3.address, user4.address, user5.address], [3, 5, 0], [1, 2, 20], txParams);

const [balance3a] = await token.functions.balanceOf(user3.address);
const [balance4a] = await token.functions.balanceOf(user4.address);
const [balance5a] = await token.functions.balanceOf(user5.address);

expect(balance3a.toNumber()).to.equal(balance3.toNumber() + 1);
expect(balance4a.toNumber()).to.equal(balance4.toNumber() + 2);
expect(balance5a.toNumber()).to.equal(balance5.toNumber() + 20);

const [totalSupplya] = await token.functions.totalSupply();
expect(totalSupplya.toNumber()).to.equal(totalSupply.toNumber() + 23);

});

it('tokenGate', async function () {

await minter.connect(owner).functions.setPhase(1);
Expand Down Expand Up @@ -192,6 +218,28 @@ describe('mint functions', function () {
.revertedWith('Must send the mint price');

});

});

describe('determinePrefectureId', function () {
it('determinePrefectureId', async function () {

await provider.connect(owner).functions.setRandomValueForTest(true);
let prefectureCount = new Array(47).fill(0);

for (var i = 0; i < 300; i++) {
const [prefectureId] = await provider.connect(owner).functions.determinePrefectureId(i);
prefectureCount[prefectureId.toNumber() - 1]++;
}

// 全ての都道府県が1以上出現する
for (var i = 0; i < prefectureCount.length; i++) {
expect(prefectureCount[i]).to.greaterThan(0);
// console.log("prefectureId", i, prefectureCount[i]);
}

await provider.connect(owner).functions.setRandomValueForTest(false);
});
});

describe('P2P', function () {
Expand All @@ -214,20 +262,21 @@ describe('P2P', function () {

// mintMaxに現在の発行数+1をセット
const [totalSupply] = await token.functions.totalSupply();
await minter.functions.setMintMax(totalSupply.toNumber()+1);
await minter.functions.setMintMax(totalSupply.toNumber() + 1);
const [mintMax] = await minter.functions.mintMax();
expect(mintMax.toNumber()).to.equal(totalSupply.toNumber()+1);
expect(mintMax.toNumber()).to.equal(totalSupply.toNumber() + 1);

const txParams = { value: ethers.utils.parseUnits("0.006", "ether") };
await expect(minter.connect(user5).functions.mintSelectedPrefecture(47, 2, txParams))
.revertedWith('Over the mint limit');

// 一つだけならOK
const [balance] = await token.functions.balanceOf(user5.address);
const txParams2 = { value: ethers.utils.parseUnits("0.003", "ether") };
await minter.connect(user5).functions.mintSelectedPrefecture(47, 1,txParams2);
await minter.connect(user5).functions.mintSelectedPrefecture(47, 1, txParams2);

const [balance] = await token.functions.balanceOf(user5.address);
expect(balance.toNumber()).to.equal(1); // user1は1つ保持
const [balancea] = await token.functions.balanceOf(user5.address);
expect(balancea.toNumber()).to.equal(balance.toNumber() + 1); // user1は1つ保持

await minter.functions.setMintMax(1500);
const [mintMax2] = await minter.functions.mintMax();
Expand Down
Loading