Skip to content

Commit

Permalink
Wrote first draft for accepting counter offers.
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Apr 26, 2024
1 parent b0e6138 commit 84b6dd9
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ forge clean
Run the tests:

```sh
forge test -vvv
clear && forge test -vvv
```

Or to run a single test (function):
Expand Down
72 changes: 54 additions & 18 deletions src/DecentralisedInvestmentManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,27 @@ import { TierInvestment } from "../src/TierInvestment.sol";
import { SaasPaymentProcessor } from "../src/SaasPaymentProcessor.sol";
import { DecentralisedInvestmentHelper } from "../src/Helper.sol";
import { CustomPaymentSplitter } from "../src/CustomPaymentSplitter.sol";
import { ReceiveCounterOffer } from "../src/ReceiveCounterOffer.sol";
import { Offer } from "../src/ReceiveCounterOffer.sol";

import "forge-std/src/console2.sol"; // Import the console library

// struct Offer {
// address payable _offerInvestor;
// uint256 _investmentAmount;
// uint16 _offerMultiplier;
// uint256 _offerDuration; // Time in seconds for project lead to decide
// uint256 _offerStartTime;
// bool _offerIsAccepted;
// }

interface Interface {
function receiveSaasPayment() external payable;

function receiveInvestment() external payable;

function receiveAcceptedOffer(address payable offerInvestor) external payable;

function withdraw(uint256 amount) external;

function getTierInvestmentLength() external returns (uint256 nrOfTierInvestments);
Expand Down Expand Up @@ -46,6 +60,8 @@ contract DecentralisedInvestmentManager is Interface {
// Custom attributes of the contract.
Tier[] private _tiers;

ReceiveCounterOffer private _receiveCounterOffer;

DecentralisedInvestmentHelper private _helper;

SaasPaymentProcessor private _saasPaymentProcessor;
Expand All @@ -55,9 +71,22 @@ contract DecentralisedInvestmentManager is Interface {
uint32 private _raisePeriod;
uint256 private _investmentTarget;

uint256 private _offerInvestmentAmount;
uint16 private _offerMultiplier;
uint256 private _offerDuration; // Time in seconds for project lead to decide
uint256 private _offerStartTime;
bool private _offerIsAccepted;

event PaymentReceived(address indexed from, uint256 indexed amount);
event InvestmentReceived(address indexed from, uint256 indexed amount);

// Modifier to check if the delay has passed and investment target is unmet
modifier onlyAfterDelayAndUnderTarget() {
require(block.timestamp >= _startTime + _raisePeriod, "The fund raising period has not passed yet.");
require(_cumReceivedInvestments < _investmentTarget, "Investment target reached!");
_; // Allows execution of the decorated (triggerReturnAll) function.
}

/**
* Constructor for creating a Tier instance. The values cannot be changed
* after creation.
Expand Down Expand Up @@ -114,13 +143,7 @@ contract DecentralisedInvestmentManager is Interface {
Tier tierOwnedByThisContract = new Tier(someMin, someMax, someMultiple);
_tiers.push(tierOwnedByThisContract);
}
}

// Modifier to check if the delay has passed and investment target is unmet
modifier onlyAfterDelayAndUnderTarget() {
require(block.timestamp >= _startTime + _raisePeriod, "The fund raising period has not passed yet.");
require(_cumReceivedInvestments < _investmentTarget, "Investment target reached!");
_; // Allows execution of the decorated (triggerReturnAll) function.
_receiveCounterOffer = new ReceiveCounterOffer(projectLead);
}

/**
Expand Down Expand Up @@ -232,6 +255,19 @@ contract DecentralisedInvestmentManager is Interface {
emit InvestmentReceived(msg.sender, msg.value);
}

function receiveAcceptedOffer(address payable offerInvestor) public payable override {
require(msg.value > 0, "The amount invested was not larger than 0.");

require(
!_helper.hasReachedInvestmentCeiling(_cumReceivedInvestments, _tiers),
"The investor ceiling is not reached."
);

_allocateInvestment(msg.value, offerInvestor);

emit InvestmentReceived(offerInvestor, msg.value);
}

function increaseCurrentMultipleInstantly(uint256 newMultiple) public override {
require(
msg.sender == _projectLead,
Expand Down Expand Up @@ -288,6 +324,17 @@ contract DecentralisedInvestmentManager is Interface {
return projectLeadFracNumerator;
}

// Function that can be called externally to trigger returnAll if conditions are met
function triggerReturnAll() public onlyAfterDelayAndUnderTarget {
// TODO: return all investments.
uint256 nrOfTierInvestments = _tierInvestments.length;
for (uint256 i = 0; i < nrOfTierInvestments; ++i) {
// Transfer the amount to the PaymentSplitter contract
payable(_tierInvestments[i].getInvestor()).transfer(_tierInvestments[i].getNewInvestmentAmount());
}
require(address(this).balance == 0, "After returning investments, there is still money in the contract.");
}

/**
@notice If the investment ceiling is not reached, it finds the lowest open
investment tier, and then computes how much can still be invested in that
Expand Down Expand Up @@ -381,15 +428,4 @@ contract DecentralisedInvestmentManager is Interface {
_paymentSplitter.publicAddSharesToPayee(receivingWallet, amount);
}
}

// Function that can be called externally to trigger returnAll if conditions are met
function triggerReturnAll() public onlyAfterDelayAndUnderTarget {
// TODO: return all investments.
uint256 nrOfTierInvestments = _tierInvestments.length;
for (uint256 i = 0; i < nrOfTierInvestments; ++i) {
// Transfer the amount to the PaymentSplitter contract
payable(_tierInvestments[i].getInvestor()).transfer(_tierInvestments[i].getNewInvestmentAmount());
}
require(address(this).balance == 0, "After returning investments, there is still money in the contract.");
}
}
89 changes: 89 additions & 0 deletions src/ReceiveCounterOffer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.23; // Specifies the Solidity compiler version.
import { Tier } from "../src/Tier.sol";
import { DecentralisedInvestmentManager } from "../../src/DecentralisedInvestmentManager.sol";
struct Offer {
address payable _offerInvestor;
uint256 _investmentAmount;
uint16 _offerMultiplier;
uint256 _offerDuration; // Time in seconds for project lead to decide
uint256 _offerStartTime;
bool _offerIsAccepted;
}

interface Interface {
function makeOffer(uint256 investmentAmount, uint16 multiplier, uint256 duration) external payable;

function getOffer(uint256 offerId) external returns (Offer memory);

function getOwner() external returns (address payable);

function acceptOrRejectOffer(uint256 offerId, bool accept) external;
}

contract ReceiveCounterOffer is Interface {
uint256 private _offerInvestmentAmount;
uint16 private _offerMultiplier;
uint256 private _offerDuration; // Time in seconds for project lead to decide
uint256 private _offerStartTime;
bool private _offerIsAccepted;
address payable private _owner;

Offer[] public offers;
address private _projectLead;

/**
* Used to ensure only the owner/creator of the constructor of this contract is
* able to call/use functions that use this function (modifier).
*/
modifier onlyOwner() {
require(msg.sender == _owner, "The message is sent by someone other than the owner of this contract.");
_;
}

/**
* Constructor for creating a Tier instance. The values cannot be changed
* after creation.
*
*/
constructor(address projectLead) public {
_owner = payable(msg.sender);
_projectLead = projectLead;
}

function makeOffer(uint256 investmentAmount, uint16 multiplier, uint256 duration) external payable override {
require(msg.value == investmentAmount, "Investment amount mismatch");

offers.push(Offer(payable(msg.sender), msg.value, multiplier, duration, block.timestamp, false));
}

function getOffer(uint256 offerId) public view override returns (Offer memory) {
require(offerId < offers.length, "Invalid offer ID");
return offers[offerId];
}

function getOwner() public view override returns (address payable) {
return _owner;
}

function acceptOrRejectOffer(uint256 offerId, bool accept) public override {
require(msg.sender == _projectLead, "Only project lead can accept offer");
// Offer memory offer ==
Offer memory offer = getOffer(offerId);
require(!offer._offerIsAccepted, "Offer already accepted");
require(block.timestamp <= offer._offerStartTime + offer._offerDuration, "Offer expired");

if (accept) {
offer._offerIsAccepted = true;
DecentralisedInvestmentManager dim = DecentralisedInvestmentManager(_owner);
// address(_owner).receiveAcceptedOffer{ value: offer._investmentAmount }(offer._offerInvestor);
dim.receiveAcceptedOffer{ value: offer._investmentAmount }(offer._offerInvestor);
// _owner.receiveSaasPayment{ value: offer._investmentAmount }();
// _owner.receiveSaasPayment.value(msg.value)(msg.sender);

// the transaction is rejected e.g. because the investmentCeiling is reached.
} else {
offer._offerIsAccepted = false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import { StdCheats } from "forge-std/src/StdCheats.sol";
// Import the main contract that is being tested.
import { DecentralisedInvestmentManager } from "../../src/DecentralisedInvestmentManager.sol";

// Import the paymentsplitter that has the shares for the investors.
import { CustomPaymentSplitter } from "../../src/CustomPaymentSplitter.sol";

interface Interface {
function setUp() external;

Expand Down

0 comments on commit 84b6dd9

Please sign in to comment.