**dpd-assembly review**
by @devtooligan
# **Repository.huff**
**(INFORMATIONAL)** **l2** Address.huff imported but not used
**(INFORMATIONAL)** **l5-18** Interface
- missing `owners(uint256)`
- incorrect fn sig sb `addDpd(uint256,bytes32,address,address)`
- extranneous fn `updateDpd(uint256,bytes32)`
- missing `updateDpdData(uint256, bytes32)`
- incorrect fn name `setDpdOwner` sb `updateDpdOwner`
- incorrect fn name `setDpdUpdater` sb `updateDpdUpdater`
- inccorect event sig sb `DPDUpdated(uint,bytes32)`
- incorrect event name `DPDOwnerChanged` sb `DPDOwnerUpdated`
- incorrect event name `DPDUpdaterChanged` sb `DPDUpdaterUpdated`
**(INFORMATIONAL)** **l25** Incorrect event sig in comment sb `DPDUpdated(uint,bytes32)`
**(INFORMATIONAL)** **l28-29** Should use correct fn name in comment and as constant name for consistency. sb `DPDOwnerUpdated` and `DPD_OWNER_UPDATED` respectively
**(INFORMATIONAL)** **l31-32** Should use correct fn name in comment and as constant name for consistency. sb `DPDUpdaterUpdated` and `DPD_UPDATER_UPDATED` respectively
**(INFORMATIONAL)** **l35** Unclear/inconsistent inline comment - Is there a reason only this one has a comment after?
**(INFORMATIONAL)** **l36** Unused storage pointer `DPD_ARRAY_LENGTH_SLOT` - related to l72
**(INFORMATIONAL)** **l44 & l49** Inconsistent comments - The two similar, subsequent view functions don't have the same comments as this fn.
**(INFORMATIONAL)** **l51** Inconsistent with subsequent 2 view fns which put the return on the next line.
**(MEDIUM)** **l71** Missing view functions:
- Consider adding a `GET_ARRAY` function that returns the entire array. [see Solcurity Standard V9](https://github.com/Rari-Capital/solcurity)
- Consider adding a `TOTAL_SUPPLY` function that returns the total number of DPD's (the length of the array).
**(CRITICAL)** **l74** `ADD_DPD`
Function comment says `Get the DPD array length, which serves as the DPD ID.` but in actuality, an index is passed in. **NOTE: SEE BELOW RECOMMENDATION FOR OVERHAULING THIS FUNCTION AND REPLACING WITH ADD_DPD() AND UPDATE_DPD()**
**(INFORMATIONAL)** **l95-104** For some reason in the stack comment `length*32, length` gets added to the end.
**(INFORMATIONAL)** **l117** Confirming we don't allow zero's for any of the fields `updater`, `data`, or `owner` (so no "burning" by sending to zero address).
**(INFORMATIONAL)** **l144** `UPDATE_DPD` - Consider renaming this to `UPDATE_DPD_DATA` or `UPDATE_DPD_CID` to be consistent.
**(HIGH)** **l144** We revert if any zeros are found during ADD_DPD but we allow zeros to go through here and in the other single attribute update fns. Recommend iszero check for all 3 update functions.
**(INFORMATIONAL)** **l158-l170** Consider abstracting this logic to a macro `ONLY_OWNER_OR_UPDATER`.
**(MEDIUM)** **l199** Recommend allowing owner to change `updater`.
**(INFORMATIONAL)** **l233** Consider allowing the `updater` to update the `owner` as well. This would allow for an approved `udpater` proxy to transfer the DPD on behalf of the owner.
**(GAS OPTIMIZATION)** **l260** The fns in MAIN() could be sorted, transactional first then sorted by most often called. View functions that would likely be viewed offchain can go last.
**RECOMMENDATION FOR OVERHAULING ADD_DPD()**
```
/* DPD Overwrite existing */
/// @dev 🔫 This low level fn has no auth, validation, or other error checking
#define macro SET_DPD() = takes(0) returns (0) {
// Store the DPD data.
0x24 calldataload // [data, dpd_id]
dup2 // [dpd_id, data, dpd_id]
[DPD_ARRAY_SLOT] // [salt, dpd_id, data, dpd_id]
STORE_ELEMENT_FROM_KEYS(0x00) // [dpd_id]
// Store the address of the owner.
0x44 calldataload // [owner, dpd_id]
dup2 // [dpd_id, owner, dpd_io]
[DPD_OWNER_MAP_SLOT] // [salt, dpd_id, owner, dpd_io]
STORE_ELEMENT_FROM_KEYS(0x00) // [dpd_id]
// Store the address of the updater.
0x64 calldataload // [updater, dpd_id]
dup2 // [dpd_id, updater, dpd_id]
[DPD_UPDATER_MAP_SLOT] // [salt, dpd_id, updater, dpd_id]
STORE_ELEMENT_FROM_KEYS(0x00) // [dpd_id]
/* DPD Add New */
#define macro ADD_DPD() = takes(0) returns (0) {
// Get the DPD array length, which serves as the DPD ID.
[DPD_ARRAY_LENGTH_SLOT] // [salt]
LOAD_ELEMENT(0x00) // [dpd_id]
dup1 // [dpd_id, dpd_id]
// Update array length
0x1 add // [dpd_id + 1, dpd_id]
[DPD_ARRAY_LENGTH_SLOT] // [salt, dpd_id + 1, dpd_id]
STORE_ELEMENT(0x00) // [dpd_id]
// Checks
ID_CHECKS(error) // [dpd_id]
// Emit the event.
// DPDAdded(uint256 indexed dpdId, address owner, address updater, bytes32 cid)
dup1 // [dpd_id, dpd_id]
[DPD_ADDED_EVENT_SIGNATURE] // [sig, dpd_id, dpd_id]
0x44 calldataload 0x00 mstore // [sig, dpd_id, dpd_id]
0x64 calldataload 0x20 mstore // [sig, dpd_id, dpd_id]
0x24 calldataload 0x40 mstore // [sig, dpd_id, dpd_id]
0x60 0x00 // [offset, size, sig, dpd_id, dpd_id]
log2 // [dpd_id]
SET_DPD() // [dpd_id]
// Stop the program.
stop
// Revert if the checks are wrong.
error:
0x00 0x00 revert
}
/* DPD Edit */
#define macro UPDATE_DPD() = takes(0) returns (0) {
0x04 calldataload // [dpd_id]
// Checks
// by checking isOwner we also get an in bounds check
ONLY_OWNER_OR_UPDATER() // [dpi_id]
ID_CHECKS(error) // [dpd_id]
// Emit the event.
// DPDAdded(uint256 indexed dpdId, address owner, address updater, bytes32 cid)
dup1 // [dpd_id, dpd_id]
[DPD_UPDATED_EVENT_SIGNATURE] // [sig, dpd_id, dpd_id]
0x44 calldataload 0x00 mstore // [sig, dpd_id, dpd_id]
0x64 calldataload 0x20 mstore // [sig, dpd_id, dpd_id]
0x24 calldataload 0x40 mstore // [sig, dpd_id, dpd_id]
0x60 0x00 // [offset, size, sig, dpd_id, dpd_id]
log2 // [dpd_id]
SET_DPD() // [dpd_id]
// Stop the program.
stop
// Revert if the checks are wrong.
error:
0x00 0x00 revert
}
```
# **Repository.t.sol -- TESTS**
**Interface**
- **(INFORMATIONAL)** Recommend to split up the interface into events and functions. Not only does it make it easier to import events into the test ctrct, but [there's an argument for not including events in interfaces](https://twitter.com/alcueca/status/1466152649015836673?s=20&t=iaEVlpqN6NEWCsVi_c2NSg). Rename interface `Repository` to `IRepository` and move all events from the newly renamed `IRepository` to a new abstract contract `RepositoryEvents`. Can move these two to a new file or to the top of test file.
- **(INFORMATIONAL)** Have the test contract inherit `RepositoryEvents` and delete the duped events currently on **l9-l12** within the test contract.
- **(MEDIUM)** All `address` args in all events should be indexed. This will mean updating the `log` code in the contract and the `vm.expectEmit` code in the tests as well. [see Solcurity Standard E5](https://github.com/Rari-Capital/solcurity)
- **(INFORMATIONAL)** Incorrect natspec for event`DPDUpdated` -- sb `Event emitted when a DPD's CID is updated.`
- **(INFORMATIONAL)** Typo in natspec for event`DPDUpdated` -- "upgrader"
- **(INFORMATIONAL)** Consider adding the param names to the signatures for readability like in contract in comment on l79 `DPDAdded(uint256 indexed dpdId, address owner, address updater, bytes32 cid)`
**Tests**
- **(HIGH)** Auth checks are not tested.
- **(MEDIUM)** View fns not tested.
- **(HIGH)** Unit testing (or fuzzing) for ID_CHECKS.
- **(MEDIUM)** Currently only one event `DPDAdded` is tested. There should be at least 1 test for each event.
- **(MEDIUM)** Ensure that there is a test case for every where that the contract could revert.
- **(INFORMATIONAL)** The setup for the `vm.expectRevert` is incorrect in its current state. Since there is only 1 topic area so it should be `vm.expectRevert(true, false, false, true)`
- **(INFORMATIONAL)** The assertion made in the comment on **l45** is not entirely true. `@dev Ensure that you cannot create two DPDs with the same ID.` You can create a DPD with id of 1 and update all fields to zeros for `data`, `owner`, and `updater`. You could then call `ADD_DPD` again with the same dpd_id and it would not revert. Prefer `vm.expectRevert` to `testFail` for tighter control and less false positives.