Skip to content

Commit

Permalink
Merge pull request #12 from sablier-labs/iaro/mint-and-burn-limitation
Browse files Browse the repository at this point in the history
  • Loading branch information
IaroslavMazur authored Aug 30, 2024
2 parents a6d5454 + 1d7539c commit a5cc265
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 121 deletions.
2 changes: 1 addition & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"compiler-version": ["error", ">=0.8.12"],
"func-name-mixedcase": "off",
"func-visibility": ["error", { "ignoreConstructors": true }],
"max-line-length": ["error", 120],
"max-line-length": ["error", 123],
"named-parameters-mapping": "warn",
"no-console": "off",
"not-rely-on-time": "off",
Expand Down
58 changes: 29 additions & 29 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config

[profile.default]
auto_detect_solc = false
block_timestamp = 1717200000 # June 1, 2024 at 00:00 GMT
bytecode_hash = "none"
evm_version = "shanghai"
fuzz = { runs = 1_000 }
gas_reports = ["SRF20"]
optimizer = true
optimizer_runs = 10_000
out = "out"
script = "script"
solc = "0.8.26"
src = "src"
test = "test"
auto_detect_solc = false
block_timestamp = 1717200000 # June 1, 2024 at 00:00 GMT
bytecode_hash = "none"
evm_version = "shanghai"
fuzz = { runs = 1_000 }
gas_reports = ["SRF20"]
optimizer = true
optimizer_runs = 10_000
out = "out"
script = "script"
solc = "0.8.26"
src = "src"
test = "test"

[profile.ci]
fuzz = { runs = 10_000 }
verbosity = 4
fuzz = { runs = 10_000 }
verbosity = 4

# Speed up compilation and tests during development
[profile.lite]
optimizer = false
optimizer = false

[doc]
ignore = ["**/*.t.sol"]
out = "docs"
repository = "https://github.com/sablier-labs/stdlib"
ignore = ["**/*.t.sol"]
out = "docs"
repository = "https://github.com/sablier-labs/stdlib"

[fmt]
bracket_spacing = true
int_types = "long"
line_length = 120
multiline_func_header = "all"
number_underscore = "thousands"
quote_style = "double"
tab_width = 4
wrap_comments = true
bracket_spacing = true
int_types = "long"
line_length = 120
multiline_func_header = "all"
number_underscore = "thousands"
quote_style = "double"
tab_width = 4
wrap_comments = true

[rpc_endpoints]
localhost = "http://localhost:8545"
sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}"
localhost = "http://localhost:8545"
sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}"
4 changes: 1 addition & 3 deletions src/Constants.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.12;

// import { SubID } from "./Types.sol";

/*//////////////////////////////////////////////////////////////////////////
NATIVE TOKENS
//////////////////////////////////////////////////////////////////////////*/

bytes32 constant DEFAULT_SUB_ID = bytes32(0);
uint256 constant DEFAULT_SUB_ID = 0;

/*//////////////////////////////////////////////////////////////////////////
PRECOMPILES
Expand Down
6 changes: 0 additions & 6 deletions src/Types.sol

This file was deleted.

29 changes: 10 additions & 19 deletions src/precompiles/native-tokens/INativeTokens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,23 @@ pragma solidity >=0.8.12;
/// @dev It is NOT recommended to use this interface directly. Instead, users should use the {NativeTokens} library.
/// This interface is used by the library to ABI encode the precompile calls.
interface INativeTokens {
function balanceOf(uint256 tokenID, address account) external returns (uint256);
function burn(address holder, bytes32 subID, uint256 amount) external;
function mint(address recipient, bytes32 subID, uint256 amount) external;
function transfer(address from, address to, bytes32 tokenID, uint256 amount) external;
function balanceOf(address account, uint256 tokenID) external returns (uint256);
function burn(uint256 subID, address holder, uint256 amount) external;
function getCallValues() external returns (uint256[] memory, uint256[] memory);
function mint(uint256 subID, address recipient, uint256 amount) external;
function transfer(address to, uint256 tokenID, uint256 amount) external;
function transferAndCall(
address from,
address to,
bytes32 tokenID,
address recipientAndCallee,
uint256 tokenID,
uint256 amount,
address callee,
bytes calldata data
)
external;
function transferMultiple(
address from,
address[] calldata to,
bytes32[] calldata tokenIDs,
uint256[] calldata amounts
)
external;
function transferMultiple(address to, uint256[] calldata tokenIDs, uint256[] calldata amounts) external;
function transferMultipleAndCall(
address from,
address[] calldata to,
bytes32[] calldata tokenIDs,
address recipientAndCallee,
uint256[] calldata tokenIDs,
uint256[] calldata amounts,
address callee,
bytes calldata data
)
external;
Expand Down
112 changes: 58 additions & 54 deletions src/precompiles/native-tokens/NativeTokens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ library NativeTokens {
//////////////////////////////////////////////////////////////////////////*/

/// @notice Returns the balance of the `account` for the native token `tokenID`.
/// @param tokenID The ID of the native token to query the balance of.
/// @param account The address to query the balance of.
/// @param tokenID The ID of the native token to query the balance of.
/// @return The balance of the `account` for the native token `tokenID`, denoted in 18 decimals.
function balanceOf(uint256 tokenID, address account) internal view returns (uint256) {
function balanceOf(address account, uint256 tokenID) internal view returns (uint256) {
// ABI encode the input parameters.
bytes memory precompileData = abi.encodeCall(INativeTokens.balanceOf, (tokenID, account));
bytes memory callData = abi.encodeCall(INativeTokens.balanceOf, (account, tokenID));

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

// This is an unexpected error since the VM should have panicked if the call failed.
if (!success) {
Expand All @@ -34,39 +34,65 @@ library NativeTokens {
USER-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Burns `amount` tokens with the sub-identifier `sub_id` from the `holder`'s account.
/// @notice Burns `amount` tokens with the sub-identifier `subID` from the `holder`'s account.
/// @dev Generates a Burn receipt.
///
/// Requirements:
/// - The caller of this function must be a contract.
/// - The holder must have at least `amount` tokens.
///
/// @param holder The address to burn native tokens from.
/// @param subID The sub-identifier of the native token to burn.
/// @param amount The quantity of native tokens to burn.
function burn(address holder, bytes32 subID, uint256 amount) internal {
function burn(address holder, uint256 subID, uint256 amount) internal {
// ABI encode the input parameters.
bytes memory precompileData = abi.encodeCall(INativeTokens.burn, (holder, subID, amount));
bytes memory callData = abi.encodeCall(INativeTokens.burn, (subID, holder, amount));

// Call the precompile, ignoring the response since the VM will panic if there's an issue.
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(precompileData);
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);
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.
///
/// Requirements:
/// - The caller of this function must be a contract.
/// - The `recipient`'s balance must not overflow.
///
/// @param recipient The address to mint native tokens to.
/// @param subID The sub-identifier of the native token to mint.
/// @param amount The quantity of native tokens to mint.
function mint(address recipient, bytes32 subID, uint256 amount) internal {
function mint(address recipient, uint256 subID, uint256 amount) internal {
// ABI encode the input parameters.
bytes memory precompileData = abi.encodeCall(INativeTokens.mint, (recipient, subID, amount));
bytes memory callData = abi.encodeCall(INativeTokens.mint, (subID, recipient, amount));

// Call the precompile, ignoring the response since the VM will panic if there's an issue.
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(precompileData);
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);
response;
}

Expand All @@ -81,15 +107,12 @@ library NativeTokens {
/// @param to The address of the recipient.
/// @param tokenID The ID of the native token to transfer.
/// @param amount The quantity of native tokens to transfer.
function transfer(address to, bytes32 tokenID, uint256 amount) internal {
// The address in the calling contract.
address from = address(this);

function transfer(address to, uint256 tokenID, uint256 amount) internal {
// ABI encode the input parameters.
bytes memory precompileData = abi.encodeCall(INativeTokens.transfer, (from, to, tokenID, amount));
bytes memory callData = abi.encodeCall(INativeTokens.transfer, (to, tokenID, amount));

// Call the precompile, ignoring the response because the VM will panic if there's an issue.
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(precompileData);
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);
response;
}

Expand All @@ -100,90 +123,71 @@ library NativeTokens {
/// Requirements:
/// - The calling contract must have at least `amount` tokens.
///
/// @param to The address of the recipient.
/// @param recipientAndCallee The address of the contract recipient of the tokens which will, also, be called.
/// @param tokenID The sub-identifier of the native token to transfer.
/// @param amount The quantity of native tokens to transfer.
/// @param callee The address of the contract to call after the transfer.
/// @param data The call data to pass to the `callee`.
function transferAndCall(
address to,
bytes32 tokenID,
address recipientAndCallee,
uint256 tokenID,
uint256 amount,
address callee,
bytes calldata data
)
internal
{
// The address in the calling contract.
address from = address(this);

// ABI encode the input parameters.
bytes memory precompileData =
abi.encodeCall(INativeTokens.transferAndCall, (from, to, tokenID, amount, callee, data));
bytes memory callData =
abi.encodeCall(INativeTokens.transferAndCall, (recipientAndCallee, tokenID, amount, data));

// Call the precompile, ignoring the response because the VM will panic if there's an issue.
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(precompileData);
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);
response;
}

/// @notice Performs multiple native token transfers from the calling contract to the recipients `to`.
/// @notice Performs multiple native token transfers from the calling contract to the recipient `to`.
/// @dev In SabVM, contracts cannot transfer native tokens on behalf of other addresses.
/// Generates multiple Transfer receipts.
///
/// Requirements:
/// - The calling contract must have at least `amounts[i]` tokens for each token ID `tokenIDs[i]`.
///
/// @param to The addresses of the recipients.
/// @param to The address of the recipient.
/// @param tokenIDs The IDs of the native tokens to transfer.
/// @param amounts The quantities of native tokens to transfer.
function transferMultiple(
address[] calldata to,
bytes32[] calldata tokenIDs,
uint256[] calldata amounts
)
internal
{
// The current address in the calling contract.
address from = address(this);

function transferMultiple(address to, uint256[] calldata tokenIDs, uint256[] calldata amounts) internal {
// ABI encode the input parameters.
bytes memory precompileData = abi.encodeCall(INativeTokens.transferMultiple, (from, to, tokenIDs, amounts));
bytes memory callData = abi.encodeCall(INativeTokens.transferMultiple, (to, tokenIDs, amounts));

// Call the precompile, ignoring the response because the VM will panic if there's an issue.
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(precompileData);
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);
response;
}

/// @notice Performs multiple native token transfers from the calling contract to the recipients `to`, and calls the
/// @notice Performs multiple native token transfers from the calling contract to the recipient `to`, and calls the
/// `callee` with the calldata `data`.
/// @dev Generates multiple Transfer receipts.
///
/// Requirements:
/// - The calling contract must have at least `amounts[i]` tokens for each token ID `tokenIDs[i]`.
///
/// @param to The addresses of the recipients.
/// @param recipientAndCallee The address of the contract recipient of the tokens which will, also, be called.
/// @param tokenIDs The IDs of the native tokens to transfer.
/// @param amounts The quantities of native tokens to transfer.
/// @param callee The address of the contract to call after the transfer.
/// @param data The call data to pass to the `callee`.
function transferMultipleAndCall(
address[] calldata to,
bytes32[] calldata tokenIDs,
address recipientAndCallee,
uint256[] calldata tokenIDs,
uint256[] calldata amounts,
address callee,
bytes calldata data
)
internal
{
// The address in the calling contract.
address from = address(this);

// ABI encode the input parameters.
bytes memory precompileData =
abi.encodeCall(INativeTokens.transferMultipleAndCall, (from, to, tokenIDs, amounts, callee, data));
bytes memory callData =
abi.encodeCall(INativeTokens.transferMultipleAndCall, (recipientAndCallee, tokenIDs, amounts, data));

// Call the precompile, ignoring the response because the VM will panic if there's an issue.
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(precompileData);
(bool response,) = PRECOMPILE_NATIVE_TOKENS.delegatecall(callData);
response;
}
}
8 changes: 4 additions & 4 deletions src/standards/srf-20/SRF20Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ pragma solidity >=0.8.12;

import { SRF20 } from "../../standards/srf-20/SRF20.sol";

/// @notice Dummy mock contract for testing the SRF-20 implementation. The difference between this
/// and {SRF20} is that this mock can be deployed. Abstracts cannot be deployed.
/// @notice Dummy mock contract for testing the SRF-20 implementation. The difference between this and {SRF20} is that
/// this mock can be deployed. Abstracts cannot be deployed.
/// @dev WARNING: This contract is for testing purposes only. Do not use in production.
contract SRF20Mock is SRF20 {
constructor(string memory name, string memory symbol) SRF20(name, symbol) { }

function burn(address holder, uint256 amount) external {
_burn(holder, amount);
function burn(uint256 amount) external {
_burn(msg.sender, amount);
}

function mint(address recipient, uint256 amount) external {
Expand Down
Loading

0 comments on commit a5cc265

Please sign in to comment.