Skip to content

Commit

Permalink
Merge pull request #31 from macctown/master
Browse files Browse the repository at this point in the history
9 张伟 - 第一次作业
  • Loading branch information
Wei Zhang authored Jan 14, 2018
2 parents 5c804ee + caa3ffd commit 4bed6e6
Show file tree
Hide file tree
Showing 12 changed files with 614 additions and 4 deletions.
16 changes: 16 additions & 0 deletions Lesson2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## 硅谷live以太坊智能合约频道官方地址

### 第二课《智能合约设计进阶-多员工薪酬系统》

目录结构
<br/>|
<br/>|--orgin 课程初始代码
<br/>|
<br/>|--assignment 课程作业提交代码
<br/>
### 本节知识点
第2课:智能合约设计进阶-多员工薪酬系统
- 动态静态数组的不同
- 函数输入参数检查 revert
- 循环与遍历的安全性
- 程序运行错误检查和容错:assert与require
10 changes: 10 additions & 0 deletions Lesson2/assignment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## 硅谷live以太坊智能合约 第二课作业
这里是同学提交作业的目录

### 第二课:课后作业
完成今天的智能合约添加100ETH到合约中
- 加入十个员工,每个员工的薪水都是1ETH
每次加入一个员工后调用calculateRunway这个函数,并且记录消耗的gas是多少?Gas变化么?如果有 为什么?
- 如何优化calculateRunway这个函数来减少gas的消耗?
提交:智能合约代码,gas变化的记录,calculateRunway函数的优化

282 changes: 282 additions & 0 deletions Lesson2/assignment/yours.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
pragma solidity ^0.4.14;

/** @title Payroll contract. */
contract Payroll {
struct Employee {
address wallet;
uint salary;
uint lastPayday;
}

Employee[] employees;
address owner;
uint constant payDuration = 10 seconds;

/** @dev Constructor.
*/
function Payroll() {
owner = msg.sender;
}

/** @dev Pay employee their rest payment before any change.
* @param employee Employee that need to be paid.
*/
function _payRestSalary(Employee employee) private {
uint restPayment = employee.salary * (now - employee.lastPayday) / payDuration;
employee.wallet.transfer(restPayment);
}

/** @dev Find target employee in employee array.
* @param targetWallet Wallet address of the target employee.
*/
function _findEmployee(address targetWallet) private returns (Employee, uint) {
for (uint i=0; i<employees.length; i++) {
if (employees[i].wallet == targetWallet) {
return (employees[i], i);
}
}
}

/** @dev Add new employee to contract.
* @param salary Salary of the new employee.
* @param wallet Wallet address of the new employee.
*/
function addEmployee(uint salary, address wallet) {
require(msg.sender == owner);

var (employee,index) = _findEmployee(wallet);
assert(employee.wallet == 0x0);

employees.push(Employee(wallet, salary * 1 ether, now));
}

/** @dev Remove new employee in the contract.
* @param salary Salary of the new employee needs to be updated.
* @param wallet Wallet address of the employee needs to be updated.
*/
function updateEmployee(uint salary, address wallet) {
require(msg.sender == owner);
var (employee, index) = _findEmployee(wallet);
assert(employee.wallet != 0x0);

_payRestSalary(employees[index]);
employees[index].salary = salary;
employees[index].lastPayday = now;
}

/** @dev Update employee in the contract.
* @param wallet Wallet address of the employee needs to be removed.
*/
function removeEmployee(address wallet) {
require(msg.sender == owner);
var (employee, index) = _findEmployee(wallet);
assert(employee.wallet != 0x0);

_payRestSalary(employees[index]);
employees[index] = employees[employees.length-1];
employees.length-1;
}

/** @dev add fund to employer.
* @return employer balance after adding fund.
*/
function addFund() payable returns (uint) {
return this.balance;
}

/** @dev calculate how many times the employer can pay the employees
* @return the times that employees can get paid from employer's balance
*/
function calculateRunway() returns (uint) {
uint totalSalary = 0;
for (uint i=0; i<employees.length; i++) {
totalSalary += employees[i].salary;
}
return this.balance / totalSalary;
}

/** @dev check if employer has enough money to pay
* @return true means employers has enough money, false means not
*/
function hasEnoughFund() returns (bool) {
return calculateRunway() > 0;
}

/** @dev pay salary to employee
*/
function getPaid() {
var (employee, index) = _findEmployee(msg.sender);
assert(employee.wallet == 0x0);

uint nextPayDay = employee.lastPayday + payDuration;
if (nextPayDay > now) {
revert();
}

employees[index].lastPayday = nextPayDay;
employees[index].wallet.transfer(employee.salary);
}
}

//gas history
0xca35b7d915458ef540ade6068dfe2f44e8fa733c
transaction cost 23009 gas
execution cost 1737 gas

0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
transaction cost 23779 gas
execution cost 2507 gas

0x583031d1113ad414f02576bd6afabfb302140225
transaction cost 24549 gas
execution cost 3277 gas

0xdd870fa1b7c4700f2bd7f44238821c26f7392148
transaction cost 25319 gas
execution cost 4047 gas

//transaction cost 和 execution cost都在逐渐提升

//原因:我认为是因为Employee数组中的员工数量递增,使得for循环次数增加,导致计算成本加大,从而gas cost升高。

//优化:不必每次调用calculateRunway都调用for循环来计算totalSalary,可以在每次添加新员工是进行计算,还要在更新员工信息的时候进行更新。
//优化代码
pragma solidity ^0.4.14;

/** @title Payroll contract. */
contract Payroll {
struct Employee {
address wallet;
uint salary;
uint lastPayday;
}

Employee[] employees;
address owner;
uint totalSalary = 0;
uint constant payDuration = 10 seconds;

/** @dev Constructor.
*/
function Payroll() {
owner = msg.sender;
}

/** @dev Pay employee their rest payment before any change.
* @param employee Employee that need to be paid.
*/
function _payRestSalary(Employee employee) private {
uint restPayment = employee.salary * (now - employee.lastPayday) / payDuration;
employee.wallet.transfer(restPayment);
}

/** @dev Find target employee in employee array.
* @param targetWallet Wallet address of the target employee.
*/
function _findEmployee(address targetWallet) private returns (Employee, uint) {
for (uint i=0; i<employees.length; i++) {
if (employees[i].wallet == targetWallet) {
return (employees[i], i);
}
}
}

/** @dev Add new employee to contract.
* @param salary Salary of the new employee.
* @param wallet Wallet address of the new employee.
*/
function addEmployee(uint salary, address wallet) {
require(msg.sender == owner);

var (employee,index) = _findEmployee(wallet);
assert(employee.wallet == 0x0);

employees.push(Employee(wallet, salary * 1 ether, now));
totalSalary += salary * 1 ether;
}

/** @dev Remove new employee in the contract.
* @param salary Salary of the new employee needs to be updated.
* @param wallet Wallet address of the employee needs to be updated.
*/
function updateEmployee(uint salary, address wallet) {
require(msg.sender == owner);
var (employee, index) = _findEmployee(wallet);
assert(employee.wallet != 0x0);

_payRestSalary(employees[index]);
totalSalary = totalSalary - employees[index].salary;
employees[index].salary = salary * 1 ether;
totalSalary = totalSalary + salary;

employees[index].lastPayday = now;
}

/** @dev Update employee in the contract.
* @param wallet Wallet address of the employee needs to be removed.
*/
function removeEmployee(address wallet) {
require(msg.sender == owner);
var (employee, index) = _findEmployee(wallet);
assert(employee.wallet != 0x0);

_payRestSalary(employees[index]);
employees[index] = employees[employees.length-1];
employees.length-1;
}

/** @dev add fund to employer.
* @return employer balance after adding fund.
*/
function addFund() payable returns (uint) {
return this.balance;
}

/** @dev calculate how many times the employer can pay the employees
* @return the times that employees can get paid from employer's balance
*/
function calculateRunway() returns (uint) {
return this.balance / totalSalary;
}

/** @dev check if employer has enough money to pay
* @return true means employers has enough money, false means not
*/
function hasEnoughFund() returns (bool) {
return calculateRunway() > 0;
}

/** @dev pay salary to employee
*/
function getPaid() {
var (employee, index) = _findEmployee(msg.sender);
assert(employee.wallet == 0x0);

uint nextPayDay = employee.lastPayday + payDuration;
if (nextPayDay > now) {
revert();
}

employees[index].lastPayday = nextPayDay;
employees[index].wallet.transfer(employee.salary);
}
}

//new gas history
0xca35b7d915458ef540ade6068dfe2f44e8fa733c
transaction cost 125490 gas
execution cost 102618 gas

0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
transaction cost 22202 gas
execution cost 930 gas

0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db
transaction cost 22202 gas
execution cost 930 gas

0x583031d1113ad414f02576bd6afabfb302140225
transaction cost 22202 gas
execution cost 930 gas

//gas 不变了,因为calculateRunway现在只是做一个除法
3 changes: 3 additions & 0 deletions Lesson2/orgin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 硅谷live以太坊智能合约 第二课《智能合约设计进阶-多员工薪酬系统》

这里是每一课的初始代码,有需要的同学可以参考
50 changes: 50 additions & 0 deletions Lesson2/orgin/payroll.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
pragma solidity ^0.4.14;

contract Payroll {
struct Employee {
address id;
uint salary;
uint lastPayday;
}

uint constant payDuration = 10 seconds;

address owner;
Employee[] employees;

function Payroll() {
owner = msg.sender;
}

function _partialPaid(Employee employee) private {
}

function _findEmployee(address employeeId) private returns (Employee, uint) {
}

function addEmployee(address employeeId, uint salary) {
}

function removeEmployee(address employeeId) {
}

function updateEmployee(address employeeId, uint salary) {
}

function addFund() payable returns (uint) {
}

function calculateRunway() returns (uint) {
uint totalSalary = 0;
for (uint i = 0; i < employees.length; i++) {
totalSalary += employees[i].salary;
}
return this.balance / totalSalary;
}

function hasEnoughFund() returns (bool) {
}

function getPaid() {
}
}
Loading

0 comments on commit 4bed6e6

Please sign in to comment.