From 98e286a855a9d169586694e501c440800d87f7bb Mon Sep 17 00:00:00 2001 From: rockca Date: Tue, 21 Dec 2021 11:37:28 +0800 Subject: [PATCH 1/5] add Deposit --- contracts/Vault.sol | 84 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/contracts/Vault.sol b/contracts/Vault.sol index e001503..c8f0696 100644 --- a/contracts/Vault.sol +++ b/contracts/Vault.sol @@ -28,7 +28,11 @@ contract Vault { uint callerPayout ); event ChequeBounced(); - event Withdraw(uint amount); + event Withdraw(address indexed from, uint amount); + event Deposit(address indexed from, uint amount); + + event IncreaseStake(uint amount); + event DecreaseStake(address indexed recipient, uint amount); struct EIP712Domain { string name; @@ -36,6 +40,12 @@ contract Vault { uint256 chainId; } + /* structure to keep track of the stake records*/ + struct stake { + uint amount; /* total stake */ + uint canBeDecreasedAt; /* point in time after which stake can be decreased*/ + } + bytes32 public constant EIP712DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId)" ); @@ -89,6 +99,8 @@ contract Vault { address public issuer; /* indicates wether a cheque bounced in the past */ bool public bounced; + /* total amount staked*/ + stake public totalStake; /** @param _issuer the issuer of cheques from this Vault (needed as an argument for "Setting up a Vault as a payment"). @@ -107,6 +119,11 @@ contract Vault { return token.balanceOf(address(this)); } + /// @return the part of the balance that is not covered by totalStake + function liquidBalance() public view returns(uint) { + return totakbalance().sub(totalStake.amount); + } + /** @dev internal function responsible for checking the issuerSignature, updating hardDeposit balances and doing transfers. Called by cashCheque and cashChequeBeneficary @@ -166,7 +183,15 @@ contract Vault { /* ensure we don't take anything from the hard deposit */ require(amount <= totalbalance(), "totalbalance not sufficient"); require(token.transfer(issuer, amount), "transfer failed"); - emit Withdraw(amount); + emit Withdraw(issuer, amount); + } + + /* + * deposit wbtt to this + */ + function deposit(uint amount) public { + require(token.transferFrom(msg.sender, address(this), amount), "deposit failed"); + emit Deposit(msg.sender, amount); } function chequeHash(address vault, address beneficiary, uint cumulativePayout) @@ -178,4 +203,59 @@ contract Vault { cumulativePayout )); } + + + /** + @notice increase the stake + @param amount increased stake amount + */ + function increaseStake(uint amount) public { + require(msg.sender == issuer, "increaseStake: not issuer"); + /* ensure totalStake don't exceed the global balance */ + require(totalStake.amount.add(amount) <= totakbalance(), "stake exceeds balance"); + /* increase totalStake*/ + totalStake.amount = totalStake.amount.add(amount); + refreshStakeTime(); + + emit IncreaseStake(amount); + } + + function refreshStakeTime() private { + totalStake.canBeDecreasedAt = block.timestamp + 180 days; + } + + /** + @notice decrease the stake + @param amount decreased stake amount + */ + function decreaseStake(uint amount, address recipient) public { + require(msg.sender == issuer, "decreaseStake: not issuer"); + /* must reach lock-up time*/ + require(block.timestamp >= totalStake.canBeDecreasedAt && totalStake.canBeDecreasedAt != 0, "lock-up time (180 days) not yet been reached"); + /* must be a right value*/ + require(amount <= totalStake.amount && amount > 0, "invalid amount"); + + /* reset the canBeDecreasedAt */ + refreshStakeTime(); + + /* update totalStake.amount */ + totalStake.amount = totalStake.amount.sub(amount); + + // transfer amount to recipient + if (recipient != address(0)) { + require(token.transfer(recipient, amount), "decreaseStake: transfer failed"); + } + + emit DecreaseStake(recipient, amount); + } + + /* get total stake amount */ + function getTotalStake() public view returns(uint) { + return totalStake.amount; + } + + /* get lock-up time */ + function getTimeCanBeDecreased() public view returns(uint) { + return totalStake.canBeDecreasedAt; + } } \ No newline at end of file From 216ae1dd87221c3c812742537a496eabad2c42c3 Mon Sep 17 00:00:00 2001 From: rockca Date: Tue, 21 Dec 2021 16:08:05 +0800 Subject: [PATCH 2/5] refresh vault contract --- contracts/Vault.sol | 73 +-------------------------------------------- 1 file changed, 1 insertion(+), 72 deletions(-) diff --git a/contracts/Vault.sol b/contracts/Vault.sol index c8f0696..edb07b8 100644 --- a/contracts/Vault.sol +++ b/contracts/Vault.sol @@ -31,21 +31,12 @@ contract Vault { event Withdraw(address indexed from, uint amount); event Deposit(address indexed from, uint amount); - event IncreaseStake(uint amount); - event DecreaseStake(address indexed recipient, uint amount); - struct EIP712Domain { string name; string version; uint256 chainId; } - /* structure to keep track of the stake records*/ - struct stake { - uint amount; /* total stake */ - uint canBeDecreasedAt; /* point in time after which stake can be decreased*/ - } - bytes32 public constant EIP712DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId)" ); @@ -99,8 +90,6 @@ contract Vault { address public issuer; /* indicates wether a cheque bounced in the past */ bool public bounced; - /* total amount staked*/ - stake public totalStake; /** @param _issuer the issuer of cheques from this Vault (needed as an argument for "Setting up a Vault as a payment"). @@ -119,11 +108,6 @@ contract Vault { return token.balanceOf(address(this)); } - /// @return the part of the balance that is not covered by totalStake - function liquidBalance() public view returns(uint) { - return totakbalance().sub(totalStake.amount); - } - /** @dev internal function responsible for checking the issuerSignature, updating hardDeposit balances and doing transfers. Called by cashCheque and cashChequeBeneficary @@ -187,7 +171,7 @@ contract Vault { } /* - * deposit wbtt to this + * deposit wbtt to address(this), befrore it, must approve to address(this) */ function deposit(uint amount) public { require(token.transferFrom(msg.sender, address(this), amount), "deposit failed"); @@ -203,59 +187,4 @@ contract Vault { cumulativePayout )); } - - - /** - @notice increase the stake - @param amount increased stake amount - */ - function increaseStake(uint amount) public { - require(msg.sender == issuer, "increaseStake: not issuer"); - /* ensure totalStake don't exceed the global balance */ - require(totalStake.amount.add(amount) <= totakbalance(), "stake exceeds balance"); - /* increase totalStake*/ - totalStake.amount = totalStake.amount.add(amount); - refreshStakeTime(); - - emit IncreaseStake(amount); - } - - function refreshStakeTime() private { - totalStake.canBeDecreasedAt = block.timestamp + 180 days; - } - - /** - @notice decrease the stake - @param amount decreased stake amount - */ - function decreaseStake(uint amount, address recipient) public { - require(msg.sender == issuer, "decreaseStake: not issuer"); - /* must reach lock-up time*/ - require(block.timestamp >= totalStake.canBeDecreasedAt && totalStake.canBeDecreasedAt != 0, "lock-up time (180 days) not yet been reached"); - /* must be a right value*/ - require(amount <= totalStake.amount && amount > 0, "invalid amount"); - - /* reset the canBeDecreasedAt */ - refreshStakeTime(); - - /* update totalStake.amount */ - totalStake.amount = totalStake.amount.sub(amount); - - // transfer amount to recipient - if (recipient != address(0)) { - require(token.transfer(recipient, amount), "decreaseStake: transfer failed"); - } - - emit DecreaseStake(recipient, amount); - } - - /* get total stake amount */ - function getTotalStake() public view returns(uint) { - return totalStake.amount; - } - - /* get lock-up time */ - function getTimeCanBeDecreased() public view returns(uint) { - return totalStake.canBeDecreasedAt; - } } \ No newline at end of file From c8549db8962ecd86e885648260d8020e6c226e5d Mon Sep 17 00:00:00 2001 From: rockca Date: Tue, 4 Jan 2022 15:20:48 +0800 Subject: [PATCH 3/5] fix:update the price interface --- contracts/PriceOracle.sol | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/contracts/PriceOracle.sol b/contracts/PriceOracle.sol index c0c0644..d5330ff 100644 --- a/contracts/PriceOracle.sol +++ b/contracts/PriceOracle.sol @@ -108,11 +108,19 @@ contract PriceOracle is Ownable { * @dev Emitted when the price is updated. */ event PriceUpdate(uint256 price); + /** + * @dev Emitted when the rate is updated. + */ + event ExchangeRateUpdate(uint256 rate); // current price in wei per GB/month uint256 public price; - constructor(uint256 _price) { + // current rate + uint256 public exchangeRate; + + constructor(uint256 _price, uint256 _exchangeRate) { price = _price; + exchangeRate = _exchangeRate; } /** @@ -122,6 +130,13 @@ contract PriceOracle is Ownable { return price; } + /** + * @notice Returns the rate of price + */ + function getExchangeRate() external view returns (uint256) { + return exchangeRate; + } + /** * @notice Update the price. Can only be called by the owner. * @param newPrice the new price @@ -130,4 +145,13 @@ contract PriceOracle is Ownable { price = newPrice; emit PriceUpdate(price); } + + /** + * @notice Update the rate. Can only be called by the owner. + * @param newRate the new rate + */ + function updateExchangeRate(uint256 newRate) external onlyOwner { + exchangeRate = newRate; + emit ExchangeRateUpdate(newRate); + } } \ No newline at end of file From 189cbb5c7b9ab66d19965e95451af17811919335 Mon Sep 17 00:00:00 2001 From: rockca Date: Thu, 6 Jan 2022 15:18:00 +0800 Subject: [PATCH 4/5] feat: add peerid param for deployVault function --- contracts/VaultFactory.sol | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/contracts/VaultFactory.sol b/contracts/VaultFactory.sol index 21e499f..dfe84a2 100644 --- a/contracts/VaultFactory.sol +++ b/contracts/VaultFactory.sol @@ -11,8 +11,13 @@ import "@openzeppelin/contracts/proxy/Clones.sol"; */ contract VaultFactory { - /* event fired on every new Vault deployment */ - event VaultDeployed(address issuer,address contractAddress); + /** + @notice event fired after new Vault deployed + @param issuer the issuer of the new vault contract + @param contractAddress the address of the new deployed contract + @param id the peerID of the btfs node + */ + event VaultDeployed(address issuer,address contractAddress,string id); /* mapping to keep track of which contracts were deployed by this factory */ mapping (address => bool) public deployedContracts; @@ -33,13 +38,14 @@ contract VaultFactory { @notice creates a clone of the master Vault contract @param issuer the issuer of cheques for the new vault @param salt salt to include in create2 to enable the same address to deploy multiple Vaults + @param id the peerID of the btfs node */ - function deployVault(address issuer, bytes32 salt) + function deployVault(address issuer, bytes32 salt, string memory id) public returns (address) { address contractAddress = Clones.cloneDeterministic(master, keccak256(abi.encode(msg.sender, salt))); Vault(contractAddress).init(issuer, TokenAddress); deployedContracts[contractAddress] = true; - emit VaultDeployed(issuer,contractAddress); + emit VaultDeployed(issuer,contractAddress,id); return contractAddress; } } \ No newline at end of file From f8eb4878115eb59bf7af64a7bbee15f8be3ca732 Mon Sep 17 00:00:00 2001 From: "robin.luo" Date: Thu, 13 Jan 2022 16:16:34 +0800 Subject: [PATCH 5/5] feat: add peerVaultAddress mapping --- contracts/VaultFactory.sol | 3 +++ package.json | 2 +- test/Vault.should.js | 3 ++- test/VaultFactory.test.js | 30 ++++++++++++++++++++++-------- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/contracts/VaultFactory.sol b/contracts/VaultFactory.sol index dfe84a2..ff7f6d7 100644 --- a/contracts/VaultFactory.sol +++ b/contracts/VaultFactory.sol @@ -21,6 +21,8 @@ contract VaultFactory { /* mapping to keep track of which contracts were deployed by this factory */ mapping (address => bool) public deployedContracts; + /* mapping between btfs node's peerID and its vault address */ + mapping (string => address) public peerVaultAddress; /* address of the TRC20-token, to be used by the to-be-deployed vaults */ address public TokenAddress; @@ -45,6 +47,7 @@ contract VaultFactory { address contractAddress = Clones.cloneDeterministic(master, keccak256(abi.encode(msg.sender, salt))); Vault(contractAddress).init(issuer, TokenAddress); deployedContracts[contractAddress] = true; + peerVaultAddress[id] = contractAddress; emit VaultDeployed(issuer,contractAddress,id); return contractAddress; } diff --git a/package.json b/package.json index fddf0bb..aa15e87 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "scripts": { "compile": "hardhat compile", - "test": "hardhat test", + "test": "hardhat test --show-stack-traces", "abigen": "./abigen/gen.sh Vault.sol VaultFactory ", "lint": "solhint contracts/Vault.sol contracts/VaultFactory.sol", "coverage": "hardhat coverage --solcoverjs ./.solcover.js" diff --git a/test/Vault.should.js b/test/Vault.should.js index 1d10d5e..190d4c4 100644 --- a/test/Vault.should.js +++ b/test/Vault.should.js @@ -14,13 +14,14 @@ const { expect } = require('chai'); function shouldDeploy(issuer, value) { + const adminPeerID = "0000000000000000000000000000000000000000000000000000a" const salt = "0x000000000000000000000000000000000000000000000000000000000000abcd" beforeEach(async function() { this.TestToken = await TestToken.new({from: issuer}) await this.TestToken.mint(issuer, 1000000000, {from: issuer}); this.vaultFactory = await VaultFactory.new(this.TestToken.address) - let { logs } = await this.vaultFactory.deployVault(issuer, salt) + let { logs } = await this.vaultFactory.deployVault(issuer, salt, adminPeerID) this.VaultAddress = logs[0].args.contractAddress this.Vault = await Vault.at(this.VaultAddress) if(value != 0) { diff --git a/test/VaultFactory.test.js b/test/VaultFactory.test.js index 478b3ae..4ce250d 100644 --- a/test/VaultFactory.test.js +++ b/test/VaultFactory.test.js @@ -10,15 +10,18 @@ const VaultFactory = artifacts.require('VaultFactory') const Vault = artifacts.require('Vault') const TestToken = artifacts.require("TestToken") -contract('VaultFactory', function([issuer, other]) { +contract('VaultFactory', function([issuer, other1, other2]) { const salt = "0x000000000000000000000000000000000000000000000000000000000000abcd" + const adminPeerID = "0000000000000000000000000000000000000000000000000000a" + const userPeerID1 = "0000000000000000000000000000000000000000000000000000b" + const userPeerID2 = "0000000000000000000000000000000000000000000000000000c" function shouldDeployVault(issuer, value) { beforeEach(async function() { this.TestToken = await TestToken.new({from: issuer}) this.vaultFactory = await VaultFactory.new(this.TestToken.address) - let { logs } = await this.vaultFactory.deployVault(issuer, salt) + let { logs } = await this.vaultFactory.deployVault(issuer, salt, adminPeerID) this.VaultAddress = logs[0].args.contractAddress this.Vault = await Vault.at(this.VaultAddress) if(value != 0) { @@ -26,10 +29,6 @@ contract('VaultFactory', function([issuer, other]) { await this.TestToken.transfer(this.Vault.address, value, {from: issuer}); // deposit those tokens in vault } }) - - it('should allow other addresses to deploy with same salt', async function() { - await this.vaultFactory.deployVault(issuer, salt, { from: other }) - }) it('should deploy with the right issuer', async function() { expect(await this.Vault.issuer()).to.be.equal(issuer) @@ -48,6 +47,21 @@ contract('VaultFactory', function([issuer, other]) { it('should have set the ERC20 address correctly', async function() { expect(await this.Vault.token()).to.be.equal(this.TestToken.address) }) + + it('should allow other addresses to deploy with same salt', async function() { + let { logs } = await this.vaultFactory.deployVault(issuer, salt, userPeerID1, { from: other1 }) + const vaultAddr1 = logs[0].args.contractAddress + expect(await this.vaultFactory.deployedContracts(vaultAddr1)).to.be.true + }) + + it("should record the relationship between peerID and it's vault address", async function() { + await this.vaultFactory.deployVault(issuer, salt, userPeerID1, { from: other1 }) + await this.vaultFactory.deployVault(issuer, salt, userPeerID2, { from: other2 }) + const vaultAddr1 = await this.vaultFactory.peerVaultAddress(userPeerID1) + const vaultAddr2 = await this.vaultFactory.peerVaultAddress(userPeerID2) + expect(vaultAddr1).to.be.not.equal(vaultAddr2) + }) + } describe('when we deploy Vault', function() { @@ -61,12 +75,12 @@ contract('VaultFactory', function([issuer, other]) { shouldDeployVault(issuer, new BN(10)) }) - describe("when we deposit while issuer 0", function() { + describe("when we deposit with zero issuer", function() { this.timeout(100000); it('should fail', async function() { this.TestToken = await TestToken.new({from: issuer}) this.vaultFactory = await VaultFactory.new(this.TestToken.address) - await expectRevert(this.vaultFactory.deployVault(constants.ZERO_ADDRESS, salt), 'invalid issuer') + await expectRevert(this.vaultFactory.deployVault(constants.ZERO_ADDRESS, salt, adminPeerID), 'invalid issuer') }) }) })