Skip to content

Conversation

merisman
Copy link
Contributor

@merisman merisman commented Apr 8, 2025

No description provided.

@merisman merisman requested a review from Detoo April 8, 2025 20:02
Copy link
Contributor

@gpxl-dev gpxl-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments, a few subjective.

Most critical is that I don't think proposeAndSign can work?
Did not review tests.

@@ -199,7 +222,9 @@ contract CyberDealRegistry is Initializable, UUPSUpgradeable, BorgAuthACL {
string[] memory globalValues,
address[] memory parties,
string[] memory creatingPartyValues,
string[] memory counterPartyValues
string[] memory counterPartyValues,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes two parties - I wonder if we should account for more?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think so for dealmanager, escrow will only be for 2 parties.

string[] memory counterPartyValues
string[] memory counterPartyValues,
bytes32 secretHash,
address finalizer
) external returns (bytes32 contractId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a neater implementation would be:

  • have createContract always accept an array of arrays of party values: string[][] partyValues
  • check every array inside partyValues is the correct length, and check that the corresponding party is not the zero address.
  • loop through partyValues[] setting the mapping per the parties

This way you only have a single createContract function. If you wanted to have two explicity entry points, you could make it internal and call it _createContract, and keep the two functions you have that call through appropriately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, have made the string[][] param throughout and kept it all one create method.

@@ -55,6 +58,12 @@ contract CyberDealRegistry is Initializable, UUPSUpgradeable, BorgAuthACL {
// A mapping connecting an address to all the agreements they are a party to
mapping(address => bytes32[]) public agreementsForParty;

mapping(bytes32 => address[]) public voidedBy;

mapping(bytes32 => uint256) public expiry;
Copy link
Contributor

@gpxl-dev gpxl-dev Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expiry never gets set AFAICT.

Is there a good reason that voidedBy and expiry are not in the AgreementData struct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also for voidedBy if moved AgreementData, I prefer `mapping(address => bool) voidRequested.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added expiry (and secretHash) but kept the voidRequestedBy mapping outside -- I think this is probably preferable than nested mappings/arrays.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright. We still have signedAt as a mapping though, just poitning out it's inconsistent really

if(!isParty(contractId, party)) revert NotAParty();

AgreementData storage agreementData = agreements[contractId];
if (agreementData.finalized) revert ContractAlreadyFinalized();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since finalization is optional, I think we should probably check if there finalizer is a zero address, and if so, disallow voiding if the number of signatures is equal to the number of parties (i.e. it is fully signed)

That way you can't void if finalization of the contract is simply that it is fully signed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now set the finalized flag if finalizer is set to 0 and the contract is fully signed on the last signature.

if (keccak256(abi.encode(counterPartyCheck)) != keccak256(abi.encode(partyValues))) revert CounterPartyValueMismatch();
}

if(!conditionCheck(agreementId)) revert AgreementConditionsNotMet();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently nowhere to set conditions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to allow these to be set on creation to remove any chance of signing before they are added. Also added editing by the issuer if the deal is pending.

emit DealFinalized(
agreementId,
msg.sender,
CORP,
address(DEAL_REGISTRY),
_fillUnallocated
false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove this bool from the event I think - don't think it needs to be in here?

if(deal.status != EscrowStatus.PAID) revert EscrowNotPaid();
if(!ICyberDealRegistry(DEAL_REGISTRY).isVoided(agreementId)) revert DealNotVoided();

// Refund buyer assets first
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like something should be done with the corpAssets... I know in the case of the safe it will be burned, so this is ok for that... but wonder if we should still have the loop for corpassets regardless given we do in other functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Send them back to the printer contract, maybe? We won't burn the NFT's just void them.

Copy link
Contributor

@gpxl-dev gpxl-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments, a few subjective.

Most critical is that I don't think proposeAndSign can work?
Did not review tests.

Copy link
Contributor

@Detoo Detoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to think about @gpxl-dev 's comment on void workflow so flushing what I have for now

Escrow storage deal = escrows[agreementId];
for(uint256 i = 0; i < deal.corpAssets.length; i++) {
if(deal.corpAssets[i].tokenType == TokenType.ERC721) {
IIssuanceManager(ISSUANCE_MANAGER).voidCertificate(deal.corpAssets[i].tokenAddress, deal.corpAssets[i].tokenId);
}
}
voidEscrow(agreementId);
ICyberDealRegistry(DEAL_REGISTRY).voidContractFor(agreementId, signer, signature);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Missing expiry checks?

  2. Not sure if it is intentional: it will make voidExpiredDeal exclusive to party members only. It used to be open to anyone

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates: nvm, expiry checks is in voidContractFor();

however, what happens if none of the following conditions are met? (1) the number of void requests is equal to the number of parties, and (2) the expiry is in the past.

Wouldn't we end up with a weird state like this?

agreement.voided = false
escrow.state = VOIDED

Escrow storage deal = escrows[agreementId];
for(uint256 i = 0; i < deal.corpAssets.length; i++) {
if(deal.corpAssets[i].tokenType == TokenType.ERC721) {
IIssuanceManager(ISSUANCE_MANAGER).voidCertificate(deal.corpAssets[i].tokenAddress, deal.corpAssets[i].tokenId);
}
}
voidEscrow(agreementId);
ICyberDealRegistry(DEAL_REGISTRY).voidContractFor(agreementId, signer, signature);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates: nvm, expiry checks is in voidContractFor();

however, what happens if none of the following conditions are met? (1) the number of void requests is equal to the number of parties, and (2) the expiry is in the past.

Wouldn't we end up with a weird state like this?

agreement.voided = false
escrow.state = VOIDED

Copy link
Contributor

@Detoo Detoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed the merge of closed/open offers. A few minor suggestions

}
//matching address cannot be 0
if (parties[i] == address(0)) {
revert FirstPartyZeroAddress();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest refactoring it to PartyZeroAddress(uint256 index) due to its more general usage

}

if(agreementData.voided)
emit ContractVoided(contractId, voidRequestedBy[contractId], block.timestamp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it revert if agreementData not voided? Otherwise we'd end up with a non-void agreement + a voided escrow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants