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

Add ExpenditurePayoutClaimed event #1157

Merged
merged 3 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions contracts/colony/ColonyDataTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,18 @@ interface ColonyDataTypes {

event ArbitraryTransaction(address target, bytes data, bool success);

/// @notice Event logged when an expenditure payout is claimed.
/// @dev This is emitted in addition to the other PayoutClaimed
/// event. The other will be removed soon.
/// @param agent The address that is responsible for triggering this event
/// @param id Id of the expenditure
/// @param slot Expenditure slot of the payout claimed
/// @param token Token of the payout claim
/// @param tokenPayout Amount of the payout claimed, after network fee was deducted
event PayoutClaimed(address agent, uint256 id, uint256 slot, address token, uint256 tokenPayout);
kronosapiens marked this conversation as resolved.
Show resolved Hide resolved

// Structs

struct RewardPayoutCycle {
// Reputation root hash at the time of reward payout creation
bytes32 reputationState;
Expand Down
16 changes: 10 additions & 6 deletions contracts/colony/ColonyFunding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,9 @@ contract ColonyFunding is ColonyStorage { // ignore-swc-123
}

// Finish the payout
processPayout(expenditure.fundingPotId, _token, tokenPayout, slot.recipient);
uint256 payoutMinusFee = processPayout(expenditure.fundingPotId, _token, tokenPayout, slot.recipient);

emit PayoutClaimed(msgSender(), _id, _slot, _token, payoutMinusFee);
}

function setPaymentPayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, address _token, uint256 _amount) public
Expand Down Expand Up @@ -445,7 +447,7 @@ contract ColonyFunding is ColonyStorage { // ignore-swc-123
updatePayoutsWeCannotMakeAfterBudgetChange(task.fundingPotId, _token, currentTotalAmount);
}

function processPayout(uint256 _fundingPotId, address _token, uint256 _payout, address payable _user) private {
function processPayout(uint256 _fundingPotId, address _token, uint256 _payout, address payable _user) private returns (uint256 payoutToUser) {
refundDomain(_fundingPotId, _token);

IColonyNetwork colonyNetworkContract = IColonyNetwork(colonyNetworkAddress);
Expand All @@ -456,19 +458,19 @@ contract ColonyFunding is ColonyStorage { // ignore-swc-123
nonRewardPotsTotal[_token] -= _payout;

uint fee = isOwnExtension(_user) ? 0 : calculateNetworkFeeForPayout(_payout);
uint remainder = _payout - fee;
uint payoutToUser = _payout - fee;

if (_token == address(0x0)) {
// Payout ether
// Fee goes directly to Meta Colony
_user.transfer(remainder);
_user.transfer(payoutToUser);
metaColonyAddress.transfer(fee);
} else {
// Payout token
// If it's a whitelisted token, it goes straight to the metaColony
// If it's any other token, goes to the colonyNetwork contract first to be auctioned.
ERC20Extended payoutToken = ERC20Extended(_token);
assert(payoutToken.transfer(_user, remainder));
assert(payoutToken.transfer(_user, payoutToUser));
if (colonyNetworkContract.getPayoutWhitelist(_token)) {
assert(payoutToken.transfer(metaColonyAddress, fee));
} else {
Expand All @@ -477,7 +479,9 @@ contract ColonyFunding is ColonyStorage { // ignore-swc-123
}

// slither-disable-next-line reentrancy-unlimited-gas
emit PayoutClaimed(msgSender(), _fundingPotId, _token, remainder);
emit PayoutClaimed(msgSender(), _fundingPotId, _token, payoutToUser);

return payoutToUser;
}

function refundDomain(uint256 _fundingPotId, address _token) private {
Expand Down
22 changes: 22 additions & 0 deletions test/contracts-network/colony-expenditure.js
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,28 @@ contract("Colony Expenditure", (accounts) => {
expect(networkBalanceAfter.sub(networkBalanceBefore)).to.eq.BN(WAD.divn(100).addn(1)); // eslint-disable-line prettier/prettier
});

it("when claiming a payout, the appropriate events are emitted", async () => {
await colony.setExpenditureRecipient(expenditureId, SLOT0, RECIPIENT, { from: ADMIN });
await colony.setExpenditurePayout(expenditureId, SLOT0, token.address, WAD, { from: ADMIN });

const expenditure = await colony.getExpenditure(expenditureId);
await colony.moveFundsBetweenPots(
1,
UINT256_MAX,
1,
UINT256_MAX,
UINT256_MAX,
domain1.fundingPotId,
expenditure.fundingPotId,
WAD,
token.address
);
await colony.finalizeExpenditure(expenditureId, { from: ADMIN });
const tx = await colony.claimExpenditurePayout(expenditureId, SLOT0, token.address);
await expectEvent(tx, "PayoutClaimed", [accounts[0], expenditureId, SLOT0, token.address, WAD.divn(100).muln(99).subn(1)]);
await expectEvent(tx, "PayoutClaimed", [accounts[0], expenditure.fundingPotId, token.address, WAD.divn(100).muln(99).subn(1)]);
});

it("should allow anyone to claim on behalf of the slot, in multiple tokens", async () => {
await colony.setExpenditureRecipient(expenditureId, SLOT0, RECIPIENT, { from: ADMIN });
await colony.setExpenditurePayout(expenditureId, SLOT0, token.address, WAD, { from: ADMIN });
Expand Down