Skip to content

Commit

Permalink
add natspec and comments to DirectDepositLogic
Browse files Browse the repository at this point in the history
  • Loading branch information
0age committed Nov 5, 2024
1 parent 4e8ec08 commit 0578a1b
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 9 deletions.
3 changes: 1 addition & 2 deletions src/interfaces/ITheCompact.sol
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ interface ITheCompact {
* approve The Compact to transfer sufficient amounts on its behalf. The ERC6909 token amounts
* received by the recipient are derived from the differences between starting and ending
* balances held in the resource locks, which may differ from the amounts transferred depending
* on the implementation details of the respective tokens. Note that resouce lock ids must be
* supplied in alphanumeric order.
* on the implementation details of the respective tokens.
* @param idsAndAmounts Array of [id, amount] pairs with each pair indicating the resource lock and amount to deposit.
* @param recipient The address that will receive the corresponding ERC6909 tokens.
* @return Whether the batch deposit was successfully completed.
Expand Down
125 changes: 118 additions & 7 deletions src/lib/DirectDepositLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,57 @@ contract DirectDepositLogic is DepositLogic {
using ValidityLib for address;
using SafeTransferLib for address;

/**
* @notice Internal function for depositing native tokens into a resource lock and
* receiving back ERC6909 tokens representing the underlying locked balance controlled
* by the depositor. The allocator mediating the lock is provided as an argument, and the
* default reset period (ten minutes) and scope (multichain) will be used for the resource
* lock. The ERC6909 token amount received by the caller will match the amount of native
* tokens sent with the transaction.
* @param allocator The address of the allocator.
* @return id The ERC6909 token identifier of the associated resource lock.
*/
function _performBasicNativeTokenDeposit(address allocator) internal returns (uint256 id) {
// Derive the resource lock ID using the null address and default parameters.
id = address(0).toIdIfRegistered(Scope.Multichain, ResetPeriod.TenMinutes, allocator);

// Mint ERC6909 tokens to caller using derived ID and supplied native tokens.
_deposit(msg.sender, id, msg.value);
}

/**
* @notice Internal function for depositing multiple tokens in a single transaction. The
* first entry in idsAndAmounts can optionally represent native tokens by providing the null
* address and an amount matching msg.value. For ERC20 tokens, the caller must directly
* approve The Compact to transfer sufficient amounts on its behalf. The ERC6909 token amounts
* received by the recipient are derived from the differences between starting and ending
* balances held in the resource locks, which may differ from the amounts transferred depending
* on the implementation details of the respective tokens.
* @param idsAndAmounts Array of [id, amount] pairs with each pair indicating the resource lock and amount to deposit.
* @param recipient The address that will receive the corresponding ERC6909 tokens.
*/
function _processBatchDeposit(uint256[2][] calldata idsAndAmounts, address recipient) internal {
// Set reentrancy guard.
_setReentrancyGuard();

// Retrieve the total number of IDs and amounts in the batch.
uint256 totalIds = idsAndAmounts.length;
bool firstUnderlyingTokenIsNative;

// Declare variables for ID, amount, and whether first token is native.
uint256 id;
uint256 amount;
bool firstUnderlyingTokenIsNative;

assembly ("memory-safe") {
// Determine the offset of idsAndAmounts in calldata.
let idsAndAmountsOffset := idsAndAmounts.offset

// Load the first ID from idsAndAmounts.
id := calldataload(idsAndAmountsOffset)

// Determine if token encoded in first ID is the null address.
firstUnderlyingTokenIsNative := iszero(shr(96, shl(96, id)))

// Revert if:
// * the array is empty
// * the callvalue is zero but the first token is native
Expand All @@ -54,66 +89,142 @@ contract DirectDepositLogic is DepositLogic {
}
}

// Derive current allocator ID from first resource lock ID.
uint96 currentAllocatorId = id.toRegisteredAllocatorId();

// Declare variable for subsequent allocator IDs.
uint96 newAllocatorId;

// Deposit native tokens directly if first underlying token is native.
if (firstUnderlyingTokenIsNative) {
_deposit(recipient, id, msg.value);
}

// Iterate over remaining IDs and amounts.
unchecked {
for (uint256 i = firstUnderlyingTokenIsNative.asUint256(); i < totalIds; ++i) {
// Navigate to the current ID and amount pair in calldata.
uint256[2] calldata idAndAmount = idsAndAmounts[i];

// Retrieve the current ID and amount.
id = idAndAmount[0];
uint256 amount = idAndAmount[1];
amount = idAndAmount[1];

uint96 newAllocatorId = id.toAllocatorId();
// Derive new allocator ID from current resource lock ID.
newAllocatorId = id.toAllocatorId();

// Determine if new allocator ID differs from current allocator ID.
if (newAllocatorId != currentAllocatorId) {
// Ensure new allocator ID is registered.
newAllocatorId.mustHaveARegisteredAllocator();

// Update current allocator ID.
currentAllocatorId = newAllocatorId;
}

// Transfer underlying tokens in and mint ERC6909 tokens to recipient.
_transferAndDeposit(id.toToken(), recipient, id, amount);
}
}

// Clear reentrancy guard.
_clearReentrancyGuard();
}

/**
* @notice Internal function for depositing native tokens into a resource lock and
* receiving back ERC6909 tokens representing the underlying locked balance controlled
* by the depositor. The allocator mediating the lock is provided as an argument, and the
* default reset period (ten minutes) and scope (multichain) will be used for the resource
* lock. The ERC6909 token amount received by the caller will match the amount of native
* tokens sent with the transaction.
* @param allocator The address of the allocator.
* @return id The ERC6909 token identifier of the associated resource lock.
*/
function _performBasicERC20Deposit(address token, address allocator, uint256 amount) internal returns (uint256 id) {
// Derive resource lock ID using provided token, default parameters, and allocator.
id = token.excludingNative().toIdIfRegistered(Scope.Multichain, ResetPeriod.TenMinutes, allocator);

// Transfer underlying tokens in and mint ERC6909 tokens to caller.
_transferAndDepositWithReentrancyGuard(token, msg.sender, id, amount);
}

/**
* @notice Internal function for depositing native tokens into a resource lock with custom
* reset period and scope parameters. The ERC6909 token amount received by the recipient
* will match the amount of native tokens sent with the transaction.
* @param allocator The address of the allocator mediating the resource lock.
* @param resetPeriod The duration after which the resource lock can be reset once a forced withdrawal is initiated.
* @param scope The scope of the resource lock (multichain or single chain).
* @param recipient The address that will receive the corresponding ERC6909 tokens.
* @return id The ERC6909 token identifier of the associated resource lock.
*/
function _performCustomNativeTokenDeposit(address allocator, ResetPeriod resetPeriod, Scope scope, address recipient) internal returns (uint256 id) {
// Derive resource lock ID using null address, provided parameters, and allocator.
id = address(0).toIdIfRegistered(scope, resetPeriod, allocator);

// Deposit native tokens and mint ERC6909 tokens to recipient.
_deposit(recipient, id, msg.value);
}

/**
* @notice Internal function for depositing ERC20 tokens into a resource lock with custom reset
* period and scope parameters. The caller must directly approve The Compact to transfer a
* sufficient amount of the ERC20 token on its behalf. The ERC6909 token amount received by
* the recipient is derived from the difference between the starting and ending balance held
* in the resource lock, which may differ from the amount transferred depending on the
* implementation details of the respective token.
* @param token The address of the ERC20 token to deposit.
* @param allocator The address of the allocator mediating the resource lock.
* @param resetPeriod The duration after which the resource lock can be reset once a forced withdrawal is initiated.
* @param scope The scope of the resource lock (multichain or single chain).
* @param amount The amount of tokens to deposit.
* @param recipient The address that will receive the corresponding ERC6909 tokens.
* @return id The ERC6909 token identifier of the associated resource lock.
*/
function _performCustomERC20Deposit(address token, address allocator, ResetPeriod resetPeriod, Scope scope, uint256 amount, address recipient) internal returns (uint256 id) {
// Derive resource lock ID using provided token, parameters, and allocator.
id = token.excludingNative().toIdIfRegistered(scope, resetPeriod, allocator);

// Transfer ERC20 tokens in and mint ERC6909 tokens to recipient.
_transferAndDepositWithReentrancyGuard(token, recipient, id, amount);
}

/// @dev Transfers `amount` of `token` and mints the resulting balance change of `id` to `to`.
/// Emits a {Transfer} event.
/**
* @notice Private function for transferring ERC20 tokens in and minting the resulting balance
* change of `id` to `to`. Emits a Transfer event.
* @param token The address of the ERC20 token to transfer.
* @param to The address that will receive the corresponding ERC6909 tokens.
* @param id The ERC6909 token identifier of the associated resource lock.
* @param amount The amount of tokens to transfer.
*/
function _transferAndDeposit(address token, address to, uint256 id, uint256 amount) private {
// Retrieve initial token balance of this contract.
uint256 initialBalance = token.balanceOf(address(this));

// Transfer tokens from caller to this contract.
token.safeTransferFrom(msg.sender, address(this), amount);

// Compare new balance to initial balance and deposit ERC6909 tokens to recipient.
_checkBalanceAndDeposit(token, to, id, initialBalance);
}

/// @dev Transfers `amount` of `token` and mints the resulting balance change of `id` to `to`.
/// Emits a {Transfer} event.
/**
* @notice Private function for transferring ERC20 tokens in and minting the resulting balance
* change of `id` to `to`. Emits a Transfer event.
* @param token The address of the ERC20 token to transfer.
* @param to The address that will receive the corresponding ERC6909 tokens.
* @param id The ERC6909 token identifier of the associated resource lock.
* @param amount The amount of tokens to transfer.
*/
function _transferAndDepositWithReentrancyGuard(address token, address to, uint256 id, uint256 amount) private {
// Set reentrancy guard.
_setReentrancyGuard();

// Transfer tokens in and mint ERC6909 tokens to recipient.
_transferAndDeposit(token, to, id, amount);

// Clear reentrancy guard.
_clearReentrancyGuard();
}
}

0 comments on commit 0578a1b

Please sign in to comment.