Skip to content

Commit

Permalink
Merge pull request #25 from semiotic-ai/5-should-we-allow-for-drainin…
Browse files Browse the repository at this point in the history
…g-any-remaining-balance-if-the-collateral-is-insufficient

feat(collateral-contract): allows max withdrawal for insufficient col…
  • Loading branch information
ColePBryan authored Jul 20, 2023
2 parents 5710422 + 66d6014 commit dc73543
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 27 deletions.
46 changes: 19 additions & 27 deletions src/Collateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ contract Collateral {

// Stores how much collateral each sender has deposited for each receiver, as well as thawing information
mapping(address sender => mapping(address reciever => CollateralAccount collateralAccount))
private collateralAccounts;
public collateralAccounts;
// Map of signer to authorized signer information
mapping(address signer => SenderAuthorization authorizedSigner)
private authorizedSigners;
public authorizedSigners;

// The ERC20 token used for collateral
IERC20 public immutable collateralToken;
Expand Down Expand Up @@ -103,12 +103,15 @@ contract Collateral {

/**
* @dev Emitted when collateral is redeemed by a receiver.
* @notice If the actual amount redeemed is less than the expected amount,
* there was insufficient collateral available to redeem.
*/
event Redeem(
address indexed sender,
address indexed receiver,
address indexed allocationID,
uint256 amount
uint256 expectedAmount,
uint256 actualAmount
);

/**
Expand Down Expand Up @@ -351,14 +354,12 @@ contract Collateral {
}

/**
* @dev Redeems collateral for a receiver using a signed RAV.
* @dev Redeems collateral (up to amount available in collateral) for a receiver using a signed RAV.
* @param signedRAV Signed RAV containing the receiver and collateral amount.
* @param allocationIDProof Proof of allocationID ownership.
* @notice REVERT: This function may revert if ECDSA.recover fails, check Open Zeppelin ECDSA library for details.
* @notice REVERT with error:
* - InvalidRAVSigner: If the RAV is signed by a signer who is not authorized by any sender
* - InsufficientCollateral: If the sender associated with the RAV signer has less collateral
* than the value of the RAV
* - AllocationIDTracker.AllocationIDPreviouslyClaimed: If the allocation ID was previously claimed
* - AllocationIDTracker.InvalidProof: If the allocation ID ownership proof is not valid
*/
Expand All @@ -374,15 +375,13 @@ contract Collateral {

address sender = authorizedSigners[signer].sender;
address receiver = msg.sender;
uint256 amount = signedRAV.rav.valueAggregate;
address allocationId = signedRAV.rav.allocationId;

if (collateralAccounts[sender][receiver].balance < amount) {
revert InsufficientCollateral({
available: collateralAccounts[sender][receiver].balance,
required: amount
});
}
// Amount is the minimum between the amount owed on rav and the actual balance
uint256 amount = signedRAV.rav.valueAggregate >
collateralAccounts[sender][receiver].balance
? collateralAccounts[sender][receiver].balance
: signedRAV.rav.valueAggregate;

unchecked {
collateralAccounts[sender][receiver].balance -= amount;
Expand All @@ -394,7 +393,13 @@ contract Collateral {
allocationIDProof
);
staking.collect(amount, allocationId);
emit Redeem(sender, msg.sender, signedRAV.rav.allocationId, amount);
emit Redeem(
sender,
msg.sender,
signedRAV.rav.allocationId,
signedRAV.rav.valueAggregate,
amount
);
}

/**
Expand All @@ -410,19 +415,6 @@ contract Collateral {
return collateralAccounts[sender][receiver].balance;
}

/**
* @dev Retrieves the collateral account details for a sender-receiver pair.
* @param sender Address of the sender.
* @param receiver Address of the receiver.
* @return The collateral account details.
*/
function getCollateralAccount(
address sender,
address receiver
) external view returns (CollateralAccount memory) {
return collateralAccounts[sender][receiver];
}

/**
* @dev Retrieves the collateral account details for a sender-receiver pair of the sender that a signer is authorized for.
* @param signer Address of the authorized signer.
Expand Down
35 changes: 35 additions & 0 deletions test/Collateral.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,41 @@ contract CollateralContractTest is Test {
assertEq(stakingBalanceAfter, stakingBalance + RAVAggregateAmount, "Incorrect receiver balance after redeeming");
}

function testRedeemRAVWithValueGreaterThanAvailableCollateral() public {
depositCollateral(SENDER_ADDRESS, receiverAddress, COLLATERAL_AMOUNT);

authorizeSignerWithProof(SENDER_ADDRESS, authorizedSignerPrivateKeys[0], authorizedsigners[0]);

// Create a signed rav
uint128 RAVAggregateAmount = 2 * uint128(COLLATERAL_AMOUNT);
uint64 timestampNs = 10;
TAPVerifier.SignedRAV memory signed_rav =
createSignedRAV(receiversAllocationIDs[0], timestampNs, RAVAggregateAmount, authorizedSignerPrivateKeys[0]);

// get number of tokens in staking contract account before redeeming
uint256 stakingBalance = mockERC20.balanceOf(address(staking));

// Receiver redeems value from the SignedRAV, expect receiver grt amount to increase
redeemSignedRAV(
receiversAllocationIDs[0],
receiversAllocationIDPrivateKeys[0],
receiverAddress,
SENDER_ADDRESS,
address(collateralContract),
signed_rav
);

assertEq(
collateralContract.getCollateralAmount(SENDER_ADDRESS, receiverAddress),
uint128(0),
"Incorrect remaining collateral"
);

// get number of tokens in staking contract account after redeeming and check that it increased by the amount of remaining sender collateral
uint256 stakingBalanceAfter = mockERC20.balanceOf(address(staking));
assertEq(stakingBalanceAfter, stakingBalance + COLLATERAL_AMOUNT, "Incorrect receiver balance after redeeming");
}

function testGetCollateralAmount() public {
depositCollateral(SENDER_ADDRESS, receiverAddress, COLLATERAL_AMOUNT);

Expand Down

0 comments on commit dc73543

Please sign in to comment.