From d26b66c270650fe0a521f8a7787885533f299698 Mon Sep 17 00:00:00 2001 From: Akira-Taniguchi Date: Thu, 11 Nov 2021 13:19:37 +0900 Subject: [PATCH 1/2] first --- .solhint.json | 2 +- contracts/Admin.sol | 2 +- contracts/Example.sol | 16 ---- contracts/PropertyGovernance.sol | 78 +++++++++++++++++ contracts/PropertyGovernanceFactory.sol | 35 ++++++++ contracts/UpgradeableProxy.sol | 2 +- contracts/UsingAddressRegistry.sol | 25 ++++++ hardhat.config.ts | 2 +- package.json | 13 +-- scripts/deploy-example.ts | 42 ++++----- scripts/utils.ts | 58 ++++++------- test/upgradeable-proxy.ts | 108 ++++++++++++------------ test/utils.ts | 3 - yarn.lock | 23 ++++- 14 files changed, 275 insertions(+), 134 deletions(-) delete mode 100644 contracts/Example.sol create mode 100644 contracts/PropertyGovernance.sol create mode 100644 contracts/PropertyGovernanceFactory.sol create mode 100644 contracts/UsingAddressRegistry.sol diff --git a/.solhint.json b/.solhint.json index 669f0a33..f8159e23 100644 --- a/.solhint.json +++ b/.solhint.json @@ -2,7 +2,7 @@ "extends": "solhint:recommended", "rules": { "no-empty-blocks": "off", - "compiler-version": ["error", "0.8.7"], + "compiler-version": ["error", "0.8.9"], "func-visibility": ["warn", { "ignoreConstructors": true }] } } diff --git a/contracts/Admin.sol b/contracts/Admin.sol index 74c290c2..79127aaa 100644 --- a/contracts/Admin.sol +++ b/contracts/Admin.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -pragma solidity =0.8.7; +pragma solidity =0.8.9; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; diff --git a/contracts/Example.sol b/contracts/Example.sol deleted file mode 100644 index dd296c2f..00000000 --- a/contracts/Example.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -pragma solidity =0.8.7; - -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -contract Example is Initializable { - uint256 public value; - - function initialize(uint256 initialValue) external initializer { - value = initialValue; - } - - function add(uint256 v) external { - value = value + v; - } -} diff --git a/contracts/PropertyGovernance.sol b/contracts/PropertyGovernance.sol new file mode 100644 index 00000000..3f2c9799 --- /dev/null +++ b/contracts/PropertyGovernance.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity =0.8.9; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IProperty} from "@devprotocol/protocol-v2/contracts/interface/IProperty.sol"; + +contract PropertyGovernance { + address public property; + address public factory; + bool public isFinished = false; + //TokenShare[] public tokenShare; + + struct TokenShare { + address member; + uint256 percentage; + } + + constructor(address _property) { + property = _property; + factory = msg.sender; + } + + modifier onlyPropertyAuthor() { + require(IProperty(property).author() == msg.sender, "illegal access"); + _; + } + + modifier notFinished() { + require(isFinished == false, "already finished"); + _; + } + + function setShareInfo(TokenShare[] memory _share) + external + onlyPropertyAuthor + notFinished + returns (bool) + { + uint256 sum = 0; + for (uint256 i = 0; i < _share.length; i++) { + sum += _share[i].percentage; + } + require(sum == 100, "total percentage is not 100"); + // アドレス重複チェック + // シェア計算 + + // tokenShare.length = 0; + // for (uint256 i = 0; i < _share.length; i++) { + // tokenShare.push(TokenShare(_share[i].member, _share[i].percentage)); + // } + //tokenShare = _share; + return true; + } + + function share() public onlyPropertyAuthor notFinished returns (bool) { + // Withdraw.withdraw + // シェア計算の割合で分配 + return true; + } + + function finish() external onlyPropertyAuthor notFinished returns (bool) { + share(); + sendToken(property); + isFinished = true; + return true; + } + + function rescue(address _token) external onlyPropertyAuthor returns (bool) { + require(isFinished == true, "illegal access"); + return sendToken(_token); + } + + function sendToken(address _token) private returns (bool) { + IERC20 token = IERC20(_token); + uint256 balance = token.balanceOf(address(this)); + return token.transfer(IProperty(property).author(), balance); + } +} diff --git a/contracts/PropertyGovernanceFactory.sol b/contracts/PropertyGovernanceFactory.sol new file mode 100644 index 00000000..a816bd48 --- /dev/null +++ b/contracts/PropertyGovernanceFactory.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity =0.8.9; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IProperty} from "@devprotocol/protocol-v2/contracts/interface/IProperty.sol"; +import {IPropertyFactory} from "@devprotocol/protocol-v2/contracts/interface/IPropertyFactory.sol"; +import {PropertyGovernance} from "./PropertyGovernance.sol"; +import {UsingAddressRegistry} from "./UsingAddressRegistry.sol"; + +contract PropertyGovernanceFactory is UsingAddressRegistry { + mapping(address => address) public governanceMap; + + function initialize(address _addressRegistry) external initializer { + __UsingAddressRegistry_init(_addressRegistry); + } + + function create(address _property) external returns (address) { + require( + propertyFactory().isProperty(_property), + "not property address" + ); + IProperty property = IProperty(_property); + require(property.author() == msg.sender, "illegal access"); + PropertyGovernance governance = new PropertyGovernance(_property); + IERC20 erc20Token = IERC20(_property); + // 一旦authorが保持する全てのトークンを預ける + erc20Token.transferFrom( + msg.sender, + address(governance), + erc20Token.balanceOf(msg.sender) + ); + governanceMap[_property] = address(governance); + return address(governance); + } +} diff --git a/contracts/UpgradeableProxy.sol b/contracts/UpgradeableProxy.sol index b102681e..c4955d11 100644 --- a/contracts/UpgradeableProxy.sol +++ b/contracts/UpgradeableProxy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -pragma solidity =0.8.7; +pragma solidity =0.8.9; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/contracts/UsingAddressRegistry.sol b/contracts/UsingAddressRegistry.sol new file mode 100644 index 00000000..a0a6bf03 --- /dev/null +++ b/contracts/UsingAddressRegistry.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity =0.8.9; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IAddressRegistry} from "@devprotocol/protocol-v2/contracts/interface/IAddressRegistry.sol"; +import {IPropertyFactory} from "@devprotocol/protocol-v2/contracts/interface/IPropertyFactory.sol"; + +contract UsingAddressRegistry is Initializable { + address public addressRegistry; + + // solhint-disable-next-line func-name-mixedcase + function __UsingAddressRegistry_init(address _addressRegistry) + public + initializer + { + addressRegistry = _addressRegistry; + } + + function propertyFactory() internal view returns (IPropertyFactory) { + return + IPropertyFactory( + IAddressRegistry(addressRegistry).registries("PropertyFactory") + ); + } +} diff --git a/hardhat.config.ts b/hardhat.config.ts index 45db90d4..04c9f946 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -6,5 +6,5 @@ import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-waffle' module.exports = { - solidity: '0.8.7', + solidity: '0.8.9', } diff --git a/package.json b/package.json index 27138ef4..68fb7bd5 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "devDependencies": { "@nomiclabs/hardhat-ethers": "2.0.2", "@nomiclabs/hardhat-waffle": "2.0.1", - "@openzeppelin/contracts": "4.3.2", + "@typechain/ethers-v5": "^8.0.0", + "@typechain/hardhat": "^2.3.0", "@types/chai": "4.2.22", "@types/dotenv": "8.2.0", "@types/mocha": "9.0.0", @@ -44,13 +45,13 @@ "prettier": "2.4.1", "prettier-plugin-solidity": "1.0.0-beta.18", "ts-node": "10.4.0", - "typescript": "4.4.4" + "typescript": "4.4.4", + "solhint": "^3.3.2", + "typechain": "^6.0.0" }, "dependencies": { + "@devprotocol/protocol-v2": "^0.5.0", "@openzeppelin/contracts-upgradeable": "^4.3.2", - "@typechain/ethers-v5": "^8.0.0", - "@typechain/hardhat": "^2.3.0", - "solhint": "^3.3.2", - "typechain": "^6.0.0" + "@openzeppelin/contracts": "4.3.2" } } diff --git a/scripts/deploy-example.ts b/scripts/deploy-example.ts index 55013646..c5f0f65f 100644 --- a/scripts/deploy-example.ts +++ b/scripts/deploy-example.ts @@ -1,26 +1,26 @@ -import { ethers } from 'hardhat' -import { deployAdmin, deployProxy } from './utils' +// Import { ethers } from 'hardhat' +// import { deployAdmin, deployProxy } from './utils' -async function main() { - const Example = await ethers.getContractFactory('Example') - const example = await Example.deploy() +// async function main() { +// const Example = await ethers.getContractFactory('Example') +// const example = await Example.deploy() - const admin = await deployAdmin() +// const admin = await deployAdmin() - const upgradeableProxy = await deployProxy( - example.address, - admin.address, - ethers.utils.arrayify('0x') - ) +// const upgradeableProxy = await deployProxy( +// example.address, +// admin.address, +// ethers.utils.arrayify('0x') +// ) - console.log('Example address:', example.address) - console.log('Admin address:', admin.address) - console.log('UpgradeableProxy address:', upgradeableProxy.address) -} +// console.log('Example address:', example.address) +// console.log('Admin address:', admin.address) +// console.log('UpgradeableProxy address:', upgradeableProxy.address) +// } -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) +// main() +// .then(() => process.exit(0)) +// .catch((error) => { +// console.error(error) +// process.exit(1) +// }) diff --git a/scripts/utils.ts b/scripts/utils.ts index d0c61081..4f842426 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -1,31 +1,31 @@ -import { ethers } from 'hardhat' -import { - Admin, - Admin__factory, - UpgradeableProxy, - UpgradeableProxy__factory, -} from '../typechain' +// Import { ethers } from 'hardhat' +// import { +// Admin, +// Admin__factory, +// UpgradeableProxy, +// UpgradeableProxy__factory, +// } from '../typechain' -export const deployAdmin = async (): Promise => { - const adminFactory = (await ethers.getContractFactory( - 'Admin' - )) as Admin__factory - const admin = await adminFactory.deploy() - return admin.deployed() -} +// export const deployAdmin = async (): Promise => { +// const adminFactory = (await ethers.getContractFactory( +// 'Admin' +// )) as Admin__factory +// const admin = await adminFactory.deploy() +// return admin.deployed() +// } -export const deployProxy = async ( - impl: string, - admin: string, - data: Readonly -): Promise => { - const upgradeableProxyFactory = (await ethers.getContractFactory( - 'UpgradeableProxy' - )) as UpgradeableProxy__factory - const upgradeableProxy = await upgradeableProxyFactory.deploy( - impl, - admin, - data - ) - return upgradeableProxy.deployed() -} +// export const deployProxy = async ( +// impl: string, +// admin: string, +// data: Readonly +// ): Promise => { +// const upgradeableProxyFactory = (await ethers.getContractFactory( +// 'UpgradeableProxy' +// )) as UpgradeableProxy__factory +// const upgradeableProxy = await upgradeableProxyFactory.deploy( +// impl, +// admin, +// data +// ) +// return upgradeableProxy.deployed() +// } diff --git a/test/upgradeable-proxy.ts b/test/upgradeable-proxy.ts index 5426a308..cd7fca04 100644 --- a/test/upgradeable-proxy.ts +++ b/test/upgradeable-proxy.ts @@ -1,59 +1,59 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { expect, use } from 'chai' -import { ethers } from 'hardhat' -import { solidity } from 'ethereum-waffle' -import { deploy, deployProxy } from './utils' -import { Example, UpgradeableProxy } from '../typechain' +// /* eslint-disable @typescript-eslint/no-unsafe-member-access */ +// import { expect, use } from 'chai' +// import { ethers } from 'hardhat' +// import { solidity } from 'ethereum-waffle' +// import { deploy, deployProxy } from './utils' +// import { Example, UpgradeableProxy } from '../typechain' -use(solidity) +// use(solidity) -describe('UpgradeableProxy', () => { - const init = async (): Promise<[UpgradeableProxy, Example, Example]> => { - const data = ethers.utils.arrayify('0x') - const example = await deploy('Example') - const [owner, addr1] = await ethers.getSigners() - const proxy = await deployProxy(example.address, owner.address, data) - const proxified = example.attach(proxy.address).connect(addr1) - await proxified.initialize(3) +// describe('UpgradeableProxy', () => { +// const init = async (): Promise<[UpgradeableProxy, Example, Example]> => { +// const data = ethers.utils.arrayify('0x') +// const example = await deploy('Example') +// const [owner, addr1] = await ethers.getSigners() +// const proxy = await deployProxy(example.address, owner.address, data) +// const proxified = example.attach(proxy.address).connect(addr1) +// await proxified.initialize(3) - return [proxy, example, proxified] - } +// return [proxy, example, proxified] +// } - describe('upgradeTo', () => { - describe('success', () => { - it('upgrade logic contract', async () => { - const [proxy, impl] = await init() - const impl1 = await proxy.callStatic.implementation() - const nextImpl = await deploy('Example') - await proxy.upgradeTo(nextImpl.address) - const impl2 = await proxy.callStatic.implementation() - expect(impl1).to.not.equal(impl2) - expect(impl1).to.equal(impl.address) - expect(impl2).to.equal(nextImpl.address) - }) +// describe('upgradeTo', () => { +// describe('success', () => { +// it('upgrade logic contract', async () => { +// const [proxy, impl] = await init() +// const impl1 = await proxy.callStatic.implementation() +// const nextImpl = await deploy('Example') +// await proxy.upgradeTo(nextImpl.address) +// const impl2 = await proxy.callStatic.implementation() +// expect(impl1).to.not.equal(impl2) +// expect(impl1).to.equal(impl.address) +// expect(impl2).to.equal(nextImpl.address) +// }) - it('storing data', async () => { - const [proxy, , proxified] = await init() - await proxified.add(2) - const nextImpl = await deploy('Example') - await proxy.upgradeTo(nextImpl.address) - const value = await proxified.value() - expect(value).to.equal(3 + 2) - }) - }) - describe('fail', () => { - it('should fail to upgrade when the caller is not admin', async () => { - const [proxy, impl] = await init() - const nextImpl = await deploy('Example') - const [, addr1] = await ethers.getSigners() - const res = await proxy - .connect(addr1) - .upgradeTo(nextImpl.address) - .catch((err: Error) => err) - const impl1 = await proxy.callStatic.implementation() - expect(res).to.be.instanceOf(Error) - expect(impl1).to.be.equal(impl.address) - }) - }) - }) -}) +// it('storing data', async () => { +// const [proxy, , proxified] = await init() +// await proxified.add(2) +// const nextImpl = await deploy('Example') +// await proxy.upgradeTo(nextImpl.address) +// const value = await proxified.value() +// expect(value).to.equal(3 + 2) +// }) +// }) +// describe('fail', () => { +// it('should fail to upgrade when the caller is not admin', async () => { +// const [proxy, impl] = await init() +// const nextImpl = await deploy('Example') +// const [, addr1] = await ethers.getSigners() +// const res = await proxy +// .connect(addr1) +// .upgradeTo(nextImpl.address) +// .catch((err: Error) => err) +// const impl1 = await proxy.callStatic.implementation() +// expect(res).to.be.instanceOf(Error) +// expect(impl1).to.be.equal(impl.address) +// }) +// }) +// }) +// }) diff --git a/test/utils.ts b/test/utils.ts index 16154cb8..dc9326be 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { ethers } from 'hardhat' import { Contract, BigNumber } from 'ethers' import { UpgradeableProxy, UpgradeableProxy__factory } from '../typechain' diff --git a/yarn.lock b/yarn.lock index 7343e215..5b7862d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,6 +35,22 @@ dependencies: "@cspotcode/source-map-consumer" "0.8.0" +"@devprotocol/protocol-v2@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@devprotocol/protocol-v2/-/protocol-v2-0.5.0.tgz#5174ebb31fa13ffcd22d2fe39ebada56b3c4d858" + integrity sha512-HMdrQGGxhQjuht89V9pFFN0pcUazZG+VMUzQEO/IhyF39ojOKd4ij1yNQBGhua+Q4q86SMFKQ5+ansSMdBCaNA== + dependencies: + "@devprotocol/util-contracts" "3.3.0" + "@openzeppelin/contracts" "4.3.2" + "@openzeppelin/contracts-upgradeable" "4.3.2" + +"@devprotocol/util-contracts@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@devprotocol/util-contracts/-/util-contracts-3.3.0.tgz#56978e832f12b1796d937b20e57be307f2fcbc07" + integrity sha512-5KgaYUcp7tbPIjSzoxE/40pMYqfFdqHsMraKy+ATDWVEy1D7gfdQEvOyx2nMwE/a5pjSO+UDgZP1hclEbnyTRQ== + dependencies: + "@openzeppelin/contracts" "4.3.0" + "@ensdomains/ens@^0.4.4": version "0.4.5" resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.5.tgz#e0aebc005afdc066447c6e22feb4eda89a5edbfc" @@ -931,11 +947,16 @@ "@types/sinon-chai" "^3.2.3" "@types/web3" "1.0.19" -"@openzeppelin/contracts-upgradeable@^4.3.2": +"@openzeppelin/contracts-upgradeable@4.3.2", "@openzeppelin/contracts-upgradeable@^4.3.2": version "4.3.2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.3.2.tgz#92df481362e366c388fc02133cf793029c744cea" integrity sha512-i/pOaOtcqDk4UqsrOv735uYyTbn6dvfiuVu5hstsgV6c4ZKUtu88/31zT2BzkCg+3JfcwOfgg2TtRKVKKZIGkQ== +"@openzeppelin/contracts@4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.0.tgz#345236d4ec73ef381ab4907c6ef66fd55e5dedad" + integrity sha512-+uBDl/TrmR0Kch6mq3tuxMex/fK7huR6+fQMae+zJk1K5T+dp0pFl12Hbc+1L6oYMXoyDSBJ8zqhRIntrREDFA== + "@openzeppelin/contracts@4.3.2": version "4.3.2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.2.tgz#ff80affd6d352dbe1bbc5b4e1833c41afd6283b6" From eb2090afa90f686d2f9235fe943a1f07199e9633 Mon Sep 17 00:00:00 2001 From: Akira-Taniguchi Date: Thu, 11 Nov 2021 16:21:50 +0900 Subject: [PATCH 2/2] finish --- contracts/IPropertyGovernance.sol | 16 +++ contracts/IPropertyGovernanceFactory.sol | 13 ++ contracts/PropertyGovernance.sol | 132 +++++++++++++++---- contracts/PropertyGovernanceFactory.sol | 35 +++-- contracts/UsingAddressRegistry.sol | 21 ++- contracts/UsingAddressRegistryUpgradable.sol | 22 ++++ 6 files changed, 184 insertions(+), 55 deletions(-) create mode 100644 contracts/IPropertyGovernance.sol create mode 100644 contracts/IPropertyGovernanceFactory.sol create mode 100644 contracts/UsingAddressRegistryUpgradable.sol diff --git a/contracts/IPropertyGovernance.sol b/contracts/IPropertyGovernance.sol new file mode 100644 index 00000000..f7671c40 --- /dev/null +++ b/contracts/IPropertyGovernance.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity =0.8.9; + +interface IPropertyGovernance { + struct TokenAllocate { + address member; + uint256 percentage; + } + event SetTokenAllocate(TokenAllocate[] tokenAllocateInfo); + + function setAllocateInfo(TokenAllocate[] memory _share) external; + + function finish() external returns (bool); + + function rescue(address _token) external returns (bool); +} diff --git a/contracts/IPropertyGovernanceFactory.sol b/contracts/IPropertyGovernanceFactory.sol new file mode 100644 index 00000000..8d39e921 --- /dev/null +++ b/contracts/IPropertyGovernanceFactory.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity =0.8.9; + +interface IPropertyGovernanceFactory { + event Created( + address indexed property, + address author, + address governance, + uint256 tokenAmount + ); + + function create(address _property) external returns (address); +} diff --git a/contracts/PropertyGovernance.sol b/contracts/PropertyGovernance.sol index 3f2c9799..9c838e63 100644 --- a/contracts/PropertyGovernance.sol +++ b/contracts/PropertyGovernance.sol @@ -1,23 +1,32 @@ // SPDX-License-Identifier: MPL-2.0 pragma solidity =0.8.9; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IProperty} from "@devprotocol/protocol-v2/contracts/interface/IProperty.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import "@devprotocol/protocol-v2/contracts/interface/IProperty.sol"; +import "@devprotocol/protocol-v2/contracts/interface/IWithdraw.sol"; +import "./UsingAddressRegistry.sol"; +import "./IPropertyGovernance.sol"; -contract PropertyGovernance { +contract PropertyGovernance is UsingAddressRegistry, IPropertyGovernance { address public property; address public factory; bool public isFinished = false; - //TokenShare[] public tokenShare; + uint256 public alreadyAllocateReward; + mapping(address => uint256) public percentage; + mapping(address => uint256) public rewardMap; + EnumerableSet.AddressSet private members; - struct TokenShare { - address member; - uint256 percentage; - } + using EnumerableSet for EnumerableSet.AddressSet; - constructor(address _property) { + constructor(address _addressRegistry, address _property) + UsingAddressRegistry(_addressRegistry) + { property = _property; factory = msg.sender; + address author = IProperty(property).author(); + members.add(author); + percentage[author] = 100; } modifier onlyPropertyAuthor() { @@ -30,37 +39,52 @@ contract PropertyGovernance { _; } - function setShareInfo(TokenShare[] memory _share) + function setAllocateInfo(TokenAllocate[] memory _allocate) external onlyPropertyAuthor notFinished - returns (bool) { - uint256 sum = 0; - for (uint256 i = 0; i < _share.length; i++) { - sum += _share[i].percentage; + validateTokenAllocate(_allocate); + addReward(); + for (uint256 i = 0; i < _allocate.length; i++) { + address member = _allocate[i].member; + if (!members.contains(member)) { + members.add(member); + } + percentage[member] = _allocate[i].percentage; } - require(sum == 100, "total percentage is not 100"); - // アドレス重複チェック - // シェア計算 - - // tokenShare.length = 0; - // for (uint256 i = 0; i < _share.length; i++) { - // tokenShare.push(TokenShare(_share[i].member, _share[i].percentage)); - // } - //tokenShare = _share; - return true; + for (uint256 i = 0; i < members.length(); i++) { + address member = members.at(i); + if (!isAllocateMember(_allocate, member)) { + percentage[member] = 0; + } + } + emit SetTokenAllocate(_allocate); } - function share() public onlyPropertyAuthor notFinished returns (bool) { - // Withdraw.withdraw - // シェア計算の割合で分配 + function allocate() public onlyPropertyAuthor notFinished returns (bool) { + addReward(); + IWithdraw(withdrawAddress()).withdraw(property); + IERC20 dev = IERC20(devAddress()); + uint256 transferedReward = 0; + for (uint256 i = 0; i < members.length(); i++) { + address member = members.at(i); + uint256 reward = rewardMap[member]; + if (reward == 0) { + continue; + } + rewardMap[member] = 0; + require(dev.transfer(member, reward), "failed to transfer"); + transferedReward += reward; + } return true; } function finish() external onlyPropertyAuthor notFinished returns (bool) { - share(); - sendToken(property); + require(allocate(), "failed to allocate"); + require(sendToken(property), "failed to send property token"); + // just in case + require(sendToken(devAddress()), "failed to send dev token"); isFinished = true; return true; } @@ -70,9 +94,59 @@ contract PropertyGovernance { return sendToken(_token); } + function addReward() private { + (uint256 currentReward, , , ) = IWithdraw(withdrawAddress()) + .calculateRewardAmount(property, address(this)); + uint256 reward = currentReward - alreadyAllocateReward; + for (uint256 i = 0; i < members.length(); i++) { + address member = members.at(i); + uint256 individualReward = (reward * percentage[member]) / 100; + rewardMap[member] += individualReward; + } + alreadyAllocateReward = currentReward; + } + + function isAllocateMember(TokenAllocate[] memory _allocate, address _member) + private + pure + returns (bool) + { + for (uint256 i = 0; i < _allocate.length; i++) { + address member = _allocate[i].member; + if (member == _member) { + return true; + } + } + return false; + } + + function validateTokenAllocate(TokenAllocate[] memory _allocate) + private + pure + { + uint256 sum = 0; + address[] memory alocateMembers = new address[](_allocate.length); + for (uint256 i = 0; i < _allocate.length; i++) { + alocateMembers[i] = _allocate[i].member; + sum += _allocate[i].percentage; + } + require(sum == 100, "total percentage is not 100"); + for (uint256 i = 0; i < _allocate.length; i++) { + for (uint256 k = i; k < _allocate.length; k++) { + require( + alocateMembers[k] == alocateMembers[i], + "total percentage is not 100" + ); + } + } + } + function sendToken(address _token) private returns (bool) { IERC20 token = IERC20(_token); uint256 balance = token.balanceOf(address(this)); + if (balance == 0) { + return true; + } return token.transfer(IProperty(property).author(), balance); } } diff --git a/contracts/PropertyGovernanceFactory.sol b/contracts/PropertyGovernanceFactory.sol index a816bd48..153eb55c 100644 --- a/contracts/PropertyGovernanceFactory.sol +++ b/contracts/PropertyGovernanceFactory.sol @@ -1,33 +1,42 @@ // SPDX-License-Identifier: MPL-2.0 pragma solidity =0.8.9; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IProperty} from "@devprotocol/protocol-v2/contracts/interface/IProperty.sol"; -import {IPropertyFactory} from "@devprotocol/protocol-v2/contracts/interface/IPropertyFactory.sol"; -import {PropertyGovernance} from "./PropertyGovernance.sol"; -import {UsingAddressRegistry} from "./UsingAddressRegistry.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@devprotocol/protocol-v2/contracts/interface/IProperty.sol"; +import "@devprotocol/protocol-v2/contracts/interface/IPropertyFactory.sol"; +import "./IPropertyGovernanceFactory.sol"; +import "./PropertyGovernance.sol"; +import "./UsingAddressRegistryUpgradable.sol"; -contract PropertyGovernanceFactory is UsingAddressRegistry { +contract PropertyGovernanceFactory is + UsingAddressRegistryUpgradable, + IPropertyGovernanceFactory +{ mapping(address => address) public governanceMap; function initialize(address _addressRegistry) external initializer { - __UsingAddressRegistry_init(_addressRegistry); + __UsingAddressRegistryUpgradable_init(_addressRegistry); } function create(address _property) external returns (address) { require( - propertyFactory().isProperty(_property), + IPropertyFactory(propertyFactoryAddress()).isProperty(_property), "not property address" ); IProperty property = IProperty(_property); require(property.author() == msg.sender, "illegal access"); - PropertyGovernance governance = new PropertyGovernance(_property); + PropertyGovernance governance = new PropertyGovernance( + addressRegistry, + _property + ); IERC20 erc20Token = IERC20(_property); - // 一旦authorが保持する全てのトークンを預ける - erc20Token.transferFrom( - msg.sender, + uint256 balance = erc20Token.balanceOf(msg.sender); + erc20Token.transferFrom(msg.sender, address(governance), balance); + emit Created( + _property, + property.author(), address(governance), - erc20Token.balanceOf(msg.sender) + balance ); governanceMap[_property] = address(governance); return address(governance); diff --git a/contracts/UsingAddressRegistry.sol b/contracts/UsingAddressRegistry.sol index a0a6bf03..93115f16 100644 --- a/contracts/UsingAddressRegistry.sol +++ b/contracts/UsingAddressRegistry.sol @@ -1,25 +1,20 @@ // SPDX-License-Identifier: MPL-2.0 pragma solidity =0.8.9; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {IAddressRegistry} from "@devprotocol/protocol-v2/contracts/interface/IAddressRegistry.sol"; -import {IPropertyFactory} from "@devprotocol/protocol-v2/contracts/interface/IPropertyFactory.sol"; -contract UsingAddressRegistry is Initializable { +contract UsingAddressRegistry { address public addressRegistry; - // solhint-disable-next-line func-name-mixedcase - function __UsingAddressRegistry_init(address _addressRegistry) - public - initializer - { + constructor(address _addressRegistry) { addressRegistry = _addressRegistry; } - function propertyFactory() internal view returns (IPropertyFactory) { - return - IPropertyFactory( - IAddressRegistry(addressRegistry).registries("PropertyFactory") - ); + function withdrawAddress() internal view returns (address) { + return IAddressRegistry(addressRegistry).registries("Withdraw"); + } + + function devAddress() internal view returns (address) { + return IAddressRegistry(addressRegistry).registries("Dev"); } } diff --git a/contracts/UsingAddressRegistryUpgradable.sol b/contracts/UsingAddressRegistryUpgradable.sol new file mode 100644 index 00000000..64ba7eed --- /dev/null +++ b/contracts/UsingAddressRegistryUpgradable.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity =0.8.9; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IAddressRegistry} from "@devprotocol/protocol-v2/contracts/interface/IAddressRegistry.sol"; +import {IWithdraw} from "@devprotocol/protocol-v2/contracts/interface/IWithdraw.sol"; + +contract UsingAddressRegistryUpgradable is Initializable { + address public addressRegistry; + + // solhint-disable-next-line func-name-mixedcase + function __UsingAddressRegistryUpgradable_init(address _addressRegistry) + public + initializer + { + addressRegistry = _addressRegistry; + } + + function propertyFactoryAddress() internal view returns (address) { + return IAddressRegistry(addressRegistry).registries("PropertyFactory"); + } +}