diff --git a/packages/contracts/contracts/IMultiSigWalletFactory.sol b/packages/contracts/contracts/IMultiSigWalletFactory.sol index 9785eeb..283ee34 100644 --- a/packages/contracts/contracts/IMultiSigWalletFactory.sol +++ b/packages/contracts/contracts/IMultiSigWalletFactory.sol @@ -5,17 +5,32 @@ pragma solidity ^0.8.2; import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; interface IMultiSigWalletFactory is IERC165 { - function create(address[] memory _owners, uint256 _required) external returns (address); + struct WalletInfo { + address creator; + address wallet; + string name; + string description; + uint256 time; + } + function create( + string calldata _name, + string calldata _description, + address[] memory _owners, + uint256 _required + ) external returns (address); function getNumberOfWalletsForCreator(address _creator) external view returns (uint256); function getWalletsForCreator( address _creator, uint256 _from, uint256 _to - ) external view returns (address[] memory); + ) external view returns (WalletInfo[] memory); + function getWalletInfo(address _wallet) external view returns (WalletInfo memory); + function changeName(address _wallet, string calldata _name) external; + function changeDescription(address _wallet, string calldata _description) external; function addOwner(address _owner, address _wallet) external; function removeOwner(address _owner, address _wallet) external; function getNumberOfWalletsForOwner(address _owner) external view returns (uint256); - function getWalletsForOwner(address _owner, uint256 _from, uint256 _to) external view returns (address[] memory); + function getWalletsForOwner(address _owner, uint256 _from, uint256 _to) external view returns (WalletInfo[] memory); } diff --git a/packages/contracts/contracts/MultiSigWallet.sol b/packages/contracts/contracts/MultiSigWallet.sol index 6d2c5d3..786b6b1 100644 --- a/packages/contracts/contracts/MultiSigWallet.sol +++ b/packages/contracts/contracts/MultiSigWallet.sol @@ -233,7 +233,7 @@ contract MultiSigWallet is ERC165, IMultiSigWallet { // call has been separated into its own function in order to take advantage // of the Solidity's code generator to produce a loop that copies tx.data into memory. function external_call(address _destination, uint256 _value, bytes memory _data) internal returns (bool) { - (bool success, bytes memory data) = _destination.call{ value: _value }(_data); + (bool success, ) = _destination.call{ value: _value }(_data); return success; } diff --git a/packages/contracts/contracts/MultiSigWalletFactory.sol b/packages/contracts/contracts/MultiSigWalletFactory.sol index 21cb418..9ba8c1f 100644 --- a/packages/contracts/contracts/MultiSigWalletFactory.sol +++ b/packages/contracts/contracts/MultiSigWalletFactory.sol @@ -20,20 +20,36 @@ contract MultiSigWalletFactory is ERC165, IMultiSigWalletFactory { */ mapping(address => bool) internal hasWallets; mapping(address => address[]) internal wallets; + mapping(address => WalletInfo) internal walletInfos; /* * Public functions */ /// @dev Allows verified creation of multi-signature wallet. + /// @param _name List of initial owners. + /// @param _description List of initial owners. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. /// @return wallet address. - function create(address[] memory _owners, uint256 _required) external override returns (address) { + function create( + string calldata _name, + string calldata _description, + address[] memory _owners, + uint256 _required + ) external override returns (address) { address wallet = address(new MultiSigWallet(address(this), _owners, _required)); for (uint256 idx = 0; idx < _owners.length; idx++) { _addOwner(_owners[idx], wallet); } register(wallet); + WalletInfo memory walletInfo = WalletInfo({ + creator: msg.sender, + wallet: wallet, + name: _name, + description: _description, + time: block.timestamp + }); + walletInfos[wallet] = walletInfo; return wallet; } @@ -59,14 +75,34 @@ contract MultiSigWalletFactory is ERC165, IMultiSigWalletFactory { address _creator, uint256 _from, uint256 _to - ) external view override returns (address[] memory) { - address[] memory values = new address[](_to - _from); + ) external view override returns (WalletInfo[] memory) { + WalletInfo[] memory values = new WalletInfo[](_to - _from); for (uint256 i = _from; i < _to; i++) { - values[i - _from] = wallets[_creator][i]; + address wallet = wallets[_creator][i]; + values[i - _from] = walletInfos[wallet]; } return values; } + /// @dev Returns information of the wallet + /// @param _wallet Address of wallet + /// @return information of wallet + function getWalletInfo(address _wallet) external view override returns (WalletInfo memory) { + return walletInfos[_wallet]; + } + + function changeName(address _wallet, string calldata _name) external override { + require(walletInfos[_wallet].creator == msg.sender, "Sender is not authorized to execute"); + + walletInfos[_wallet].name = _name; + } + + function changeDescription(address _wallet, string calldata _description) external override { + require(walletInfos[_wallet].creator == msg.sender, "Sender is not authorized to execute"); + + walletInfos[_wallet].description = _description; + } + mapping(address => address[]) internal walletsForOwnerValues; mapping(address => mapping(address => uint256)) internal walletsForOwnerIndexes; @@ -98,10 +134,11 @@ contract MultiSigWalletFactory is ERC165, IMultiSigWalletFactory { address _owner, uint256 _from, uint256 _to - ) external view override returns (address[] memory) { - address[] memory values = new address[](_to - _from); + ) external view override returns (WalletInfo[] memory) { + WalletInfo[] memory values = new WalletInfo[](_to - _from); for (uint256 i = _from; i < _to; i++) { - values[i - _from] = walletsForOwnerValues[_owner][i]; + address wallet = walletsForOwnerValues[_owner][i]; + values[i - _from] = walletInfos[wallet]; } return values; } diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0e22d8f..fae6499 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -14,7 +14,11 @@ "lint": "tslint --config tslint.json \\\"{src,test}/**/*.ts\\\"", "lint-fix": "tslint --fix --config tslint.json \\\"{src,test}/**/*.ts\\\"", "formatting:check": "prettier '**/*.{json,sol,ts,js,md}' -c", - "formatting:write": "prettier '**/*.{json,sol,ts,js,md}' --write" + "formatting:write": "prettier '**/*.{json,sol,ts,js,md}' --write", + "test:ExecutionAfterRequirementsChanged": "hardhat test test/ExecutionAfterRequirementsChanged.test.ts", + "test:ExternalCalls": "hardhat test test/ExternalCalls.test.ts", + "test:Factory": "hardhat test test/Factory.test.ts", + "test:MultiSigToken": "hardhat test test/MultiSigToken.test.ts" }, "repository": { "type": "git", diff --git a/packages/contracts/test/Factory.test.ts b/packages/contracts/test/Factory.test.ts index 3f4d151..8ae8823 100644 --- a/packages/contracts/test/Factory.test.ts +++ b/packages/contracts/test/Factory.test.ts @@ -20,14 +20,16 @@ async function deployMultiSigWalletFactory(deployer: Wallet): Promise { const contractFactory = await ethers.getContractFactory("MultiSigWalletFactory"); - const factoryContract = contractFactory.attach(factoryAddress); + const factoryContract = contractFactory.attach(factoryAddress) as MultiSigWalletFactory; const address = await ContractUtils.getEventValueString( - await factoryContract.connect(deployer).create(owners, required), + await factoryContract.connect(deployer).create(name, description, owners, required), factoryContract.interface, "ContractInstantiation", "wallet" @@ -39,7 +41,6 @@ async function deployMultiSigWallet( } describe("Test for MultiSigWalletFactory", () => { - const provider = ethers.provider; const raws = HardhatAccount.keys.map((m) => new Wallet(m, ethers.provider)); const [deployer, account0, account1, account2, account3, account4, account5, account6, account7] = raws; const owners1 = [account0, account1, account2]; @@ -52,6 +53,25 @@ describe("Test for MultiSigWalletFactory", () => { let multiSigWallet3: MultiSigWallet | undefined; const requiredConfirmations = 2; + const walletInfos = [ + { + name: "My Wallet 1", + description: "My first multi-sign wallet", + }, + { + name: "My Wallet 2", + description: "My second multi-sign wallet", + }, + { + name: "My Wallet 3", + description: "My third multi-sign wallet", + }, + { + name: "Fund", + description: "Fund of develop", + }, + ]; + before(async () => { multiSigFactory = await deployMultiSigWalletFactory(deployer); assert.ok(multiSigFactory); @@ -61,6 +81,8 @@ describe("Test for MultiSigWalletFactory", () => { multiSigWallet1 = await deployMultiSigWallet( multiSigFactory.address, deployer, + walletInfos[0].name, + walletInfos[0].description, owners1.map((m) => m.address), requiredConfirmations ); @@ -72,6 +94,8 @@ describe("Test for MultiSigWalletFactory", () => { multiSigWallet2 = await deployMultiSigWallet( multiSigFactory.address, deployer, + walletInfos[1].name, + walletInfos[1].description, owners2.map((m) => m.address), requiredConfirmations ); @@ -83,6 +107,8 @@ describe("Test for MultiSigWalletFactory", () => { multiSigWallet3 = await deployMultiSigWallet( multiSigFactory.address, deployer, + walletInfos[2].name, + walletInfos[2].description, owners3.map((m) => m.address), requiredConfirmations ); @@ -90,10 +116,19 @@ describe("Test for MultiSigWalletFactory", () => { assert.deepStrictEqual(await multiSigFactory.getNumberOfWalletsForOwner(account0.address), BigNumber.from(2)); assert.deepStrictEqual(await multiSigFactory.getNumberOfWalletsForOwner(account3.address), BigNumber.from(2)); - assert.deepStrictEqual(await multiSigFactory.getWalletsForOwner(account0.address, 0, 2), [ - multiSigWallet1.address, - multiSigWallet3.address, - ]); + const res = await multiSigFactory.getWalletsForOwner(account0.address, 0, 2); + assert.deepStrictEqual( + res.map((m) => m.wallet), + [multiSigWallet1.address, multiSigWallet3.address] + ); + assert.deepStrictEqual( + res.map((m) => m.name), + [walletInfos[0].name, walletInfos[2].name] + ); + assert.deepStrictEqual( + res.map((m) => m.description), + [walletInfos[0].description, walletInfos[2].description] + ); }); it("Remove owner", async () => { @@ -151,9 +186,19 @@ describe("Test for MultiSigWalletFactory", () => { account6.address, ]); - assert.deepStrictEqual(await multiSigFactory.getWalletsForOwner(account6.address, 0, 1), [ - multiSigWallet1.address, - ]); + const res = await multiSigFactory.getWalletsForOwner(account6.address, 0, 1); + assert.deepStrictEqual( + res.map((m) => m.wallet), + [multiSigWallet1.address] + ); + assert.deepStrictEqual( + res.map((m) => m.name), + [walletInfos[0].name] + ); + assert.deepStrictEqual( + res.map((m) => m.description), + [walletInfos[0].description] + ); }); it("Replace owner", async () => { @@ -188,8 +233,47 @@ describe("Test for MultiSigWalletFactory", () => { account6.address, ]); - assert.deepStrictEqual(await multiSigFactory.getWalletsForOwner(account7.address, 0, 1), [ - multiSigWallet1.address, - ]); + const res = await multiSigFactory.getWalletsForOwner(account7.address, 0, 1); + assert.deepStrictEqual( + res.map((m) => m.wallet), + [multiSigWallet1.address] + ); + assert.deepStrictEqual( + res.map((m) => m.name), + [walletInfos[0].name] + ); + assert.deepStrictEqual( + res.map((m) => m.description), + [walletInfos[0].description] + ); + }); + + it("getWalletInfo", async () => { + assert.ok(multiSigWallet1); + + const res = await multiSigFactory.getWalletInfo(multiSigWallet1.address); + assert.deepStrictEqual(res.wallet, multiSigWallet1.address); + assert.deepStrictEqual(res.name, walletInfos[0].name); + assert.deepStrictEqual(res.description, walletInfos[0].description); + }); + + it("changeName", async () => { + assert.ok(multiSigWallet1); + + await multiSigFactory.connect(deployer).changeName(multiSigWallet1.address, walletInfos[3].name); + const res = await multiSigFactory.getWalletInfo(multiSigWallet1.address); + assert.deepStrictEqual(res.wallet, multiSigWallet1.address); + assert.deepStrictEqual(res.name, walletInfos[3].name); + assert.deepStrictEqual(res.description, walletInfos[0].description); + }); + + it("changeDescription", async () => { + assert.ok(multiSigWallet1); + + await multiSigFactory.connect(deployer).changeDescription(multiSigWallet1.address, walletInfos[3].description); + const res = await multiSigFactory.getWalletInfo(multiSigWallet1.address); + assert.deepStrictEqual(res.wallet, multiSigWallet1.address); + assert.deepStrictEqual(res.name, walletInfos[3].name); + assert.deepStrictEqual(res.description, walletInfos[3].description); }); }); diff --git a/packages/contracts/test/MultiSigToken.test.ts b/packages/contracts/test/MultiSigToken.test.ts index 530264a..9206cc7 100644 --- a/packages/contracts/test/MultiSigToken.test.ts +++ b/packages/contracts/test/MultiSigToken.test.ts @@ -22,14 +22,16 @@ async function deployMultiSigWalletFactory(deployer: Wallet): Promise { const contractFactory = await ethers.getContractFactory("MultiSigWalletFactory"); - const factoryContract = contractFactory.attach(factoryAddress); + const factoryContract = contractFactory.attach(factoryAddress) as MultiSigWalletFactory; const address = await ContractUtils.getEventValueString( - await factoryContract.connect(deployer).create(owners, required), + await factoryContract.connect(deployer).create(name, description, owners, required), factoryContract.interface, "ContractInstantiation", "wallet" @@ -67,6 +69,8 @@ describe("Test for MultiSigWalletFactory", () => { multiSigWallet = await deployMultiSigWallet( multiSigFactory.address, deployer, + "My Wallet 1", + "My first multi-sign wallet", owners1.map((m) => m.address), requiredConfirmations );