Skip to content

Commit

Permalink
chore: move getCallValues() to non-constant functions
Browse files Browse the repository at this point in the history
  • Loading branch information
IaroslavMazur committed Aug 28, 2024
1 parent 110ff4a commit 1d7539c
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 25 deletions.
49 changes: 24 additions & 25 deletions src/precompiles/native-tokens/NativeTokens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,6 @@ library NativeTokens {
return abi.decode(returnData, (uint256));
}

/// @notice Returns the token ids and amounts of the Native Tokens transferred in the context of the current
/// contract call.
///
/// Requirements:
/// - The caller of this function must be a contract.
///
/// @return tokenIDs The IDs of the transferred Native Tokens.
/// @return amounts The amounts of the transferred Native Tokens.
function getCallValues(address notUsed) internal returns (uint256[] memory, uint256[] memory) {
notUsed;
// ABI encode the input parameters.
bytes memory callData = abi.encodeCall(INativeTokens.getCallValues, ());

// Call the precompile.
(bool success, bytes memory returnData) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);

// This is an unexpected error since the VM should have panicked if the call failed.
if (!success) {
revert StdLib_UnknownError("NativeTokens: getCallValues failed");
}

// Decode the return data.
return abi.decode(returnData, (uint256[], uint256[]));
}

/*//////////////////////////////////////////////////////////////////////////
USER-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
Expand All @@ -78,6 +53,30 @@ library NativeTokens {
response;
}

/// @notice Returns the token ids and amounts of the Native Tokens transferred in the context of the current
/// contract call.
///
/// Requirements:
/// - The caller of this function must be a contract.
///
/// @return tokenIDs The IDs of the transferred Native Tokens.
/// @return amounts The amounts of the transferred Native Tokens.
function getCallValues(address /*notUsed*/ ) internal returns (uint256[] memory, uint256[] memory) {
// ABI encode the input parameters.
bytes memory callData = abi.encodeCall(INativeTokens.getCallValues, ());

// Call the precompile.
(bool success, bytes memory returnData) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);

// This is an unexpected error since the VM should have panicked if the call failed.
if (!success) {
revert StdLib_UnknownError("NativeTokens: getCallValues failed");
}

// Decode the return data.
return abi.decode(returnData, (uint256[], uint256[]));
}

/// @notice Mints `amount` tokens with sub-identifier `subID` to the provided `recipient`.
/// @dev Generates a Mint receipt.
///
Expand Down
64 changes: 64 additions & 0 deletions src/test-utils/ContractToTransferAndCallTo.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.12;

import { NativeTokens } from "../precompiles/native-tokens/NativeTokens.sol";

/// @notice A mock contract playing the role of the "receiving" side in the single-
/// and multi-token transfer-and-call tests
contract ContractToTransferAndCallTo {
using NativeTokens for address;

function transferTokenForAFee(address recipient, uint256 tokenID, uint256 amount, uint256 fee) external payable {
require(fee < amount, "Fee must be less than the amount to transfer");

// Make sure the caller has transferred enough tokenID tokens
(uint256[] memory tokenIds, uint256[] memory tokenAmounts) = address(this).getCallValues();
require(tokenIds.length == 1 && tokenAmounts.length == 1, "Caller must have transferred exactly one token");
require(tokenIds[0] == tokenID && tokenAmounts[0] == amount, "Caller must transfer the token to the callee");

// Transfer the token to the provided recipient.
recipient.transfer(tokenID, amount - fee);
}

function transferMultipleTokensForAFee(
address recipient,
uint256[] calldata tokenIDs,
uint256[] calldata amounts,
uint256 fee
)
external
payable
{
require(tokenIDs.length == amounts.length, "Token IDs and amounts must have the same length");

for (uint256 i = 0; i < tokenIDs.length; i++) {
require(fee < amounts[i], "Fee must be less than the amount to transfer");

// Make sure the caller has transferred amounts[i] worth of the tokenIDs[i] token
require(
enoughTokensTransferred(tokenIDs[i], amounts[i], tokenIDs, amounts), "Not enough tokens transferred"
);

// Transfer the token to the provided recipient.
recipient.transfer(tokenIDs[i], amounts[i] - fee);
}
}

function enoughTokensTransferred(
uint256 tokenId,
uint256 tokenAmount,
uint256[] memory tokenIds,
uint256[] memory tokenAmounts
)
internal
pure
returns (bool)
{
for (uint256 i = 0; i < tokenIds.length; i++) {
if (tokenIds[i] == tokenId && tokenAmounts[i] == tokenAmount) {
return true;
}
}
return false;
}
}
49 changes: 49 additions & 0 deletions src/test-utils/NaiveTokenTransferrerMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.12;

import { NativeTokens } from "../precompiles/native-tokens/NativeTokens.sol";

/// @notice Dummy mock contract for testing the individual- and multi-token transfer functionalities
contract NaiveTokenTransferrerMock {
using NativeTokens for address;

function getBalanceOfToken(address account, uint256 tokenID) external view returns (uint256) {
return account.balanceOf(tokenID);
}

function getCallValues() external payable returns (uint256[] memory, uint256[] memory) {
// TODO: make the getCallValues() function callable directly from the Precompile, w/o having to call it on an
// address?
return address(this).getCallValues();
}

function transfer(address to, uint256 tokenID, uint256 amount) external {
to.transfer(tokenID, amount);
}

function transferAndCall(
address recipientAndCallee,
uint256 tokenID,
uint256 amount,
bytes calldata data
)
external
{
recipientAndCallee.transferAndCall(tokenID, amount, data);
}

function transferMultiple(address to, uint256[] calldata tokenIDs, uint256[] calldata amounts) external {
to.transferMultiple(tokenIDs, amounts);
}

function transferMultipleAndCall(
address recipientAndCallee,
uint256[] calldata tokenIDs,
uint256[] calldata amounts,
bytes calldata data
)
external
{
recipientAndCallee.transferMultipleAndCall(tokenIDs, amounts, data);
}
}

0 comments on commit 1d7539c

Please sign in to comment.