profile
viewpoint
Jake Lin JakeLin @realestate-com-au Melbourne, Australia https://dribbble.com/Jake_Lin Dad of 👶 & 👦, iOS & Android dev. Passionate about design and animation. Make @IBAnimatable and SwiftWeather.

pull request commentJakeLin/SwiftLanguageWeather

Codable

@Mor4eza thanks for your PR, I have submitted a PR to upgrade the entire app to a newer version of Swift #77 , can you have a look, that PR will replace the changes in your PR.

Mor4eza

comment created time in a month

PR opened JakeLin/SwiftLanguageWeather

Upgrade to use Xcode 10

Upgrade to use Xcode 10

+2113 -2747

0 comment

68 changed files

pr created time in a month

issue commentIBAnimatable/IBAnimatable

SwiftUI integration / support

The programming pattern in SwiftUI is quite different from the way we use UIKit/Storyboard etc. I haven't found a way to put them together yet, I think the whole iOS community needs to figure out how the transition going.

mingsai

comment created time in a month

delete branch IBAnimatable/IBAnimatable

delete branch : feature/compound

delete time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Third attempt for Remittance contract

 pragma solidity >=0.4.21 <0.6.0;  import "./Pausable.sol";-import "./Ownable.sol";  contract Remittance is Pausable {-  address public carol;-  bytes32 private withdrawHash;+  struct Escrow {+    address carol;+    uint256 amount;+  }++  mapping (bytes32 => Escrow) public escrows;    event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);-  event LogWithdrawn(address indexed sender, uint256 amount);+  event LogWithdrawn(address indexed sender, bytes32 hash, uint256 amount);

Shall we add Alice’s address here? Then we don’t need to look up Alice in LogDeposited event via the hash

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Third attempt for Remittance contract

 pragma solidity >=0.4.21 <0.6.0;  import "./Pausable.sol";-import "./Ownable.sol";  contract Remittance is Pausable {-  address public carol;-  bytes32 private withdrawHash;+  struct Escrow {+    address carol;+    uint256 amount;+  }++  mapping (bytes32 => Escrow) public escrows;    event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);-  event LogWithdrawn(address indexed sender, uint256 amount);+  event LogWithdrawn(address indexed sender, bytes32 hash, uint256 amount);    constructor(bool paused) Pausable(paused) public {   } -  function generateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {-     return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));+  function generateHash(address carol, bytes32 bobPassword, bytes32 carolPassword) public view returns(bytes32) {+     return keccak256(abi.encodePacked(this, carol, bobPassword, carolPassword));   } -  function deposit(bytes32 _hash, address _carol) external payable onlyOwner whenRunning whenAlive {-    require(_carol != address(0), "Carol's address must not be zero!");+  function deposit(bytes32 hash, address carol) external payable onlyOwner whenRunning whenAlive {+    require(carol != address(0), "Carol's address must not be zero!");     require(msg.value > 0, "The value of deposit must be more than zero ether!");-    carol = _carol;-    withdrawHash = _hash;-    emit LogDeposited(msg.sender, msg.value, _hash, _carol);+    escrows[hash] = Escrow(carol, msg.value);

Yes, the owner can override the previous amount

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Second attempt

 contract Remittance is Pausable {   constructor(bool paused) Pausable(paused) public {   } -  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+  function generateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {      return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));   } -  function deposit(bytes32 _hash, address _carol) external payable whenRunning whenAlive {+  function deposit(bytes32 _hash, address _carol) external payable onlyOwner whenRunning whenAlive {

thanks, I have fixed that in the PR #3

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Second attempt

 contract Remittance is Pausable {   constructor(bool paused) Pausable(paused) public {   } -  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+  function generateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {      return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));   } -  function deposit(bytes32 _hash, address _carol) external payable whenRunning whenAlive {+  function deposit(bytes32 _hash, address _carol) external payable onlyOwner whenRunning whenAlive {

thanks, I have fixed that in the PR #3

JakeLin

comment created time in 2 months

pull request commentIBAnimatable/IBAnimatable

For iOS13 do not use `automatic` modal presentation style for segue

Because Segue doesn't support @IBInspectable, we have to set a default value for one Segue, the walkaround is to create different segue for different Presentation.

If the current Segues only support fullscreen, shall we change the method func present(animated flag: Bool = true) to func presentFullScreen(animated flag: Bool = true)?

phimage

comment created time in 2 months

delete branch IBAnimatable/IBAnimatable

delete branch : feature/hashable

delete time in 2 months

push eventIBAnimatable/IBAnimatable

Eric Marchand

commit sha 57424097e1f52588587bbf62f73c132b2aa61e72

Use `Hasher` to hash to implement `Hashable`

view details

Eric Marchand

commit sha 938529721e2bd1e6f82f307ca36bd0e3f9ad8534

Update CHANGELOG about `Hashable`

view details

push time in 2 months

PR merged IBAnimatable/IBAnimatable

Use `Hasher` to implement `Hashable`

better late than never

+20 -2

2 comments

4 changed files

phimage

pr closed time in 2 months

PR opened JakeLin/ethereum-remittance

Reviewers
Third attempt for Remittance contract

In this contract, I add support to multiple escrows. And also address the comments for PR #2.

@xavierlepretre can you please have a look? thanks

+108 -30

0 comment

3 changed files

pr created time in 2 months

create barnchJakeLin/ethereum-remittance

branch : feature/third-attempt

created branch time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Second attempt

 pragma solidity >=0.4.21 <0.6.0;  import "./Pausable.sol";+import "./Ownable.sol";

you're right, Pausable has imported Ownable

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+     return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));

is this is enough here? or I need to use address(this) to use the contract address which won't change for a deployed contract.

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {

If I shadow it, I can do

  function deposit(bytes32 _hash, address carol) external payable onlyOwner whenRunning whenAlive {
    require(carol != address(0), "Carol's address must not be zero!");
    require(msg.value > 0, "The value of deposit must be more than zero ether!");
    // carol = _carol;
    Remittance.carol = carol; // Setting a shadowing state
    withdrawHash = _hash;
    emit LogDeposited(msg.sender, msg.value, _hash, carol);
  }
JakeLin

comment created time in 2 months

delete branch JakeLin/ethereum-remittance

delete branch : feature/second-attempt

delete time in 2 months

push eventJakeLin/ethereum-remittance

Jake Lin

commit sha acf0e66cd4949ec50d88c2acdd918929b58bda14

[Test] - Add description to `beforeEach`

view details

Jake Lin

commit sha d7d3d2f3468f7f155353a0f35aca608d6350c36f

[Contract] - remove unused `owner` from `Remittance` contract

view details

Jake Lin

commit sha f5ae3786fea3d7b0ff486440c4dbf59bd092d50a

[Contract] - Only owner can deposit

view details

Jake Lin

commit sha c45f3031690088be2db2e1b8860779441f56e3e0

[Test] - rename to `remittance` for the contract instance

view details

Jake Lin

commit sha 75545cb00db3fcb2f046f5282ebe4d07ed10f2b3

[Contract] - rename to `generateHash`

view details

Jake Lin

commit sha 69204be82ae6a9027389221b055b09de7571d3d6

[Deployment] - create the `HDWalletProvider` based on the condition.

view details

Jake Lin

commit sha a2dc34f3545d4b8434beb0723ece48c1806a23b8

Merge pull request #2 from JakeLin/feature/second-attempt All comments will be addressed in the next PR

view details

push time in 2 months

PR merged JakeLin/ethereum-remittance

Second attempt

Address the comments for PR #1

@xavierlepretre can you please have a look again? Thanks

+52 -35

0 comment

3 changed files

JakeLin

pr closed time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {

Ok for shadowing.

I don't get this part here, why shall we keep a warning here?

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+     return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));+  }++  function deposit(bytes32 _hash, address _carol) external payable whenRunning whenAlive {

I think it's the same issue in this comment https://github.com/JakeLin/ethereum-remittance/pull/2#discussion_r316921467

I am thinking to create an array or mapping to recode each TX to keep the amount, then no previous TX can be overwritten.

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {

Since we don't store the passwords and only store withdrawHash, how can the others/hack see the passwords?

If the hack doesn't a hack to do a front-run tx to overwrite Carol's address int the second parameter. But they don't know how to generate the same hash to withdraw. And the hacker's fund (ethers in msg.value) will be locked in the contract. The owner's fund didn't get mined, so it's not lost. Am I correct?

JakeLin

comment created time in 2 months

pull request commentJakeLin/ethereum-splitter

Address comments for PR 6

That's weird, may I come back to this later? I am trying to finish the other projects

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Second attempt

 contract Remittance is Pausable {   constructor(bool paused) Pausable(paused) public {   } -  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+  function generateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {      return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));   } -  function deposit(bytes32 _hash, address _carol) external payable whenRunning whenAlive {+  function deposit(bytes32 _hash, address _carol) external payable onlyOwner whenRunning whenAlive {

If I understand correctly, the owner can deposit twice. For example.

// First deposit
const hash1 = remittance.methods.generateHash('password1', 'password2', address1).call();
deposit(hash1, address1); // deposit 10 ethers

// Second deposit
const hash2 = remittance.methods.generateHash('different-password3', 'different-password4', address2).call();
deposit(hash2, address2); // deposit 5 ethers

Then address2 can withdraw 5 ethers andaddress1` lost all 10 ethers.

Is it the hack you are thinking?

Thanks again

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Second attempt

 pragma solidity >=0.4.21 <0.6.0;  import "./Pausable.sol";+import "./Ownable.sol";

it's used in onlyOwner

JakeLin

comment created time in 2 months

PR opened JakeLin/ethereum-remittance

Reviewers
Second attempt

Address the comments for PR #1

@xavierlepretre can you please have a look again? Thanks

+52 -35

0 comment

3 changed files

pr created time in 2 months

create barnchJakeLin/ethereum-remittance

branch : feature/second-attempt

created branch time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {

@xavierlepretre If I remove _ from _carol, there is a warning says This declaration shadows an existing declaration. Because I have a state for the contract called address public carol;

JakeLin

comment created time in 2 months

delete branch JakeLin/ethereum-remittance

delete branch : feature/first-attempt

delete time in 2 months

push eventJakeLin/ethereum-remittance

Jake Lin

commit sha dbbb96917c6c081550b14c32c030c0636cee5af0

[Contract] - Add some boilerplate

view details

Jake Lin

commit sha 5fc64a55c4d0c4f5d7dba9c5eaa679c52a424be2

[Contract] - Add `Remittance` contract

view details

Jake Lin

commit sha 168b5c7e25c15766889e51c634cf9dc386d82000

Add README.md

view details

Jake Lin

commit sha 787d73a4ad0edaad8d452677a0b26ffd6988e719

Merge pull request #1 from JakeLin/feature/first-attempt Merge this PR now, will address the comments in next PR.

view details

push time in 2 months

PR merged JakeLin/ethereum-remittance

Remittance Contract

First attempt to add Remittance contract

+356 -18

0 comment

8 changed files

JakeLin

pr closed time in 2 months

delete branch JakeLin/ethereum-splitter

delete branch : feature/address-comments-for-pr-6

delete time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+const truffleAssert = require('truffle-assertions');+const Remittance = artifacts.require('Remittance');++const { toBN, toWei, fromAscii } = web3.utils;++const zeroAddress = '0x0000000000000000000000000000000000000000';++contract('Remittance', accounts => {+  const [owner, carol, someoneElse] = accounts;+  let contract;++  const bobCorrectPassword = fromAscii('bob-super-secure-password');+  const carolCorrectPassword = fromAscii('carol-super-secure-password');++  const bobWrongPassword = fromAscii('bob-wrong-password');+  const carolWrongPassword = fromAscii('carol-wrong-password');++  beforeEach(async () => {+    contract = (await Remittance.new(false, { from: owner, gas: 3000000 })).contract;+  });++  context('constructor()', () => {+    it('should deploy the contract correctly', async () => {+      assert.ok(contract);+      assert.strictEqual(await contract.methods.getOwner().call(), owner);+      assert.strictEqual(await contract.methods.isPaused().call(), false);+    });+  });+  +  +  context('deposit()', async () => {+    let hash;+    beforeEach(async () => {+      // Arrange+      hash = await contract.methods.calcuateHash(carol, bobCorrectPassword, carolCorrectPassword).call();

sorry, my bad

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+const truffleAssert = require('truffle-assertions');+const Remittance = artifacts.require('Remittance');++const { toBN, toWei, fromAscii } = web3.utils;++const zeroAddress = '0x0000000000000000000000000000000000000000';++contract('Remittance', accounts => {+  const [owner, carol, someoneElse] = accounts;+  let contract;

OK, let me rename it

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+     return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));+  }++  function deposit(bytes32 _hash, address _carol) external payable whenRunning whenAlive {+    require(_carol != address(0), "Carol's address must not be zero!");+    require(msg.value > 0, "The value of deposit must be more than zero ether!");+    carol = _carol;+    withdrawHash = _hash;+    emit LogDeposited(msg.sender, msg.value, _hash, _carol);+  }++  function withdraw(bytes32 _bobPassword, bytes32 _carolPassword) external whenRunning whenAlive {+    require(msg.sender == carol, "Only Carol can withdraw!");+    uint256 contractBalance = address(this).balance;+    require(contractBalance > 0, "Can't withdraw since the contract balance is zero!");++    bytes32 hashValue = calcuateHash(msg.sender, _bobPassword, _carolPassword);

I don't know line 29? because line 34 covers it.

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+     return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));+  }++  function deposit(bytes32 _hash, address _carol) external payable whenRunning whenAlive {

Do you mean adding onlyOwner?

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {+     return keccak256(abi.encodePacked(this, _carol, _bobPassword, _carolPassword));

Sorry, I don't get this here, shall Alice always use different passwords for Bob and Carol every time to avoid getting the same withdrawHash again since the withdrawHash can be found on-chain?

Because we need to re-call this function again, if we use something like block.timestamp we aren't able to get the same hash when withdraw. Any better way to generate different hash event providing the same passwords again?

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;+  address public carol;+  bytes32 private withdrawHash;++  event LogDeposited(address indexed sender, uint256 amount, bytes32 hash, address indexed carol);+  event LogWithdrawn(address indexed sender, uint256 amount);++  constructor(bool paused) Pausable(paused) public {+  }++  function calcuateHash(address _carol, bytes32 _bobPassword, bytes32 _carolPassword) public view returns(bytes32) {

Because of the requirement:

Carol submits both passwords to Alice's remittance contract.

Bob doesn't care about ether, he may not have an ether account, may not have ether for gas etc. So I think Carol must have an address to receive the fund.

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/ethereum-remittance

Remittance Contract

+pragma solidity >=0.4.21 <0.6.0;++import "./Pausable.sol";++contract Remittance is Pausable {+  address public owner;

My bad, I don't need address public owner; here, if I put it here, there are two instances of owner state on-chain I reckon.

JakeLin

comment created time in 2 months

pull request commentJakeLin/ethereum-splitter

Address comments for PR 6

@xavierlepretre sorry, for some reason, I didn't commit the fix to github. I have pushed the commit 522d3d8e36ad3d0a44f955270a34f20351de7ebd now, it should be OK now.

JakeLin

comment created time in 2 months

push eventJakeLin/ethereum-splitter

Jake Lin

commit sha 522d3d8e36ad3d0a44f955270a34f20351de7ebd

Fix `contract.methods.getOwner`

view details

push time in 2 months

MemberEvent

pull request commentJakeLin/ethereum-splitter

Address comments for PR 6

thanks @adelRestom I am working on project 2 and 3 to try to make meet the deadline, will come back to this soon.

JakeLin

comment created time in 2 months

push eventJakeLin/ethereum-remittance

Jake Lin

commit sha 168b5c7e25c15766889e51c634cf9dc386d82000

Add README.md

view details

push time in 2 months

PR opened JakeLin/ethereum-remittance

Remittance Contract

First attempt to add Remittance contract

+337 -18

0 comment

7 changed files

pr created time in 2 months

push eventJakeLin/ethereum-remittance

Jake Lin

commit sha 5fc64a55c4d0c4f5d7dba9c5eaa679c52a424be2

[Contract] - Add `Remittance` contract

view details

push time in 2 months

create barnchJakeLin/ethereum-remittance

branch : feature/first-attempt

created branch time in 2 months

push eventJakeLin/ethereum-remittance

Jake Lin

commit sha 9f7576423103c03d41293a4cd1d0fd7fe16fd8b2

Initial commit

view details

push time in 2 months

create barnchJakeLin/ethereum-remittance

branch : master

created branch time in 2 months

created repositoryJakeLin/ethereum-remittance

A smart contract for remittance running on Ethereum EVM

created time in 2 months

pull request commentJakeLin/splitter

Address comments for PR 6

@xavierlepretre I just merged the PR, can you rebase master?

JakeLin

comment created time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha af8ded9b0d178f36a9ebb844c9f2f923336847ba

[Contract] - remove the balance check in `withdrawAll`

view details

Jake Lin

commit sha 46103b7281c2453bfd7f3e52430ab3b97adcc001

[Contract] - merge `Killable` contract into `Pausable` contract.

view details

Jake Lin

commit sha 97210c20956a0a62a69a5d10ad41ee6fe62125a4

[Contract] - Add `paused` parameter to `Splitter` contract

view details

Jake Lin

commit sha ab266c7e51f40793803c2a6851ffeb29ade368fb

Merge pull request #8 from JakeLin/feature/address-comments-for-pr-6 Address the comments for PR 6

view details

push time in 2 months

PR merged JakeLin/splitter

Address the comments for PR 6

In this PR, I addressed the comments for PR #6.

@xavierlepretre and @adelRestom , can you guys please have a look again? thanks

+52 -78

0 comment

5 changed files

JakeLin

pr closed time in 2 months

PR opened JakeLin/splitter

Reviewers
Address the comments for PR 6

In this PR, I addressed the comments for PR #6.

@xavierlepretre and @adelRestom , can you guys please have a look again? thanks

+52 -78

0 comment

5 changed files

pr created time in 2 months

create barnchJakeLin/splitter

branch : feature/address-comments-for-pr-6

created branch time in 2 months

pull request commentJakeLin/splitter

Address comments for PR 6

@adelRestom I have followed your advice to add return true in the function like

  function split(address _beneficiary1, address _beneficiary2) external payable whenRunning whenAlive returns(bool) {
    require(_beneficiary1 != address(0) && _beneficiary2 != address(0), "Beneficiary's address must not be zero!");
    require(msg.value > 0, "Must split more than zero ether!");

    if (msg.value.mod(2) == 1) {
      balances[msg.sender] = balances[msg.sender].add(1);
    }
    uint256 half = msg.value.div(2);
    balances[_beneficiary1] = balances[_beneficiary1].add(half);
    balances[_beneficiary2] = balances[_beneficiary2].add(half);
    emit LogSplitted(msg.sender, msg.value, _beneficiary1, _beneficiary2);
    return true;
  }

And add the call() check in App.js like

      const result = await contract.methods.split(beneficiary1Address, beneficiary2Address).call({
        from: accounts[0],
        value: web3.utils.toWei(etherToSplit, 'ether')
      });
      if (!result) {
        return;
      }

However, even I send 0 wei to the contract, result is still true. I also tried the same approach in withdraw function. They always return true when I call call().

So I don't know how can I check before I send().

JakeLin

comment created time in 2 months

delete branch JakeLin/splitter

delete branch : feature/address-comments-for-pr-6

delete time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha d650c9c5d9b57639cc22f4a0d7b0a47b8ae1f4c4

[Contract] - Add `changeOwner` to `Ownable` Contract.

view details

Jake Lin

commit sha bc920341581663e86c51d766d4286f17d1413535

[Contract] - Add the parameter `paused` to `Pausable` contract

view details

Jake Lin

commit sha c6dff277d4cf9cee145e8a98da2de4c3a2e7708f

[Contract] - rename `whenNotPaused` to `whenRunning`

view details

Jake Lin

commit sha b192fb2090132d8c5b9048d96b1dccbd49944444

[Contract] - rename `unpause`to `resume` in `Pausable` contract

view details

Jake Lin

commit sha b150fa170124109c8753b21ddfb4aaf48fa3fdcb

[Contract] - Use modifier `whenRunning` in the `pause` method

view details

Jake Lin

commit sha 042dabdd0f84b5f08b3373a44376bbcb59fe5e4e

[Contract] - add `Killable` contract

view details

Jake Lin

commit sha 33829d055cbbe96d38c406daff5e76bb024a3591

[Contract] - add `withdrawAll` method in `Splitter` contract

view details

Jake Lin

commit sha d5854bb5be70f625d2e5ce8b50704c23f3cb85f6

[Contract] - Credit the remainder to the sender's balance when split odd number of ether.

view details

Jake Lin

commit sha 842b855d586457e6be534d4f9db43ec2f6d564fb

Merge pull request #7 from JakeLin/feature/address-comments-for-pr-6 @adelRestom and @JakeLin thank you very much for your review, I will address all comments in the next PR, merged this for now.

view details

push time in 2 months

PR merged JakeLin/splitter

Reviewers
Address comments for PR 6

In this PR, I addressed the comments for PR #6

@adelRestom I have addressed all your comments except for the App.js.

App.js

  1. Very important thing we usually do is to protect the user from losing Ether on function calls that would fail; we do that by first calling the function with .call() which runs it locally/simulation (locally => no transaction => no mining => no gas consumed => no Ether lost), if that call doesn't fail then we proceed to actually call the function (i.e. call it without .call.
  2. You used on('transactionHash'), also use on('receipt') to inspect the receipt status (that it's == 1) and to inform the user of a successful transaction

For the first comment, do you call all the send method like await contract.methods.split(beneficiary1Address, beneficiary2Address).call(); before

await contract.methods.split(beneficiary1Address, beneficiary2Address).send({
  from: accounts[0],
  value: web3.utils.toWei(etherToSplit, 'ether')
});

For the second comment, I have checked the receipt in the promise call - see tx variable, please.

  const tx = await contract.methods.split(beneficiary1Address, beneficiary2Address).send({
        from: accounts[0],
        value: web3.utils.toWei(etherToSplit, 'ether')
      }).on('transactionHash', txHash => {
        this.setState({ splitMessage: `Transaction ${txHash} submitted, waiting for the network to mine` });
      }).on('receipt', receipt => {
        console.log(receipt);
      });
      console.log(tx);

I print out receipt and tx, they are exactly same.

@xavierlepretre can you please have a look as well? thanks

+245 -26

4 comments

5 changed files

JakeLin

pr closed time in 2 months

pull request commentJakeLin/splitter

Address comments for PR 6

@xavierlepretre @adelRestom

I have tried out the following code

   const result = await contract.methods.split(beneficiary1Address, beneficiary2Address).call({
        from: accounts[0],
        value: web3.utils.toWei(etherToSplit, 'ether')
      });
      console.log(result);
      const tx = await contract.methods.split(beneficiary1Address, beneficiary2Address).send({
        from: accounts[0],
        value: web3.utils.toWei(etherToSplit, 'ether')
      }).on('transactionHash', txHash => {
        this.setState({ splitMessage: `Transaction ${txHash} submitted, waiting for the network to mine` });
      });
      if (tx.status) {
        const contractBalance = web3.utils.fromWei(await web3.eth.getBalance(contractAddress));
        this.setState({ splitMessage: `Transaction ${tx.transactionHash} got mined successfully.`, contractBalance });
      } else {
        this.setState({ splitMessage: `Something went wrong with transaction ${tx.transactionHash}.` });
      }

I found out whether the transaction fails or not in await contract.methods.split(beneficiary1Address, beneficiary2Address).send(... , await contract.methods.split(beneficiary1Address, beneficiary2Address).call(... will always returns an object of Result like Screen Shot 2019-08-18 at 11 32 14 am

Can you guys tell me how can I meet @adelRestom 's requirement?

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 contract('Splitter', accounts => {   });    context('When owner pause the contract', () => {-    context('if the contract is unpaused', () => {+    context('if the contract is not paused', () => {

thanks @xavierlepretre I keep as it's for now.

JakeLin

comment created time in 2 months

PR opened JakeLin/splitter

Reviewers
Address comments for PR 6

In this PR, I addressed the comments for PR #6

@adelRestom I have addressed all your comments except for the App.js.

App.js

  1. Very important thing we usually do is to protect the user from losing Ether on function calls that would fail; we do that by first calling the function with .call() which runs it locally/simulation (locally => no transaction => no mining => no gas consumed => no Ether lost), if that call doesn't fail then we proceed to actually call the function (i.e. call it without .call.
  2. You used on('transactionHash'), also use on('receipt') to inspect the receipt status (that it's == 1) and to inform the user of a successful transaction

For the first comment, do you call all the send method like await contract.methods.split(beneficiary1Address, beneficiary2Address).call(); before

await contract.methods.split(beneficiary1Address, beneficiary2Address).send({
  from: accounts[0],
  value: web3.utils.toWei(etherToSplit, 'ether')
});

For the second comment, I have checked the receipt in the promise call - see tx variable, please.

  const tx = await contract.methods.split(beneficiary1Address, beneficiary2Address).send({
        from: accounts[0],
        value: web3.utils.toWei(etherToSplit, 'ether')
      }).on('transactionHash', txHash => {
        this.setState({ splitMessage: `Transaction ${txHash} submitted, waiting for the network to mine` });
      }).on('receipt', receipt => {
        console.log(receipt);
      });
      console.log(tx);

I print out receipt and tx, they are exactly same.

@xavierlepretre can you please have a look as well? thanks

+245 -26

0 comment

5 changed files

pr created time in 2 months

create barnchJakeLin/splitter

branch : feature/address-comments-for-pr-6

created branch time in 2 months

pull request commentJakeLin/splitter

Address comments for PR #5

@adelRestom thanks for your feedback, I merged the PR now and will address your comment in the next PR.

@xavierlepretre thanks again for your follow-up, can you please have a look again on my comments?

JakeLin

comment created time in 2 months

delete branch JakeLin/splitter

delete branch : feature/address-comments-for-pr-5

delete time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha 3e94dc8ae97d1b16100b04eff15e4aad248c07b6

[Contract] - Add `Ownable` contract

view details

Jake Lin

commit sha c1e9dbaecb5046b3dc7e969e067a17848343ffab

Update `Pausable` to be sub-contract of `Ownable` and make `paused` to be `private`

view details

Jake Lin

commit sha fb094fb331a9fe1cc4958c1c7df5d67f991b6f31

[Contract] - Add `LogPaused` and `LogUnpaused` events

view details

Jake Lin

commit sha d93f2c29e8a699403ffa7cf9042559ae3e65750b

[Contract] - Rename to `unpause`

view details

Jake Lin

commit sha 08c3c9fd5165158cf7b8b48e5a674324d65c78d1

[Web] - Show the error message if needed

view details

Jake Lin

commit sha f349a5f1e9590d025a86cff878497096416b9c05

[Web] - Get the network id from web3

view details

Jake Lin

commit sha 2bab45acfc5f23a2da6ad9a638ee4144aba83ba1

[Web] - Add status update when submitting the transaction.

view details

Jake Lin

commit sha 1c2fa2fc0cf61c6ec73951fb402c488440c70dd9

[Web] - fallback to local node if MetaMask is not available.

view details

Jake Lin

commit sha d3c8992df64b513707d26466349ab257c5cfd8df

[Contract] - Add a getter method for `owner` in `Ownable` contract

view details

Jake Lin

commit sha 79be12b0dccb46a7a75efb963468680ed11c555f

[Contract] - Improve the comments for the tests

view details

Jake Lin

commit sha a69c8a41ee5797cfb2e83af44a5353aefa4aa78b

[Web] - Enhance the error message

view details

Jake Lin

commit sha 8b2cde4ddbf946ae251592599ee7eb4b1161f7b5

[Contract] - add `indexed` to make the `LogPaused` and `LogUnpaused` can be searched by `account`

view details

Jake Lin

commit sha 5296ae8b788461bb2e976000dc9cbfd3132a8708

[Contract] - Explicitly set `paused` to `false` in the constructor.

view details

Jake Lin

commit sha d463e61c49482f93b44cd739726276a22c524bdf

Merge pull request #6 from JakeLin/feature/address-comments-for-pr-5 Address comments for PR #5, comments will be addressed in next PR

view details

push time in 2 months

PR merged JakeLin/splitter

Address comments for PR #5

I have addressed all comments for PR #5, can you please have a look again? @xavierlepretre thanks

+128 -59

2 comments

6 changed files

JakeLin

pr closed time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 contract('Splitter', accounts => {   });    context('When owner pause the contract', () => {-    context('if the contract is unpaused', () => {+    context('if the contract is not paused', () => {

do you think I should add a test case at the same level like? thanks

context('When owner pause the contract') {
    context('if the contract is not paused') {}
    context('if the contract is paused') {}
    context('if the contract is unpaused') {} // Add new test case here?
}
JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 contract('Splitter', accounts => {   });    context('When owner pause the contract', () => {-    context('if the contract is unpaused', () => {+    context('if the contract is not paused', () => {

sorry, I can't find beforeEach("unpause the contract"); in the test.

There is an Act in the beforeEach like

beforeEach(async () => {
        // Arrange, because by default, the contract is not paused, no need to arrange.
        // Act
        tx = await contract.methods.pause().send({from: owner});
      });

The test cases I try to cover are

context('When owner pause the contract') {
    context('if the contract is not paused') {
        beforeEach() // No need to Arrange, shareable Act for both two its below
        it('the contract should be paused'); // Assert
        it('should emit the LogPaused event'); // Assert
    }
    
    context('if the contract is paused') {
        beforeEach(); // Arrange to set the test to  be paused
        it('should fail'); // Act and Assert
    }
}
JakeLin

comment created time in 2 months

pull request commentJakeLin/splitter

Address comments for PR #5

@xavierlepretre thanks for your review, I have addressed the comments, can you please take another look?

JakeLin

comment created time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha 8b2cde4ddbf946ae251592599ee7eb4b1161f7b5

[Contract] - add `indexed` to make the `LogPaused` and `LogUnpaused` can be searched by `account`

view details

Jake Lin

commit sha 5296ae8b788461bb2e976000dc9cbfd3132a8708

[Contract] - Explicitly set `paused` to `false` in the constructor.

view details

push time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 contract('Splitter', accounts => {   });    context('When owner pause the contract', () => {-    context('if the contract is unpaused', () => {+    context('if the contract is not paused', () => {

I put context('if the contract is not paused', () => { to group the happy case because there is a sad case in the same level context('if the contract is paused', () => {. Can you advice how can I improve this?

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 pragma solidity >=0.4.21 <0.6.0; -contract Pausable {-  address public owner;-  bool public paused;+import "./Ownable.sol"; -  constructor() public {-    owner = msg.sender;-  }+contract Pausable is Ownable {+  event LogPaused(address account);+  event LogUnpaused(address account); -  modifier onlyOwner() {-    require(msg.sender == owner, "Only owner can do this!");-    _;-  }+  bool private paused = false;

Fixed by 5296ae8b788461bb2e976000dc9cbfd3132a8708

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 pragma solidity >=0.4.21 <0.6.0; -contract Pausable {-  address public owner;-  bool public paused;+import "./Ownable.sol"; -  constructor() public {-    owner = msg.sender;-  }+contract Pausable is Ownable {+  event LogPaused(address account);+  event LogUnpaused(address account); -  modifier onlyOwner() {-    require(msg.sender == owner, "Only owner can do this!");-    _;-  }+  bool private paused = false;

Fixed by 5296ae8b788461bb2e976000dc9cbfd3132a8708

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 pragma solidity >=0.4.21 <0.6.0; -contract Pausable {-  address public owner;-  bool public paused;+import "./Ownable.sol"; -  constructor() public {-    owner = msg.sender;-  }+contract Pausable is Ownable {+  event LogPaused(address account);+  event LogUnpaused(address account);

Fixed by 8b2cde4ddbf946ae251592599ee7eb4b1161f7b5

JakeLin

comment created time in 2 months

CommitCommentEvent

Pull request review commentJakeLin/splitter

Address comments for PR #5

 pragma solidity >=0.4.21 <0.6.0; -contract Pausable {-  address public owner;-  bool public paused;+import "./Ownable.sol"; -  constructor() public {-    owner = msg.sender;-  }+contract Pausable is Ownable {+  event LogPaused(address account);+  event LogUnpaused(address account);

right, got it, thanks. If they need to filter the log, we need to index that.

JakeLin

comment created time in 2 months

pull request commentJakeLin/splitter

Address comments for PR #5

@xavierlepretre I have addressed your comments, can you please have a look again? Thanks

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 class App extends Component {      try {       this.setState({ splitMessage: 'Submitting the transaction.' });-      await contract.methods.split(beneficiary1Address, beneficiary2Address).send({+      const tx = await contract.methods.split(beneficiary1Address, beneficiary2Address).send({         from: accounts[0],         value: web3.utils.toWei(etherToSplit, 'ether')+      }).on("transactionHash", txHash => {+        this.setState({ splitMessage: `Transaction ${txHash} submitted, waiting for the network to mine` });       });-      const contractBalance = web3.utils.fromWei(await web3.eth.getBalance(contractAddress));-      this.setState({ splitMessage: `Splitted successfully.`, contractBalance });+      if (tx.status) {+        const contractBalance = web3.utils.fromWei(await web3.eth.getBalance(contractAddress));+        this.setState({ splitMessage: `Transaction ${tx.transactionHash} got mined successfully.`, contractBalance });+      } else {+        this.setState({ splitMessage: `Something went wrong.` });

Yes, please see a69c8a41ee5797cfb2e83af44a5353aefa4aa78b

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 contract('Splitter', accounts => {   });    context('When owner pause the contract', () => {-    context('if the contract is unpaused', () => {+    context('if the contract is not paused', () => {

I prefer to put the common logic including Act in beforeEach because they can be shared in different it('Assert'). Because the contract by default is not paused, so we don't need to do Arrange here. Please see 79be12b0dccb46a7a75efb963468680ed11c555f

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

+pragma solidity >=0.4.21 <0.6.0;++contract Ownable {+  address private owner;

thanks, please see d3c8992df64b513707d26466349ab257c5cfd8df

JakeLin

comment created time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha d3c8992df64b513707d26466349ab257c5cfd8df

[Contract] - Add a getter method for `owner` in `Ownable` contract

view details

Jake Lin

commit sha 79be12b0dccb46a7a75efb963468680ed11c555f

[Contract] - Improve the comments for the tests

view details

Jake Lin

commit sha a69c8a41ee5797cfb2e83af44a5353aefa4aa78b

[Web] - Enhance the error message

view details

push time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 pragma solidity >=0.4.21 <0.6.0; -contract Pausable {-  address public owner;-  bool public paused;+import "./Ownable.sol"; -  constructor() public {-    owner = msg.sender;-  }+contract Pausable is Ownable {+  event LogPaused(address account);+  event LogUnpaused(address account);

Not really, they more concern about can they do split or withdraw, am I right?

JakeLin

comment created time in 2 months

Pull request review commentJakeLin/splitter

Address comments for PR #5

 pragma solidity >=0.4.21 <0.6.0; -contract Pausable {-  address public owner;-  bool public paused;+import "./Ownable.sol"; -  constructor() public {-    owner = msg.sender;-  }+contract Pausable is Ownable {+  event LogPaused(address account);+  event LogUnpaused(address account); -  modifier onlyOwner() {-    require(msg.sender == owner, "Only owner can do this!");-    _;-  }+  bool private paused = false;

The one in the constructor wins, in that case, it will be true

JakeLin

comment created time in 2 months

CommitCommentEvent

PR opened JakeLin/splitter

Reviewers
Address comments for PR #5

I have addressed all comments for PR #5, can you please have a look again? @xavierlepretre thanks

+115 -51

0 comment

5 changed files

pr created time in 2 months

create barnchJakeLin/splitter

branch : feature/address-comments-for-pr-5

created branch time in 2 months

delete branch JakeLin/splitter

delete branch : feature/web

delete time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha b699cd8a92e201abb89655abc7b807e9271bb7b7

[Web] - Initialise Web3 in the web

view details

Jake Lin

commit sha 8c05a7226e39648b7abfdfbd92ed11f189ffd52b

[Web] - Add Contract Info

view details

Jake Lin

commit sha 33559b28a262b66765ebacbf44d19edd952382d8

[Web] - Add check account balance

view details

Jake Lin

commit sha d1c83843dbe55bcb7b2a04189d7ae023fe333ea2

[Web] - Add `split` feature in Web

view details

Jake Lin

commit sha 5c8ce775dfa31c6e0cc2d65ede3898db88ae554e

[Contract] - Remove `Ownable` and everyone can call `split`.

view details

Jake Lin

commit sha 5ba8f3065a275aae2014b81912662eea50633da7

[Web] - fix the messages

view details

Jake Lin

commit sha 2d134920045861118f9b71436cdc076440a5cb7f

[Web] - Add `withdraw` to the website

view details

Jake Lin

commit sha fd30c06e358c0e9dcdcfcb10b6a7fe2dff3b2e53

[Contract] - Add `Pausable` contract

view details

Jake Lin

commit sha 178495a56bf64608bb01d3830fcc0d3ad1323aea

[Web] - Refresh the contract balance after the operation.

view details

Jake Lin

commit sha 0a7b5884f6702438e1447866d02ce3c79ad8d7e4

Update README.md

view details

Jake Lin

commit sha f6bab93b0e40ad1137054ad0e366c28e0df557fe

Merge pull request #5 from JakeLin/feature/web Add web UI to interact with the contract, all comments will be addressed in next PR, thanks

view details

push time in 2 months

PR merged JakeLin/splitter

Add web UI to interact with the contract

In this PR, I have addressed the comments for PR #4 and added a web UI to interact with the contract.

Screen Shot 2019-08-13 at 10 42 48 pm

@xavierlepretre Can you please have a look again? thanks

+451 -139

0 comment

9 changed files

JakeLin

pr closed time in 2 months

PR opened JakeLin/splitter

Reviewers
Add web UI to interact with the contract

In this PR, I have addressed the comments for PR #3 and added a web UI to interact with the contract.

Screen Shot 2019-08-13 at 10 42 48 pm

@xavierlepretre Can you please have a look again? thanks

+451 -139

0 comment

9 changed files

pr created time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha 0a7b5884f6702438e1447866d02ce3c79ad8d7e4

Update README.md

view details

push time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha 178495a56bf64608bb01d3830fcc0d3ad1323aea

[Web] - Refresh the contract balance after the operation.

view details

push time in 2 months

create barnchJakeLin/splitter

branch : feature/web

created branch time in 2 months

delete branch JakeLin/splitter

delete branch : feature/improve-contract-part4

delete time in 2 months

push eventJakeLin/splitter

Jake Lin

commit sha d1d0c401cdf8024dcdd52eb56adcfb4b745295eb

Add `Ownable` parent contract

view details

Jake Lin

commit sha aea25553741471afa0008fc6e46a6873dc4371f9

Use the `gasPrice` from the transaction.

view details

Jake Lin

commit sha 6ed226c095736a88baefb8a53e1fc0b87b62f2d5

Remove unnecessary `link` method

view details

Jake Lin

commit sha 3e62aa38a5abc5b696d662d15f006e524eea74fa

Merge pull request #4 from JakeLin/feature/improve-contract-part4 Improve contract part4 - @xavierlepretre thanks, I will address all your comments in the next PR with GUI.

view details

push time in 2 months

PR merged JakeLin/splitter

Improve contract part4

Address all comment for part3.

  1. Add Ownable parent contract
  2. Use the gasPrice from the transaction.
  3. Remove unnecessary link method

Can you please have a look again? @xavierlepretre thanks

+79 -14

0 comment

4 changed files

JakeLin

pr closed time in 2 months

PR opened JakeLin/splitter

Reviewers
Improve contract part4

Address all comment for part3.

  1. Add Ownable parent contract
  2. Use the gasPrice from the transaction.
  3. Remove unnecessary link method

Can you please have a look again? @xavierlepretre thanks

+79 -14

0 comment

4 changed files

pr created time in 2 months

create barnchJakeLin/splitter

branch : feature/improve-contract-part4

created branch time in 2 months

more