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
20 changes: 18 additions & 2 deletions contract/contracts/LocalNounsToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
pragma solidity ^0.8.6;

import '@openzeppelin/contracts/utils/Strings.sol';

import './libs/ProviderTokenA2.sol';
import { INounsSeeder } from './localNouns/interfaces/INounsSeeder.sol';
import './localNouns/interfaces/IAssetProviderExMint.sol';
Expand Down Expand Up @@ -77,7 +78,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 +88,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,9 +186,10 @@ 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();

}
}
1 change: 1 addition & 0 deletions contract/contracts/external/nouns/NounsDescripter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ contract NounsDescriptor is INounsDescriptor, Ownable {
*/
function dataURI(uint256 tokenId, INounsSeeder.Seed memory seed) public view override returns (string memory) {
string memory nounId = tokenId.toString();

string memory name = string(abi.encodePacked('Aid pNoun ', nounId));
string memory description = string(abi.encodePacked('Aid pNoun ', nounId, ' is proof of contribution to pNouns DAO.'));

Expand Down
54 changes: 48 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] acumulationRatioRank = [5, 9, 12, 15, 17]; // ratioRankの積み上げ
uint256 acumulationRatioRankTotal = 17; // sum(ratioRank)
mapping(uint256 => uint256[]) public prefectureRatio;

constructor(
INounsDescriptor _descriptor,
Expand Down Expand Up @@ -94,6 +101,12 @@ 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];
}

function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
Expand Down Expand Up @@ -166,16 +179,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 % acumulationRatioRankTotal;
for (uint256 i = 0; i < acumulationRatioRank.length; i++) {
if (rank < acumulationRatioRank[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
2 changes: 2 additions & 0 deletions contract/contracts/localNouns/LocalNounsSeeder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ contract LocalNounsSeeder is ILocalNounsSeeder {
keccak256(abi.encodePacked(blockhash(block.number - 1), nounId))
);


uint256 accessoryCount = descriptor.accessoryCountInPrefecture(prefectureId % 100); // 1,2桁目:都道府県番号、3桁目以降:バージョン番号
uint256 headCount = descriptor.headCountInPrefecture(prefectureId);

uint256 accesoryPartId = descriptor.accessoryInPrefecture(prefectureId % 100, (pseudorandomness >> 96) % accessoryCount);

uint256 headPartId = descriptor.headInPrefecture(prefectureId, (pseudorandomness >> 144) % headCount);

return Seed({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ import 'assetprovider.sol/IAssetProvider.sol';
interface IAssetProviderExMint is IAssetProvider {
function mint(uint256 prefectureId, uint256 _assetId) external returns (uint256);
function getPrefectureId(uint256 prefectureId) external returns (uint256);

}
90 changes: 45 additions & 45 deletions contract/contracts/localNouns/interfaces/INounsDescriptor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,87 +21,87 @@ import { INounsSeeder } from './INounsSeeder.sol';
import { INounsDescriptorMinimal } from './INounsDescriptorMinimal.sol';

interface INounsDescriptor is INounsDescriptorMinimal {
event PartsLocked();
event PartsLocked();

event DataURIToggled(bool enabled);
event DataURIToggled(bool enabled);

event BaseURIUpdated(string baseURI);
event BaseURIUpdated(string baseURI);

function arePartsLocked() external returns (bool);
function arePartsLocked() external returns (bool);

function isDataURIEnabled() external returns (bool);
function isDataURIEnabled() external returns (bool);

function baseURI() external returns (string memory);
function baseURI() external returns (string memory);

function palettes(uint8 paletteIndex, uint256 colorIndex) external view returns (string memory);
function palettes(uint8 paletteIndex, uint256 colorIndex) external view returns (string memory);

function backgrounds(uint256 index) external view returns (string memory);
function backgrounds(uint256 index) external view returns (string memory);

function bodies(uint256 index) external view returns (bytes memory);
function bodies(uint256 index) external view returns (bytes memory);

function accessories(uint256 index) external view returns (bytes memory);
function accessories(uint256 index) external view returns (bytes memory);

function heads(uint256 index) external view returns (bytes memory);
function heads(uint256 index) external view returns (bytes memory);

function glasses(uint256 index) external view returns (bytes memory);
function glasses(uint256 index) external view returns (bytes memory);

function backgroundCount() external view override returns (uint256);
function backgroundCount() external view override returns (uint256);

function bodyCount() external view override returns (uint256);
function bodyCount() external view override returns (uint256);

function accessoryCount() external view override returns (uint256);
function accessoryCount() external view override returns (uint256);

function accessoryCountInPrefecture(uint256 prefectureId) external view returns (uint256);
function accessoryCountInPrefecture(uint256 prefectureId) external view returns (uint256);

function accessoryName(uint256 partsId) external view returns (string memory);

function headCount() external view override returns (uint256);
function accessoryName(uint256 partsId) external view returns (string memory);

function headCountInPrefecture(uint256 prefectureId) external view returns (uint256);
function headCount() external view override returns (uint256);

function headName(uint256 partsId) external view returns (string memory);
function headCountInPrefecture(uint256 prefectureId) external view returns (uint256);

function glassesCount() external view override returns (uint256);
function headName(uint256 partsId) external view returns (string memory);

function addManyColorsToPalette(uint8 paletteIndex, string[] calldata newColors) external;
function glassesCount() external view override returns (uint256);

function addManyBackgrounds(string[] calldata backgrounds) external;
function addManyColorsToPalette(uint8 paletteIndex, string[] calldata newColors) external;

function addManyBodies(bytes[] calldata bodies) external;
function addManyBackgrounds(string[] calldata backgrounds) external;

function addManyAccessories(uint256 prefectureId, bytes[] calldata accessories, string[] calldata name) external;
function addManyBodies(bytes[] calldata bodies) external;

function addManyHeads(uint256 prefectureId, bytes[] calldata heads, string[] calldata name) external;
function addManyAccessories(uint256 prefectureId, bytes[] calldata accessories, string[] calldata name) external;

function addManyGlasses(bytes[] calldata glasses) external;
function addManyHeads(uint256 prefectureId, bytes[] calldata heads, string[] calldata name) external;

function addColorToPalette(uint8 paletteIndex, string calldata color) external;
function addManyGlasses(bytes[] calldata glasses) external;

function addBackground(string calldata background) external;
function addColorToPalette(uint8 paletteIndex, string calldata color) external;

function addBody(bytes calldata body) external;
function addBackground(string calldata background) external;

function addAccessory(uint256 prefectureId, bytes calldata accessory, string calldata name) external;
function addBody(bytes calldata body) external;

function addHead(uint256 prefectureId, bytes calldata head, string calldata name) external;
function addAccessory(uint256 prefectureId, bytes calldata accessory, string calldata name) external;

function addGlasses(bytes calldata glasses) external;
function addHead(uint256 prefectureId, bytes calldata head, string calldata name) external;

function lockParts() external;
function addGlasses(bytes calldata glasses) external;

function toggleDataURIEnabled() external;
function lockParts() external;

function setBaseURI(string calldata baseURI) external;
function toggleDataURIEnabled() external;

function tokenURI(uint256 tokenId, INounsSeeder.Seed memory seed) external view override returns (string memory);
function setBaseURI(string calldata baseURI) external;

function dataURI(uint256 tokenId, INounsSeeder.Seed memory seed) external view override returns (string memory);
function tokenURI(uint256 tokenId, INounsSeeder.Seed memory seed) external view override returns (string memory);

function genericDataURI(
string calldata name,
string calldata description,
INounsSeeder.Seed memory seed
) external view returns (string memory);
function dataURI(uint256 tokenId, INounsSeeder.Seed memory seed) external view override returns (string memory);

function generateSVGImage(INounsSeeder.Seed memory seed) external view returns (string memory);
function genericDataURI(
string calldata name,
string calldata description,
INounsSeeder.Seed memory seed
) external view returns (string memory);

function generateSVGImage(INounsSeeder.Seed memory seed) external view returns (string memory);
}
3 changes: 1 addition & 2 deletions contract/scripts/deploy_localNouns.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ethers, network } from 'hardhat';
import { exec } from 'child_process';

import { writeFile } from 'fs';
import { addresses } from '../../src/utils/addresses';

Expand All @@ -25,7 +26,6 @@ async function main() {
const addresses = `export const addresses = {\n` + ` localseeder:"${localseeder.address}",\n` + `}\n`;
await writeFile(`../src/utils/addresses/localseeder_${network.name}.ts`, addresses, () => { });


const factoryLocalNounsDescriptor = await ethers.getContractFactory('LocalNounsDescriptor', {
libraries: {
NFTDescriptor: nftDescriptor,
Expand All @@ -41,7 +41,6 @@ async function main() {
const addresses2 = `export const addresses = {\n` + ` localNounsDescriptor:"${localNounsDescriptor.address}",\n` + `}\n`;
await writeFile(`../src/utils/addresses/localNounsDescriptor_${network.name}.ts`, addresses2, () => { });


const factorySVGStore = await ethers.getContractFactory('LocalNounsProvider');
const provider = await factorySVGStore.deploy(nounsDescriptor, localNounsDescriptor.address, nounsSeeder, localseeder.address);
await provider.deployed();
Expand Down
1 change: 1 addition & 0 deletions contract/scripts/deploy_nounsDescriptorV1.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ethers, network } from 'hardhat';
import { exec } from 'child_process';
import addresses from '@nouns/sdk/dist/contract/addresses.json';

import { writeFile } from 'fs';

// const nftDescriptor: string = network.name == 'goerli' ? addresses[5].nftDescriptor : addresses[1].nftDescriptor;
Expand Down
12 changes: 9 additions & 3 deletions contract/scripts/populate_localNouns.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as dotenv from "dotenv";
import { ethers, network } from 'hardhat';

import { addresses } from '../../src/utils/addresses';

const nounsDescriptor = addresses.nounsDescriptor[network.name];
Expand All @@ -13,7 +14,6 @@ import { abi as localMinterABI } from "../artifacts/contracts/localNouns/LocalNo

dotenv.config();


const localSeederAddress = addresses.localSeeder[network.name];
const localNounsDescriptorAddress = addresses.localNounsDescriptor[network.name];
const localProviderAddress = addresses.localProvider[network.name];
Expand All @@ -32,7 +32,7 @@ async function main() {
const localProvider = new ethers.Contract(localProviderAddress, localProviderABI, wallet);
const localToken = new ethers.Contract(localTokenAddress, localTokenABI, wallet);
const localMinter = new ethers.Contract(localMinterAddress, localMinterABI, wallet);

if (true) {
// set Palette
console.log(`set Palette start`);
Expand All @@ -43,8 +43,10 @@ async function main() {
console.log(`set Accessories start`);
const accessoryChunk = chunkArrayByPrefectureId(images.accessories);
for (const chunk of accessoryChunk) {

const prefectureId = chunk[0].prefectureId;
await localNounsDescriptor.addManyAccessories(prefectureId, chunk.map(({ data }) => data), chunk.map(({ filename }) => filename));

// console.log("chunk:", prefectureId, chunk);
}
console.log(`set Accessories end`);
Expand All @@ -53,8 +55,10 @@ async function main() {
console.log(`set Heads start`);
const headChunk = chunkArrayByPrefectureId(images.heads);
for (const chunk of headChunk) {

const prefectureId = chunk[0].prefectureId;
await localNounsDescriptor.addManyHeads(prefectureId, chunk.map(({ data }) => data), chunk.map(({ filename }) => filename));

// console.log("chunk:", prefectureId, chunk);
}
console.log(`set Heads end`);
Expand Down Expand Up @@ -88,7 +92,8 @@ main().catch(error => {
});

interface ImageData {
prefectureId: string;
prefectureId: string;

filename: string;
data: string;
}
Expand Down Expand Up @@ -117,6 +122,7 @@ function chunkArrayByPrefectureId(imagedata: ImageData[]): ImageData[][] {
}
imagedata[i].filename = name;
console.log("imagedata[i].filename", imagedata[i].filename);

if (!map.has(id)) {
map.set(id, []);
}
Expand Down
Loading
Loading