Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create DummyCollateralizedContract for test #24

Merged
merged 41 commits into from
Feb 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1859e06
Create `DummyCollateralizedContract` for test
saturnial Feb 24, 2018
0702923
Fix constructor
saturnial Feb 24, 2018
a79d3ed
Unit test scaffolding for Collateralized Contract
saturnial Feb 25, 2018
5bb605a
Each invariant stands on its own for readability purposes
saturnial Feb 25, 2018
c905b45
Scaffold high level test groupings
saturnial Feb 25, 2018
4b42a30
Tests invariants for `collateralize`
saturnial Feb 25, 2018
4401b5e
Create mock for `CollateralLocked` event to be used in test
saturnial Feb 25, 2018
125c5dd
Test successful collateralization
saturnial Feb 25, 2018
3ade397
Rely on `transferFrom` not `transfer`
saturnial Feb 25, 2018
f464427
Provide dummy methods to manipulate the state of the `DummyCollateral…
saturnial Feb 25, 2018
373b5d9
Clean up imports
saturnial Feb 25, 2018
f99037e
Add tests to #returnCollateral #invariants
saturnial Feb 25, 2018
ff40a2b
Remove scoped instances of `collateralAmount`
saturnial Feb 26, 2018
3ae21b0
Add mock for `CollateralReturned` event
saturnial Feb 26, 2018
6d13f90
Remove inaccurate comment
saturnial Feb 26, 2018
739b493
Add tests for the successful return of collateral
saturnial Feb 26, 2018
bbda3e2
Write to the collateral object in the mapping, not the one in our loc…
saturnial Feb 26, 2018
6e31c6b
Add test to ensure that all subsequent calls to `returnCollateral` th…
saturnial Feb 26, 2018
47651b2
Make each requirement explicit and separate for readability
saturnial Feb 26, 2018
dfc7e2b
`transfer` -> `transferFrom`
saturnial Feb 26, 2018
fb63ff1
Write to storage, not local context
saturnial Feb 26, 2018
4324ae0
Add mock for `CollateralSeized` event
saturnial Feb 26, 2018
eadc84f
Add tests for #seizeCollateral #invariants
saturnial Feb 26, 2018
aa26616
Test the successful seizure of collateral
saturnial Feb 26, 2018
91da3ea
Update it description to be more precise
saturnial Feb 26, 2018
2f39d34
Document why we ensure lockup period is 0 before collateralizing a co…
saturnial Feb 26, 2018
7f7abc8
Document contract instantiation pipeline
saturnial Feb 26, 2018
8df86b3
`transfer` not `transferFrom` for `seizeCollateral` and `returnCollat…
saturnial Feb 26, 2018
500fcde
Remove unnecessary setup
saturnial Feb 26, 2018
b4c98a8
add test to ensure subsequent calls to `collateralize` throw
saturnial Feb 26, 2018
7a89e6d
Ensure balances and allowances are sufficient
saturnial Feb 26, 2018
47b6c32
`CONTRACT_OWNER` -> `CUSTODIAN`
saturnial Feb 26, 2018
6cc27be
Remove duplicate test
saturnial Feb 26, 2018
7dfa825
Add ability to check calls to `balanceOf` in our mock ERC20 token
saturnial Feb 27, 2018
62c7b29
Add ability to check calls to `allowance` in our mock ERC20 token
saturnial Feb 27, 2018
09568da
Add tests to ensure allowances and balances are being heeded
saturnial Feb 27, 2018
992f38c
Revert "`CONTRACT_OWNER` -> `CUSTODIAN`"
saturnial Feb 27, 2018
915a659
Tests should not be dependent on order of invariants
saturnial Feb 27, 2018
dd8ade0
Merge pull request #29 from dharmaprotocol/collateral-unit-tests-review
nadavhollander Feb 27, 2018
683fab7
Fixed review comments and fixed test errors
nadavhollander Feb 27, 2018
6c34782
Merge pull request #28 from dharmaprotocol/collateral-unit-tests
nadavhollander Feb 27, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 65 additions & 30 deletions contracts/examples/Collateralized.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,41 @@ contract Collateralized is TermsContract {
)
public
{
// validate amount and lockup period.
require(amount > 0 && lockupPeriodEndTimestamp > block.timestamp);
// the amount being put up for collateral must exceed 0.
require(amount > 0);

// ensure that the agreement has not already been collateralized.
// the lockup period must occur at some point in the future.
require(lockupPeriodEndTimestamp > block.timestamp);

/*
Ensure that the agreement has not already been collateralized.

If the agreement has already been collateralized, this check will fail
because any valid form of collateral will have a non-zero lockupPeriod.
Only an uncollateralized agreement would meet this requirement.
*/
require(collateralForAgreementID[agreementID].lockupPeriod == 0);

// take tokens as collateral.
require(ERC20(token).transferFrom(
msg.sender,
address(this),
ERC20 erc20token = ERC20(token);
address collateralizer = msg.sender;
address custodian = address(this);

/*
The collateralizer must have sufficient balance equal to or greater
than the amount being put up for collateral.
*/
require(erc20token.balanceOf(collateralizer) >= amount);

/*
The custodian must have an allowance granted by the collateralizer equal
to or greater than the amount being put up for collateral.
*/
require(erc20token.allowance(collateralizer, custodian) >= amount);

// the collateral must be successfully transferred to this contract.
require(erc20token.transferFrom(
collateralizer,
custodian,
amount
));

Expand All @@ -106,30 +131,35 @@ contract Collateralized is TermsContract {
)
public
{
// fetch collateral object.
// fetch relevant collateral instance.
Collateral memory collateral = collateralForAgreementID[agreementID];

// check if collateral is not empty, lockupPeriod is over and not withdrawn
require(
collateral.lockupPeriod > 0 &&
block.timestamp > collateral.lockupPeriod &&
!collateral.withdrawn
);
// Ensure a valid form of collateral is tied to this agreement id.
require(collateral.lockupPeriod > 0);

// Collateral can only be returned after the lockup period.
require(block.timestamp > collateral.lockupPeriod);

// Collateral can only be returned if it has yet to be withdrawn.
require(!collateral.withdrawn);

// ensure sufficient payment.
require(
getExpectedRepaymentValue(agreementID, block.timestamp) <=
getValueRepaidToDate(agreementID)
);

// transfer collateral back to sender.
// mark collateral as withdrawn.
collateralForAgreementID[agreementID].withdrawn = true;

// transfer the collateral this contract was holding in escrow back to sender.
require(
ERC20(collateral.token).transfer(collateral.collateralizer, collateral.amount)
ERC20(collateral.token).transfer(
collateral.collateralizer,
collateral.amount
)
);

// mark collateral as withdrawn.
collateral.withdrawn = true;

// log that the collateral has been succesfully returned to collateralizer.
CollateralReturned(
agreementID,
Expand All @@ -144,33 +174,38 @@ contract Collateralized is TermsContract {
)
public
{
// fetch collateral object.
// fetch relevant collateral instance.
Collateral memory collateral = collateralForAgreementID[agreementID];

// check if collateral is not empty, lockupPeriod is over and not withdrawn.
require(
collateral.lockupPeriod > 0 &&
block.timestamp > collateral.lockupPeriod &&
!collateral.withdrawn
);
// Ensure a valid form of collateral is tied to this agreement id.
require(collateral.lockupPeriod > 0);

// Seizure can only occur after the lockup period.
require(block.timestamp > collateral.lockupPeriod);

// Seizure can only occur if the collateral has yet to be withdrawn.
require(!collateral.withdrawn);

// ensure debtor is in violation of the terms.
require(
getExpectedRepaymentValue(agreementID, block.timestamp) >
getValueRepaidToDate(agreementID)
);

// mark collateral as withdrawn once transfer has succeeded.
collateralForAgreementID[agreementID].withdrawn = true;

// determine beneficiary of the seized collateral.
address beneficiary = debtRegistry.getBeneficiary(agreementID);

// seize collateral and transfer to beneficiary.
require(
ERC20(collateral.token).transfer(beneficiary, collateral.amount)
ERC20(collateral.token).transfer(
beneficiary,
collateral.amount
)
);

// mark collateral as withdrawn once transfer has succeeded.
collateral.withdrawn = true;

// log the seizure event.
CollateralSeized(
agreementID,
Expand Down
73 changes: 73 additions & 0 deletions contracts/test/DummyCollateralizedContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
pragma solidity 0.4.18;

import "zeppelin-solidity/contracts/math/SafeMath.sol";

import "../examples/Collateralized.sol";

contract DummyCollateralizedContract is Collateralized {
using SafeMath for uint;

mapping(bytes32 => uint) internal amountRepaid;
mapping(bytes32 => uint) internal expectedRepaymentValue;

function DummyCollateralizedContract(address _debtRegistry) Collateralized(_debtRegistry) public {}

/* Naive `TermsContract` interface implementation. */

function registerRepayment(
bytes32 agreementId,
address payer,
address beneficiary,
uint256 unitsOfRepayment,
address tokenAddress
) public returns (bool _success) {
return false;
}

function getExpectedRepaymentValue(
bytes32 agreementId,
uint256 timestamp
) public view returns (uint256) {
return expectedRepaymentValue[agreementId];
}

function getValueRepaidToDate(
bytes32 agreementId
) public view returns (uint256) {
return amountRepaid[agreementId];
}

/* Dummy functionality used to mock behavior for testing purposes. */

function setDummyExpectedRepaymentValue(
bytes32 agreementId,
uint amount
) public {
expectedRepaymentValue[agreementId] = amount;
}

function setDummyValueRepaid(
bytes32 agreementId,
uint amount
) public {
amountRepaid[agreementId] = amount;
}

function setDummyCollateral(
bytes32 agreementID,
address collateralizer,
address token,
uint amount,
uint lockupPeriodEndTimestamp,
bool withdrawn
) public {
collateralForAgreementID[agreementID] = Collateral({
collateralizer: collateralizer,
token: token,
amount: amount,
lockupPeriod: lockupPeriodEndTimestamp,
withdrawn: withdrawn
});
}

}
4 changes: 4 additions & 0 deletions contracts/test/mocks/MockERC20Token.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ contract MockERC20Token is MockContract {
));
}

/* `balanceOf` */

function balanceOf(address _owner) public view returns(uint _balance) {
return uint(getMockReturnValue("balanceOf", keccak256(_owner)));
}
Expand All @@ -93,6 +95,8 @@ contract MockERC20Token is MockContract {
mockReturnValue("balanceOf", keccak256(_owner), bytes32(_balance));
}

/* `allowance` */

function allowance(address _owner, address _to) public view returns(uint _allowance) {
return uint(getMockReturnValue("allowance", keccak256(_owner, _to)));
}
Expand Down
65 changes: 65 additions & 0 deletions test/ts/logs/collateralized_contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as ABIDecoder from "abi-decoder";
import * as BigNumber from "bignumber.js";
import * as LogUtils from "./log_utils";

import {
Address,
Bytes32,
UInt
} from "../../../types/common";


export function CollateralLocked(
contract: Address,
agreementID: Bytes32,
token: Address,
amount: UInt
): ABIDecoder.DecodedLog {
return {
address: contract,
events: LogUtils.getParams([
["agreementID", agreementID],
["token", token],
["amount", amount],
]),
name: "CollateralLocked",
};
}

export function CollateralReturned(
contract: Address,
agreementID: Bytes32,
collateralizer: Address,
token: Address,
amount: UInt
): ABIDecoder.DecodedLog {
return {
address: contract,
events: LogUtils.getParams([
["agreementID", agreementID],
["collateralizer", collateralizer],
["token", token],
["amount", amount],
]),
name: "CollateralReturned",
};
}

export function CollateralSeized(
contract: Address,
agreementID: Bytes32,
beneficiary: Address,
token: Address,
amount: UInt
): ABIDecoder.DecodedLog {
return {
address: contract,
events: LogUtils.getParams([
["agreementID", agreementID],
["beneficiary", beneficiary],
["token", token],
["amount", amount],
]),
name: "CollateralSeized",
};
}
Loading