dexorder
This commit is contained in:
203
lib_forge_std/LICENSE-APACHE
Normal file
203
lib_forge_std/LICENSE-APACHE
Normal file
@@ -0,0 +1,203 @@
|
||||
Copyright Contributors to Forge Standard Library
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
25
lib_forge_std/LICENSE-MIT
Normal file
25
lib_forge_std/LICENSE-MIT
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright Contributors to Forge Standard Library
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.R
|
||||
250
lib_forge_std/README.md
Normal file
250
lib_forge_std/README.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Forge Standard Library • [](https://github.com/foundry-rs/forge-std/actions/workflows/ci.yml)
|
||||
|
||||
Forge Standard Library is a collection of helpful contracts and libraries for use with [Forge and Foundry](https://github.com/foundry-rs/foundry). It leverages Forge's cheatcodes to make writing tests easier and faster, while improving the UX of cheatcodes.
|
||||
|
||||
**Learn how to use Forge-Std with the [📖 Foundry Book (Forge-Std Guide)](https://book.getfoundry.sh/forge/forge-std.html).**
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
forge install foundry-rs/forge-std
|
||||
```
|
||||
|
||||
## Contracts
|
||||
### stdError
|
||||
|
||||
This is a helper contract for errors and reverts. In Forge, this contract is particularly helpful for the `expectRevert` cheatcode, as it provides all compiler builtin errors.
|
||||
|
||||
See the contract itself for all error codes.
|
||||
|
||||
#### Example usage
|
||||
|
||||
```solidity
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
contract TestContract is Test {
|
||||
ErrorsTest test;
|
||||
|
||||
function setUp() public {
|
||||
test = new ErrorsTest();
|
||||
}
|
||||
|
||||
function testExpectArithmetic() public {
|
||||
vm.expectRevert(stdError.arithmeticError);
|
||||
test.arithmeticError(10);
|
||||
}
|
||||
}
|
||||
|
||||
contract ErrorsTest {
|
||||
function arithmeticError(uint256 a) public {
|
||||
uint256 a = a - 100;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### stdStorage
|
||||
|
||||
This is a rather large contract due to all of the overloading to make the UX decent. Primarily, it is a wrapper around the `record` and `accesses` cheatcodes. It can *always* find and write the storage slot(s) associated with a particular variable without knowing the storage layout. The one _major_ caveat to this is while a slot can be found for packed storage variables, we can't write to that variable safely. If a user tries to write to a packed slot, the execution throws an error, unless it is uninitialized (`bytes32(0)`).
|
||||
|
||||
This works by recording all `SLOAD`s and `SSTORE`s during a function call. If there is a single slot read or written to, it immediately returns the slot. Otherwise, behind the scenes, we iterate through and check each one (assuming the user passed in a `depth` parameter). If the variable is a struct, you can pass in a `depth` parameter which is basically the field depth.
|
||||
|
||||
I.e.:
|
||||
```solidity
|
||||
struct T {
|
||||
// depth 0
|
||||
uint256 a;
|
||||
// depth 1
|
||||
uint256 b;
|
||||
}
|
||||
```
|
||||
|
||||
#### Example usage
|
||||
|
||||
```solidity
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
contract TestContract is Test {
|
||||
using stdStorage for StdStorage;
|
||||
|
||||
Storage test;
|
||||
|
||||
function setUp() public {
|
||||
test = new Storage();
|
||||
}
|
||||
|
||||
function testFindExists() public {
|
||||
// Lets say we want to find the slot for the public
|
||||
// variable `exists`. We just pass in the function selector
|
||||
// to the `find` command
|
||||
uint256 slot = stdstore.target(address(test)).sig("exists()").find();
|
||||
assertEq(slot, 0);
|
||||
}
|
||||
|
||||
function testWriteExists() public {
|
||||
// Lets say we want to write to the slot for the public
|
||||
// variable `exists`. We just pass in the function selector
|
||||
// to the `checked_write` command
|
||||
stdstore.target(address(test)).sig("exists()").checked_write(100);
|
||||
assertEq(test.exists(), 100);
|
||||
}
|
||||
|
||||
// It supports arbitrary storage layouts, like assembly based storage locations
|
||||
function testFindHidden() public {
|
||||
// `hidden` is a random hash of a bytes, iteration through slots would
|
||||
// not find it. Our mechanism does
|
||||
// Also, you can use the selector instead of a string
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.hidden.selector).find();
|
||||
assertEq(slot, uint256(keccak256("my.random.var")));
|
||||
}
|
||||
|
||||
// If targeting a mapping, you have to pass in the keys necessary to perform the find
|
||||
// i.e.:
|
||||
function testFindMapping() public {
|
||||
uint256 slot = stdstore
|
||||
.target(address(test))
|
||||
.sig(test.map_addr.selector)
|
||||
.with_key(address(this))
|
||||
.find();
|
||||
// in the `Storage` constructor, we wrote that this address' value was 1 in the map
|
||||
// so when we load the slot, we expect it to be 1
|
||||
assertEq(uint(vm.load(address(test), bytes32(slot))), 1);
|
||||
}
|
||||
|
||||
// If the target is a struct, you can specify the field depth:
|
||||
function testFindStruct() public {
|
||||
// NOTE: see the depth parameter - 0 means 0th field, 1 means 1st field, etc.
|
||||
uint256 slot_for_a_field = stdstore
|
||||
.target(address(test))
|
||||
.sig(test.basicStruct.selector)
|
||||
.depth(0)
|
||||
.find();
|
||||
|
||||
uint256 slot_for_b_field = stdstore
|
||||
.target(address(test))
|
||||
.sig(test.basicStruct.selector)
|
||||
.depth(1)
|
||||
.find();
|
||||
|
||||
assertEq(uint(vm.load(address(test), bytes32(slot_for_a_field))), 1);
|
||||
assertEq(uint(vm.load(address(test), bytes32(slot_for_b_field))), 2);
|
||||
}
|
||||
}
|
||||
|
||||
// A complex storage contract
|
||||
contract Storage {
|
||||
struct UnpackedStruct {
|
||||
uint256 a;
|
||||
uint256 b;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
map_addr[msg.sender] = 1;
|
||||
}
|
||||
|
||||
uint256 public exists = 1;
|
||||
mapping(address => uint256) public map_addr;
|
||||
// mapping(address => Packed) public map_packed;
|
||||
mapping(address => UnpackedStruct) public map_struct;
|
||||
mapping(address => mapping(address => uint256)) public deep_map;
|
||||
mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct;
|
||||
UnpackedStruct public basicStruct = UnpackedStruct({
|
||||
a: 1,
|
||||
b: 2
|
||||
});
|
||||
|
||||
function hidden() public view returns (bytes32 t) {
|
||||
// an extremely hidden storage slot
|
||||
bytes32 slot = keccak256("my.random.var");
|
||||
assembly {
|
||||
t := sload(slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### stdCheats
|
||||
|
||||
This is a wrapper over miscellaneous cheatcodes that need wrappers to be more dev friendly. Currently there are only functions related to `prank`. In general, users may expect ETH to be put into an address on `prank`, but this is not the case for safety reasons. Explicitly this `hoax` function should only be used for address that have expected balances as it will get overwritten. If an address already has ETH, you should just use `prank`. If you want to change that balance explicitly, just use `deal`. If you want to do both, `hoax` is also right for you.
|
||||
|
||||
|
||||
#### Example usage:
|
||||
```solidity
|
||||
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
// Inherit the stdCheats
|
||||
contract StdCheatsTest is Test {
|
||||
Bar test;
|
||||
function setUp() public {
|
||||
test = new Bar();
|
||||
}
|
||||
|
||||
function testHoax() public {
|
||||
// we call `hoax`, which gives the target address
|
||||
// eth and then calls `prank`
|
||||
hoax(address(1337));
|
||||
test.bar{value: 100}(address(1337));
|
||||
|
||||
// overloaded to allow you to specify how much eth to
|
||||
// initialize the address with
|
||||
hoax(address(1337), 1);
|
||||
test.bar{value: 1}(address(1337));
|
||||
}
|
||||
|
||||
function testStartHoax() public {
|
||||
// we call `startHoax`, which gives the target address
|
||||
// eth and then calls `startPrank`
|
||||
//
|
||||
// it is also overloaded so that you can specify an eth amount
|
||||
startHoax(address(1337));
|
||||
test.bar{value: 100}(address(1337));
|
||||
test.bar{value: 100}(address(1337));
|
||||
vm.stopPrank();
|
||||
test.bar(address(this));
|
||||
}
|
||||
}
|
||||
|
||||
contract Bar {
|
||||
function bar(address expectedSender) public payable {
|
||||
require(msg.sender == expectedSender, "!prank");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Std Assertions
|
||||
|
||||
Contains various assertions.
|
||||
|
||||
### `console.log`
|
||||
|
||||
Usage follows the same format as [Hardhat](https://hardhat.org/hardhat-network/reference/#console-log).
|
||||
It's recommended to use `console2.sol` as shown below, as this will show the decoded logs in Forge traces.
|
||||
|
||||
```solidity
|
||||
// import it indirectly via Test.sol
|
||||
import "forge-std/Test.sol";
|
||||
// or directly import it
|
||||
import "forge-std/console2.sol";
|
||||
...
|
||||
console2.log(someValue);
|
||||
```
|
||||
|
||||
If you need compatibility with Hardhat, you must use the standard `console.sol` instead.
|
||||
Due to a bug in `console.sol`, logs that use `uint256` or `int256` types will not be properly decoded in Forge traces.
|
||||
|
||||
```solidity
|
||||
// import it indirectly via Test.sol
|
||||
import "forge-std/Test.sol";
|
||||
// or directly import it
|
||||
import "forge-std/console.sol";
|
||||
...
|
||||
console.log(someValue);
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Forge Standard Library is offered under either [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE) license.
|
||||
21
lib_forge_std/foundry.toml
Normal file
21
lib_forge_std/foundry.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[profile.default]
|
||||
fs_permissions = [{ access = "read-write", path = "./"}]
|
||||
|
||||
[rpc_endpoints]
|
||||
# The RPC URLs are modified versions of the default for testing initialization.
|
||||
mainnet = "https://eth-mainnet.alchemyapi.io/v2/WV407BEiBmjNJfKo9Uo_55u0z0ITyCOX" # Different API key.
|
||||
optimism_sepolia = "https://sepolia.optimism.io/" # Adds a trailing slash.
|
||||
arbitrum_one_sepolia = "https://sepolia-rollup.arbitrum.io/rpc/" # Adds a trailing slash.
|
||||
needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}"
|
||||
|
||||
[fmt]
|
||||
# These are all the `forge fmt` defaults.
|
||||
line_length = 120
|
||||
tab_width = 4
|
||||
bracket_spacing = false
|
||||
int_types = 'long'
|
||||
multiline_func_header = 'attributes_first'
|
||||
quote_style = 'double'
|
||||
number_underscore = 'preserve'
|
||||
single_line_statement_blocks = 'preserve'
|
||||
ignore = ["src/console.sol", "src/console2.sol"]
|
||||
16
lib_forge_std/package.json
Normal file
16
lib_forge_std/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "forge-std",
|
||||
"version": "1.7.6",
|
||||
"description": "Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry.",
|
||||
"homepage": "https://book.getfoundry.sh/forge/forge-std",
|
||||
"bugs": "https://github.com/foundry-rs/forge-std/issues",
|
||||
"license": "(Apache-2.0 OR MIT)",
|
||||
"author": "Contributors to Forge Standard Library",
|
||||
"files": [
|
||||
"src/**/*"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/foundry-rs/forge-std.git"
|
||||
}
|
||||
}
|
||||
635
lib_forge_std/scripts/vm.py
Executable file
635
lib_forge_std/scripts/vm.py
Executable file
@@ -0,0 +1,635 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import copy
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from enum import Enum as PyEnum
|
||||
from typing import Callable
|
||||
from urllib import request
|
||||
|
||||
VoidFn = Callable[[], None]
|
||||
|
||||
CHEATCODES_JSON_URL = "https://raw.githubusercontent.com/foundry-rs/foundry/master/crates/cheatcodes/assets/cheatcodes.json"
|
||||
OUT_PATH = "src/Vm.sol"
|
||||
|
||||
VM_SAFE_DOC = """\
|
||||
/// The `VmSafe` interface does not allow manipulation of the EVM state or other actions that may
|
||||
/// result in Script simulations differing from on-chain execution. It is recommended to only use
|
||||
/// these cheats in scripts.
|
||||
"""
|
||||
|
||||
VM_DOC = """\
|
||||
/// The `Vm` interface does allow manipulation of the EVM state. These are all intended to be used
|
||||
/// in tests, but it is not recommended to use these cheats in scripts.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
json_str = request.urlopen(CHEATCODES_JSON_URL).read().decode("utf-8")
|
||||
contract = Cheatcodes.from_json(json_str)
|
||||
|
||||
ccs = contract.cheatcodes
|
||||
ccs = list(filter(lambda cc: cc.status not in ["experimental", "internal"], ccs))
|
||||
ccs.sort(key=lambda cc: cc.func.id)
|
||||
|
||||
safe = list(filter(lambda cc: cc.safety == "safe", ccs))
|
||||
safe.sort(key=CmpCheatcode)
|
||||
unsafe = list(filter(lambda cc: cc.safety == "unsafe", ccs))
|
||||
unsafe.sort(key=CmpCheatcode)
|
||||
assert len(safe) + len(unsafe) == len(ccs)
|
||||
|
||||
prefix_with_group_headers(safe)
|
||||
prefix_with_group_headers(unsafe)
|
||||
|
||||
out = ""
|
||||
|
||||
out += "// Automatically @generated by scripts/vm.py. Do not modify manually.\n\n"
|
||||
|
||||
pp = CheatcodesPrinter(
|
||||
spdx_identifier="MIT OR Apache-2.0",
|
||||
solidity_requirement=">=0.6.2 <0.9.0",
|
||||
abicoder_pragma=True,
|
||||
)
|
||||
pp.p_prelude()
|
||||
pp.prelude = False
|
||||
out += pp.finish()
|
||||
|
||||
out += "\n\n"
|
||||
out += VM_SAFE_DOC
|
||||
vm_safe = Cheatcodes(
|
||||
# TODO: Custom errors were introduced in 0.8.4
|
||||
errors=[], # contract.errors
|
||||
events=contract.events,
|
||||
enums=contract.enums,
|
||||
structs=contract.structs,
|
||||
cheatcodes=safe,
|
||||
)
|
||||
pp.p_contract(vm_safe, "VmSafe")
|
||||
out += pp.finish()
|
||||
|
||||
out += "\n\n"
|
||||
out += VM_DOC
|
||||
vm_unsafe = Cheatcodes(
|
||||
errors=[],
|
||||
events=[],
|
||||
enums=[],
|
||||
structs=[],
|
||||
cheatcodes=unsafe,
|
||||
)
|
||||
pp.p_contract(vm_unsafe, "Vm", "VmSafe")
|
||||
out += pp.finish()
|
||||
|
||||
# Compatibility with <0.8.0
|
||||
def memory_to_calldata(m: re.Match) -> str:
|
||||
return " calldata " + m.group(1)
|
||||
|
||||
out = re.sub(r" memory (.*returns)", memory_to_calldata, out)
|
||||
|
||||
with open(OUT_PATH, "w") as f:
|
||||
f.write(out)
|
||||
|
||||
forge_fmt = ["forge", "fmt", OUT_PATH]
|
||||
res = subprocess.run(forge_fmt)
|
||||
assert res.returncode == 0, f"command failed: {forge_fmt}"
|
||||
|
||||
print(f"Wrote to {OUT_PATH}")
|
||||
|
||||
|
||||
class CmpCheatcode:
|
||||
cheatcode: "Cheatcode"
|
||||
|
||||
def __init__(self, cheatcode: "Cheatcode"):
|
||||
self.cheatcode = cheatcode
|
||||
|
||||
def __lt__(self, other: "CmpCheatcode") -> bool:
|
||||
return cmp_cheatcode(self.cheatcode, other.cheatcode) < 0
|
||||
|
||||
def __eq__(self, other: "CmpCheatcode") -> bool:
|
||||
return cmp_cheatcode(self.cheatcode, other.cheatcode) == 0
|
||||
|
||||
def __gt__(self, other: "CmpCheatcode") -> bool:
|
||||
return cmp_cheatcode(self.cheatcode, other.cheatcode) > 0
|
||||
|
||||
|
||||
def cmp_cheatcode(a: "Cheatcode", b: "Cheatcode") -> int:
|
||||
if a.group != b.group:
|
||||
return -1 if a.group < b.group else 1
|
||||
if a.status != b.status:
|
||||
return -1 if a.status < b.status else 1
|
||||
if a.safety != b.safety:
|
||||
return -1 if a.safety < b.safety else 1
|
||||
if a.func.id != b.func.id:
|
||||
return -1 if a.func.id < b.func.id else 1
|
||||
return 0
|
||||
|
||||
|
||||
# HACK: A way to add group header comments without having to modify printer code
|
||||
def prefix_with_group_headers(cheats: list["Cheatcode"]):
|
||||
s = set()
|
||||
for i, cheat in enumerate(cheats):
|
||||
if cheat.group in s:
|
||||
continue
|
||||
|
||||
s.add(cheat.group)
|
||||
|
||||
c = copy.deepcopy(cheat)
|
||||
c.func.description = ""
|
||||
c.func.declaration = f"// ======== {group(c.group)} ========"
|
||||
cheats.insert(i, c)
|
||||
return cheats
|
||||
|
||||
|
||||
def group(s: str) -> str:
|
||||
if s == "evm":
|
||||
return "EVM"
|
||||
if s == "json":
|
||||
return "JSON"
|
||||
return s[0].upper() + s[1:]
|
||||
|
||||
|
||||
class Visibility(PyEnum):
|
||||
EXTERNAL: str = "external"
|
||||
PUBLIC: str = "public"
|
||||
INTERNAL: str = "internal"
|
||||
PRIVATE: str = "private"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class Mutability(PyEnum):
|
||||
PURE: str = "pure"
|
||||
VIEW: str = "view"
|
||||
NONE: str = ""
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class Function:
|
||||
id: str
|
||||
description: str
|
||||
declaration: str
|
||||
visibility: Visibility
|
||||
mutability: Mutability
|
||||
signature: str
|
||||
selector: str
|
||||
selector_bytes: bytes
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: str,
|
||||
description: str,
|
||||
declaration: str,
|
||||
visibility: Visibility,
|
||||
mutability: Mutability,
|
||||
signature: str,
|
||||
selector: str,
|
||||
selector_bytes: bytes,
|
||||
):
|
||||
self.id = id
|
||||
self.description = description
|
||||
self.declaration = declaration
|
||||
self.visibility = visibility
|
||||
self.mutability = mutability
|
||||
self.signature = signature
|
||||
self.selector = selector
|
||||
self.selector_bytes = selector_bytes
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> "Function":
|
||||
return Function(
|
||||
d["id"],
|
||||
d["description"],
|
||||
d["declaration"],
|
||||
Visibility(d["visibility"]),
|
||||
Mutability(d["mutability"]),
|
||||
d["signature"],
|
||||
d["selector"],
|
||||
bytes(d["selectorBytes"]),
|
||||
)
|
||||
|
||||
|
||||
class Cheatcode:
|
||||
func: Function
|
||||
group: str
|
||||
status: str
|
||||
safety: str
|
||||
|
||||
def __init__(self, func: Function, group: str, status: str, safety: str):
|
||||
self.func = func
|
||||
self.group = group
|
||||
self.status = status
|
||||
self.safety = safety
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> "Cheatcode":
|
||||
return Cheatcode(
|
||||
Function.from_dict(d["func"]),
|
||||
str(d["group"]),
|
||||
str(d["status"]),
|
||||
str(d["safety"]),
|
||||
)
|
||||
|
||||
|
||||
class Error:
|
||||
name: str
|
||||
description: str
|
||||
declaration: str
|
||||
|
||||
def __init__(self, name: str, description: str, declaration: str):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.declaration = declaration
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> "Error":
|
||||
return Error(**d)
|
||||
|
||||
|
||||
class Event:
|
||||
name: str
|
||||
description: str
|
||||
declaration: str
|
||||
|
||||
def __init__(self, name: str, description: str, declaration: str):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.declaration = declaration
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> "Event":
|
||||
return Event(**d)
|
||||
|
||||
|
||||
class EnumVariant:
|
||||
name: str
|
||||
description: str
|
||||
|
||||
def __init__(self, name: str, description: str):
|
||||
self.name = name
|
||||
self.description = description
|
||||
|
||||
|
||||
class Enum:
|
||||
name: str
|
||||
description: str
|
||||
variants: list[EnumVariant]
|
||||
|
||||
def __init__(self, name: str, description: str, variants: list[EnumVariant]):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.variants = variants
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> "Enum":
|
||||
return Enum(
|
||||
d["name"],
|
||||
d["description"],
|
||||
list(map(lambda v: EnumVariant(**v), d["variants"])),
|
||||
)
|
||||
|
||||
|
||||
class StructField:
|
||||
name: str
|
||||
ty: str
|
||||
description: str
|
||||
|
||||
def __init__(self, name: str, ty: str, description: str):
|
||||
self.name = name
|
||||
self.ty = ty
|
||||
self.description = description
|
||||
|
||||
|
||||
class Struct:
|
||||
name: str
|
||||
description: str
|
||||
fields: list[StructField]
|
||||
|
||||
def __init__(self, name: str, description: str, fields: list[StructField]):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.fields = fields
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> "Struct":
|
||||
return Struct(
|
||||
d["name"],
|
||||
d["description"],
|
||||
list(map(lambda f: StructField(**f), d["fields"])),
|
||||
)
|
||||
|
||||
|
||||
class Cheatcodes:
|
||||
errors: list[Error]
|
||||
events: list[Event]
|
||||
enums: list[Enum]
|
||||
structs: list[Struct]
|
||||
cheatcodes: list[Cheatcode]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
errors: list[Error],
|
||||
events: list[Event],
|
||||
enums: list[Enum],
|
||||
structs: list[Struct],
|
||||
cheatcodes: list[Cheatcode],
|
||||
):
|
||||
self.errors = errors
|
||||
self.events = events
|
||||
self.enums = enums
|
||||
self.structs = structs
|
||||
self.cheatcodes = cheatcodes
|
||||
|
||||
@staticmethod
|
||||
def from_dict(d: dict) -> "Cheatcodes":
|
||||
return Cheatcodes(
|
||||
errors=[Error.from_dict(e) for e in d["errors"]],
|
||||
events=[Event.from_dict(e) for e in d["events"]],
|
||||
enums=[Enum.from_dict(e) for e in d["enums"]],
|
||||
structs=[Struct.from_dict(e) for e in d["structs"]],
|
||||
cheatcodes=[Cheatcode.from_dict(e) for e in d["cheatcodes"]],
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_json(s) -> "Cheatcodes":
|
||||
return Cheatcodes.from_dict(json.loads(s))
|
||||
|
||||
@staticmethod
|
||||
def from_json_file(file_path: str) -> "Cheatcodes":
|
||||
with open(file_path, "r") as f:
|
||||
return Cheatcodes.from_dict(json.load(f))
|
||||
|
||||
|
||||
class Item(PyEnum):
|
||||
ERROR: str = "error"
|
||||
EVENT: str = "event"
|
||||
ENUM: str = "enum"
|
||||
STRUCT: str = "struct"
|
||||
FUNCTION: str = "function"
|
||||
|
||||
|
||||
class ItemOrder:
|
||||
_list: list[Item]
|
||||
|
||||
def __init__(self, list: list[Item]) -> None:
|
||||
assert len(list) <= len(Item), "list must not contain more items than Item"
|
||||
assert len(list) == len(set(list)), "list must not contain duplicates"
|
||||
self._list = list
|
||||
pass
|
||||
|
||||
def get_list(self) -> list[Item]:
|
||||
return self._list
|
||||
|
||||
@staticmethod
|
||||
def default() -> "ItemOrder":
|
||||
return ItemOrder(
|
||||
[
|
||||
Item.ERROR,
|
||||
Item.EVENT,
|
||||
Item.ENUM,
|
||||
Item.STRUCT,
|
||||
Item.FUNCTION,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class CheatcodesPrinter:
|
||||
buffer: str
|
||||
|
||||
prelude: bool
|
||||
spdx_identifier: str
|
||||
solidity_requirement: str
|
||||
abicoder_v2: bool
|
||||
|
||||
block_doc_style: bool
|
||||
|
||||
indent_level: int
|
||||
_indent_str: str
|
||||
|
||||
nl_str: str
|
||||
|
||||
items_order: ItemOrder
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
buffer: str = "",
|
||||
prelude: bool = True,
|
||||
spdx_identifier: str = "UNLICENSED",
|
||||
solidity_requirement: str = "",
|
||||
abicoder_pragma: bool = False,
|
||||
block_doc_style: bool = False,
|
||||
indent_level: int = 0,
|
||||
indent_with: int | str = 4,
|
||||
nl_str: str = "\n",
|
||||
items_order: ItemOrder = ItemOrder.default(),
|
||||
):
|
||||
self.prelude = prelude
|
||||
self.spdx_identifier = spdx_identifier
|
||||
self.solidity_requirement = solidity_requirement
|
||||
self.abicoder_v2 = abicoder_pragma
|
||||
self.block_doc_style = block_doc_style
|
||||
self.buffer = buffer
|
||||
self.indent_level = indent_level
|
||||
self.nl_str = nl_str
|
||||
|
||||
if isinstance(indent_with, int):
|
||||
assert indent_with >= 0
|
||||
self._indent_str = " " * indent_with
|
||||
elif isinstance(indent_with, str):
|
||||
self._indent_str = indent_with
|
||||
else:
|
||||
assert False, "indent_with must be int or str"
|
||||
|
||||
self.items_order = items_order
|
||||
|
||||
def finish(self) -> str:
|
||||
ret = self.buffer.rstrip()
|
||||
self.buffer = ""
|
||||
return ret
|
||||
|
||||
def p_contract(self, contract: Cheatcodes, name: str, inherits: str = ""):
|
||||
if self.prelude:
|
||||
self.p_prelude(contract)
|
||||
|
||||
self._p_str("interface ")
|
||||
name = name.strip()
|
||||
if name != "":
|
||||
self._p_str(name)
|
||||
self._p_str(" ")
|
||||
if inherits != "":
|
||||
self._p_str("is ")
|
||||
self._p_str(inherits)
|
||||
self._p_str(" ")
|
||||
self._p_str("{")
|
||||
self._p_nl()
|
||||
self._with_indent(lambda: self._p_items(contract))
|
||||
self._p_str("}")
|
||||
self._p_nl()
|
||||
|
||||
def _p_items(self, contract: Cheatcodes):
|
||||
for item in self.items_order.get_list():
|
||||
if item == Item.ERROR:
|
||||
self.p_errors(contract.errors)
|
||||
elif item == Item.EVENT:
|
||||
self.p_events(contract.events)
|
||||
elif item == Item.ENUM:
|
||||
self.p_enums(contract.enums)
|
||||
elif item == Item.STRUCT:
|
||||
self.p_structs(contract.structs)
|
||||
elif item == Item.FUNCTION:
|
||||
self.p_functions(contract.cheatcodes)
|
||||
else:
|
||||
assert False, f"unknown item {item}"
|
||||
|
||||
def p_prelude(self, contract: Cheatcodes | None = None):
|
||||
self._p_str(f"// SPDX-License-Identifier: {self.spdx_identifier}")
|
||||
self._p_nl()
|
||||
|
||||
if self.solidity_requirement != "":
|
||||
req = self.solidity_requirement
|
||||
elif contract and len(contract.errors) > 0:
|
||||
req = ">=0.8.4 <0.9.0"
|
||||
else:
|
||||
req = ">=0.6.0 <0.9.0"
|
||||
self._p_str(f"pragma solidity {req};")
|
||||
self._p_nl()
|
||||
|
||||
if self.abicoder_v2:
|
||||
self._p_str("pragma experimental ABIEncoderV2;")
|
||||
self._p_nl()
|
||||
|
||||
self._p_nl()
|
||||
|
||||
def p_errors(self, errors: list[Error]):
|
||||
for error in errors:
|
||||
self._p_line(lambda: self.p_error(error))
|
||||
|
||||
def p_error(self, error: Error):
|
||||
self._p_comment(error.description, doc=True)
|
||||
self._p_line(lambda: self._p_str(error.declaration))
|
||||
|
||||
def p_events(self, events: list[Event]):
|
||||
for event in events:
|
||||
self._p_line(lambda: self.p_event(event))
|
||||
|
||||
def p_event(self, event: Event):
|
||||
self._p_comment(event.description, doc=True)
|
||||
self._p_line(lambda: self._p_str(event.declaration))
|
||||
|
||||
def p_enums(self, enums: list[Enum]):
|
||||
for enum in enums:
|
||||
self._p_line(lambda: self.p_enum(enum))
|
||||
|
||||
def p_enum(self, enum: Enum):
|
||||
self._p_comment(enum.description, doc=True)
|
||||
self._p_line(lambda: self._p_str(f"enum {enum.name} {{"))
|
||||
self._with_indent(lambda: self.p_enum_variants(enum.variants))
|
||||
self._p_line(lambda: self._p_str("}"))
|
||||
|
||||
def p_enum_variants(self, variants: list[EnumVariant]):
|
||||
for i, variant in enumerate(variants):
|
||||
self._p_indent()
|
||||
self._p_comment(variant.description)
|
||||
|
||||
self._p_indent()
|
||||
self._p_str(variant.name)
|
||||
if i < len(variants) - 1:
|
||||
self._p_str(",")
|
||||
self._p_nl()
|
||||
|
||||
def p_structs(self, structs: list[Struct]):
|
||||
for struct in structs:
|
||||
self._p_line(lambda: self.p_struct(struct))
|
||||
|
||||
def p_struct(self, struct: Struct):
|
||||
self._p_comment(struct.description, doc=True)
|
||||
self._p_line(lambda: self._p_str(f"struct {struct.name} {{"))
|
||||
self._with_indent(lambda: self.p_struct_fields(struct.fields))
|
||||
self._p_line(lambda: self._p_str("}"))
|
||||
|
||||
def p_struct_fields(self, fields: list[StructField]):
|
||||
for field in fields:
|
||||
self._p_line(lambda: self.p_struct_field(field))
|
||||
|
||||
def p_struct_field(self, field: StructField):
|
||||
self._p_comment(field.description)
|
||||
self._p_indented(lambda: self._p_str(f"{field.ty} {field.name};"))
|
||||
|
||||
def p_functions(self, cheatcodes: list[Cheatcode]):
|
||||
for cheatcode in cheatcodes:
|
||||
self._p_line(lambda: self.p_function(cheatcode.func))
|
||||
|
||||
def p_function(self, func: Function):
|
||||
self._p_comment(func.description, doc=True)
|
||||
self._p_line(lambda: self._p_str(func.declaration))
|
||||
|
||||
def _p_comment(self, s: str, doc: bool = False):
|
||||
s = s.strip()
|
||||
if s == "":
|
||||
return
|
||||
|
||||
s = map(lambda line: line.lstrip(), s.split("\n"))
|
||||
if self.block_doc_style:
|
||||
self._p_str("/*")
|
||||
if doc:
|
||||
self._p_str("*")
|
||||
self._p_nl()
|
||||
for line in s:
|
||||
self._p_indent()
|
||||
self._p_str(" ")
|
||||
if doc:
|
||||
self._p_str("* ")
|
||||
self._p_str(line)
|
||||
self._p_nl()
|
||||
self._p_indent()
|
||||
self._p_str(" */")
|
||||
self._p_nl()
|
||||
else:
|
||||
first_line = True
|
||||
for line in s:
|
||||
if not first_line:
|
||||
self._p_indent()
|
||||
first_line = False
|
||||
|
||||
if doc:
|
||||
self._p_str("/// ")
|
||||
else:
|
||||
self._p_str("// ")
|
||||
self._p_str(line)
|
||||
self._p_nl()
|
||||
|
||||
def _with_indent(self, f: VoidFn):
|
||||
self._inc_indent()
|
||||
f()
|
||||
self._dec_indent()
|
||||
|
||||
def _p_line(self, f: VoidFn):
|
||||
self._p_indent()
|
||||
f()
|
||||
self._p_nl()
|
||||
|
||||
def _p_indented(self, f: VoidFn):
|
||||
self._p_indent()
|
||||
f()
|
||||
|
||||
def _p_indent(self):
|
||||
for _ in range(self.indent_level):
|
||||
self._p_str(self._indent_str)
|
||||
|
||||
def _p_nl(self):
|
||||
self._p_str(self.nl_str)
|
||||
|
||||
def _p_str(self, txt: str):
|
||||
self.buffer += txt
|
||||
|
||||
def _inc_indent(self):
|
||||
self.indent_level += 1
|
||||
|
||||
def _dec_indent(self):
|
||||
self.indent_level -= 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
35
lib_forge_std/src/Base.sol
Normal file
35
lib_forge_std/src/Base.sol
Normal file
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
import {StdStorage} from "./StdStorage.sol";
|
||||
import {Vm, VmSafe} from "./Vm.sol";
|
||||
|
||||
abstract contract CommonBase {
|
||||
// Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
|
||||
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
|
||||
// console.sol and console2.sol work by executing a staticcall to this address.
|
||||
address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67;
|
||||
// Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy.
|
||||
address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
|
||||
// Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38.
|
||||
address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller"))));
|
||||
// Address of the test contract, deployed by the DEFAULT_SENDER.
|
||||
address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f;
|
||||
// Deterministic deployment address of the Multicall3 contract.
|
||||
address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11;
|
||||
// The order of the secp256k1 curve.
|
||||
uint256 internal constant SECP256K1_ORDER =
|
||||
115792089237316195423570985008687907852837564279074904382605163141518161494337;
|
||||
|
||||
uint256 internal constant UINT256_MAX =
|
||||
115792089237316195423570985008687907853269984665640564039457584007913129639935;
|
||||
|
||||
Vm internal constant vm = Vm(VM_ADDRESS);
|
||||
StdStorage internal stdstore;
|
||||
}
|
||||
|
||||
abstract contract TestBase is CommonBase {}
|
||||
|
||||
abstract contract ScriptBase is CommonBase {
|
||||
VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS);
|
||||
}
|
||||
27
lib_forge_std/src/Script.sol
Normal file
27
lib_forge_std/src/Script.sol
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
// 💬 ABOUT
|
||||
// Forge Std's default Script.
|
||||
|
||||
// 🧩 MODULES
|
||||
import {console} from "./console.sol";
|
||||
import {console2} from "./console2.sol";
|
||||
import {safeconsole} from "./safeconsole.sol";
|
||||
import {StdChains} from "./StdChains.sol";
|
||||
import {StdCheatsSafe} from "./StdCheats.sol";
|
||||
import {stdJson} from "./StdJson.sol";
|
||||
import {stdMath} from "./StdMath.sol";
|
||||
import {StdStorage, stdStorageSafe} from "./StdStorage.sol";
|
||||
import {StdStyle} from "./StdStyle.sol";
|
||||
import {StdUtils} from "./StdUtils.sol";
|
||||
import {VmSafe} from "./Vm.sol";
|
||||
|
||||
// 📦 BOILERPLATE
|
||||
import {ScriptBase} from "./Base.sol";
|
||||
|
||||
// ⭐️ SCRIPT
|
||||
abstract contract Script is ScriptBase, StdChains, StdCheatsSafe, StdUtils {
|
||||
// Note: IS_SCRIPT() must return true.
|
||||
bool public IS_SCRIPT = true;
|
||||
}
|
||||
669
lib_forge_std/src/StdAssertions.sol
Normal file
669
lib_forge_std/src/StdAssertions.sol
Normal file
@@ -0,0 +1,669 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {Vm} from "./Vm.sol";
|
||||
|
||||
abstract contract StdAssertions {
|
||||
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
event log(string);
|
||||
event logs(bytes);
|
||||
|
||||
event log_address(address);
|
||||
event log_bytes32(bytes32);
|
||||
event log_int(int256);
|
||||
event log_uint(uint256);
|
||||
event log_bytes(bytes);
|
||||
event log_string(string);
|
||||
|
||||
event log_named_address(string key, address val);
|
||||
event log_named_bytes32(string key, bytes32 val);
|
||||
event log_named_decimal_int(string key, int256 val, uint256 decimals);
|
||||
event log_named_decimal_uint(string key, uint256 val, uint256 decimals);
|
||||
event log_named_int(string key, int256 val);
|
||||
event log_named_uint(string key, uint256 val);
|
||||
event log_named_bytes(string key, bytes val);
|
||||
event log_named_string(string key, string val);
|
||||
|
||||
event log_array(uint256[] val);
|
||||
event log_array(int256[] val);
|
||||
event log_array(address[] val);
|
||||
event log_named_array(string key, uint256[] val);
|
||||
event log_named_array(string key, int256[] val);
|
||||
event log_named_array(string key, address[] val);
|
||||
|
||||
bool private _failed;
|
||||
|
||||
function failed() public view returns (bool) {
|
||||
if (_failed) {
|
||||
return _failed;
|
||||
} else {
|
||||
return vm.load(address(vm), bytes32("failed")) != bytes32(0);
|
||||
}
|
||||
}
|
||||
|
||||
function fail() internal virtual {
|
||||
vm.store(address(vm), bytes32("failed"), bytes32(uint256(1)));
|
||||
_failed = true;
|
||||
}
|
||||
|
||||
function assertTrue(bool data) internal pure virtual {
|
||||
vm.assertTrue(data);
|
||||
}
|
||||
|
||||
function assertTrue(bool data, string memory err) internal pure virtual {
|
||||
vm.assertTrue(data, err);
|
||||
}
|
||||
|
||||
function assertFalse(bool data) internal pure virtual {
|
||||
vm.assertFalse(data);
|
||||
}
|
||||
|
||||
function assertFalse(bool data, string memory err) internal pure virtual {
|
||||
vm.assertFalse(data, err);
|
||||
}
|
||||
|
||||
function assertEq(bool left, bool right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(bool left, bool right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(uint256 left, uint256 right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(uint256 left, uint256 right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEqDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertEqDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertEqDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertEq(int256 left, int256 right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(int256 left, int256 right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEqDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertEqDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertEqDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertEqDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertEq(address left, address right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(address left, address right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(bytes32 left, bytes32 right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(bytes32 left, bytes32 right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq32(bytes32 left, bytes32 right) internal pure virtual {
|
||||
assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq32(bytes32 left, bytes32 right, string memory err) internal pure virtual {
|
||||
assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(string memory left, string memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(string memory left, string memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(bytes memory left, bytes memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(bytes memory left, bytes memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(bool[] memory left, bool[] memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(bool[] memory left, bool[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(uint256[] memory left, uint256[] memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(uint256[] memory left, uint256[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(int256[] memory left, int256[] memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(int256[] memory left, int256[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(address[] memory left, address[] memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(address[] memory left, address[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(bytes32[] memory left, bytes32[] memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(bytes32[] memory left, bytes32[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(string[] memory left, string[] memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(string[] memory left, string[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEq(bytes[] memory left, bytes[] memory right) internal pure virtual {
|
||||
vm.assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq(bytes[] memory left, bytes[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertEq(left, right, err);
|
||||
}
|
||||
|
||||
// Legacy helper
|
||||
function assertEqUint(uint256 left, uint256 right) internal pure virtual {
|
||||
assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(bool left, bool right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(bool left, bool right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(uint256 left, uint256 right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(uint256 left, uint256 right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertNotEqDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string memory err)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
{
|
||||
vm.assertNotEqDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertNotEq(int256 left, int256 right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(int256 left, int256 right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEqDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertNotEqDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertNotEqDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertNotEq(address left, address right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(address left, address right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes32 left, bytes32 right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes32 left, bytes32 right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq32(bytes32 left, bytes32 right) internal pure virtual {
|
||||
assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq32(bytes32 left, bytes32 right, string memory err) internal pure virtual {
|
||||
assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(string memory left, string memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(string memory left, string memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes memory left, bytes memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes memory left, bytes memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(bool[] memory left, bool[] memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(bool[] memory left, bool[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(uint256[] memory left, uint256[] memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(uint256[] memory left, uint256[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(int256[] memory left, int256[] memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(int256[] memory left, int256[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(address[] memory left, address[] memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(address[] memory left, address[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes32[] memory left, bytes32[] memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes32[] memory left, bytes32[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(string[] memory left, string[] memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(string[] memory left, string[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes[] memory left, bytes[] memory right) internal pure virtual {
|
||||
vm.assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq(bytes[] memory left, bytes[] memory right, string memory err) internal pure virtual {
|
||||
vm.assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertLt(uint256 left, uint256 right) internal pure virtual {
|
||||
vm.assertLt(left, right);
|
||||
}
|
||||
|
||||
function assertLt(uint256 left, uint256 right, string memory err) internal pure virtual {
|
||||
vm.assertLt(left, right, err);
|
||||
}
|
||||
|
||||
function assertLtDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertLtDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertLtDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertLt(int256 left, int256 right) internal pure virtual {
|
||||
vm.assertLt(left, right);
|
||||
}
|
||||
|
||||
function assertLt(int256 left, int256 right, string memory err) internal pure virtual {
|
||||
vm.assertLt(left, right, err);
|
||||
}
|
||||
|
||||
function assertLtDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertLtDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertLtDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertLtDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertGt(uint256 left, uint256 right) internal pure virtual {
|
||||
vm.assertGt(left, right);
|
||||
}
|
||||
|
||||
function assertGt(uint256 left, uint256 right, string memory err) internal pure virtual {
|
||||
vm.assertGt(left, right, err);
|
||||
}
|
||||
|
||||
function assertGtDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertGtDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertGtDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertGt(int256 left, int256 right) internal pure virtual {
|
||||
vm.assertGt(left, right);
|
||||
}
|
||||
|
||||
function assertGt(int256 left, int256 right, string memory err) internal pure virtual {
|
||||
vm.assertGt(left, right, err);
|
||||
}
|
||||
|
||||
function assertGtDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertGtDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertGtDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertGtDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertLe(uint256 left, uint256 right) internal pure virtual {
|
||||
vm.assertLe(left, right);
|
||||
}
|
||||
|
||||
function assertLe(uint256 left, uint256 right, string memory err) internal pure virtual {
|
||||
vm.assertLe(left, right, err);
|
||||
}
|
||||
|
||||
function assertLeDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertLeDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertLeDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertLe(int256 left, int256 right) internal pure virtual {
|
||||
vm.assertLe(left, right);
|
||||
}
|
||||
|
||||
function assertLe(int256 left, int256 right, string memory err) internal pure virtual {
|
||||
vm.assertLe(left, right, err);
|
||||
}
|
||||
|
||||
function assertLeDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertLeDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertLeDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertLeDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertGe(uint256 left, uint256 right) internal pure virtual {
|
||||
vm.assertGe(left, right);
|
||||
}
|
||||
|
||||
function assertGe(uint256 left, uint256 right, string memory err) internal pure virtual {
|
||||
vm.assertGe(left, right, err);
|
||||
}
|
||||
|
||||
function assertGeDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertGeDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertGeDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertGe(int256 left, int256 right) internal pure virtual {
|
||||
vm.assertGe(left, right);
|
||||
}
|
||||
|
||||
function assertGe(int256 left, int256 right, string memory err) internal pure virtual {
|
||||
vm.assertGe(left, right, err);
|
||||
}
|
||||
|
||||
function assertGeDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
|
||||
vm.assertGeDecimal(left, right, decimals);
|
||||
}
|
||||
|
||||
function assertGeDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
|
||||
vm.assertGeDecimal(left, right, decimals, err);
|
||||
}
|
||||
|
||||
function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta) internal pure virtual {
|
||||
vm.assertApproxEqAbs(left, right, maxDelta);
|
||||
}
|
||||
|
||||
function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string memory err)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
{
|
||||
vm.assertApproxEqAbs(left, right, maxDelta, err);
|
||||
}
|
||||
|
||||
function assertApproxEqAbsDecimal(uint256 left, uint256 right, uint256 maxDelta, uint256 decimals)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
{
|
||||
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals);
|
||||
}
|
||||
|
||||
function assertApproxEqAbsDecimal(
|
||||
uint256 left,
|
||||
uint256 right,
|
||||
uint256 maxDelta,
|
||||
uint256 decimals,
|
||||
string memory err
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals, err);
|
||||
}
|
||||
|
||||
function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta) internal pure virtual {
|
||||
vm.assertApproxEqAbs(left, right, maxDelta);
|
||||
}
|
||||
|
||||
function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string memory err) internal pure virtual {
|
||||
vm.assertApproxEqAbs(left, right, maxDelta, err);
|
||||
}
|
||||
|
||||
function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
{
|
||||
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals);
|
||||
}
|
||||
|
||||
function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals, string memory err)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
{
|
||||
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals, err);
|
||||
}
|
||||
|
||||
function assertApproxEqRel(
|
||||
uint256 left,
|
||||
uint256 right,
|
||||
uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100%
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqRel(left, right, maxPercentDelta);
|
||||
}
|
||||
|
||||
function assertApproxEqRel(
|
||||
uint256 left,
|
||||
uint256 right,
|
||||
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
|
||||
string memory err
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqRel(left, right, maxPercentDelta, err);
|
||||
}
|
||||
|
||||
function assertApproxEqRelDecimal(
|
||||
uint256 left,
|
||||
uint256 right,
|
||||
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
|
||||
uint256 decimals
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals);
|
||||
}
|
||||
|
||||
function assertApproxEqRelDecimal(
|
||||
uint256 left,
|
||||
uint256 right,
|
||||
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
|
||||
uint256 decimals,
|
||||
string memory err
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals, err);
|
||||
}
|
||||
|
||||
function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta) internal pure virtual {
|
||||
vm.assertApproxEqRel(left, right, maxPercentDelta);
|
||||
}
|
||||
|
||||
function assertApproxEqRel(
|
||||
int256 left,
|
||||
int256 right,
|
||||
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
|
||||
string memory err
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqRel(left, right, maxPercentDelta, err);
|
||||
}
|
||||
|
||||
function assertApproxEqRelDecimal(
|
||||
int256 left,
|
||||
int256 right,
|
||||
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
|
||||
uint256 decimals
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals);
|
||||
}
|
||||
|
||||
function assertApproxEqRelDecimal(
|
||||
int256 left,
|
||||
int256 right,
|
||||
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
|
||||
uint256 decimals,
|
||||
string memory err
|
||||
) internal pure virtual {
|
||||
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals, err);
|
||||
}
|
||||
|
||||
// Inherited from DSTest, not used but kept for backwards-compatibility
|
||||
function checkEq0(bytes memory left, bytes memory right) internal pure returns (bool) {
|
||||
return keccak256(left) == keccak256(right);
|
||||
}
|
||||
|
||||
function assertEq0(bytes memory left, bytes memory right) internal pure virtual {
|
||||
assertEq(left, right);
|
||||
}
|
||||
|
||||
function assertEq0(bytes memory left, bytes memory right, string memory err) internal pure virtual {
|
||||
assertEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertNotEq0(bytes memory left, bytes memory right) internal pure virtual {
|
||||
assertNotEq(left, right);
|
||||
}
|
||||
|
||||
function assertNotEq0(bytes memory left, bytes memory right, string memory err) internal pure virtual {
|
||||
assertNotEq(left, right, err);
|
||||
}
|
||||
|
||||
function assertEqCall(address target, bytes memory callDataA, bytes memory callDataB) internal virtual {
|
||||
assertEqCall(target, callDataA, target, callDataB, true);
|
||||
}
|
||||
|
||||
function assertEqCall(address targetA, bytes memory callDataA, address targetB, bytes memory callDataB)
|
||||
internal
|
||||
virtual
|
||||
{
|
||||
assertEqCall(targetA, callDataA, targetB, callDataB, true);
|
||||
}
|
||||
|
||||
function assertEqCall(address target, bytes memory callDataA, bytes memory callDataB, bool strictRevertData)
|
||||
internal
|
||||
virtual
|
||||
{
|
||||
assertEqCall(target, callDataA, target, callDataB, strictRevertData);
|
||||
}
|
||||
|
||||
function assertEqCall(
|
||||
address targetA,
|
||||
bytes memory callDataA,
|
||||
address targetB,
|
||||
bytes memory callDataB,
|
||||
bool strictRevertData
|
||||
) internal virtual {
|
||||
(bool successA, bytes memory returnDataA) = address(targetA).call(callDataA);
|
||||
(bool successB, bytes memory returnDataB) = address(targetB).call(callDataB);
|
||||
|
||||
if (successA && successB) {
|
||||
assertEq(returnDataA, returnDataB, "Call return data does not match");
|
||||
}
|
||||
|
||||
if (!successA && !successB && strictRevertData) {
|
||||
assertEq(returnDataA, returnDataB, "Call revert data does not match");
|
||||
}
|
||||
|
||||
if (!successA && successB) {
|
||||
emit log("Error: Calls were not equal");
|
||||
emit log_named_bytes(" Left call revert data", returnDataA);
|
||||
emit log_named_bytes(" Right call return data", returnDataB);
|
||||
revert("assertion failed");
|
||||
}
|
||||
|
||||
if (successA && !successB) {
|
||||
emit log("Error: Calls were not equal");
|
||||
emit log_named_bytes(" Left call return data", returnDataA);
|
||||
emit log_named_bytes(" Right call revert data", returnDataB);
|
||||
revert("assertion failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
250
lib_forge_std/src/StdChains.sol
Normal file
250
lib_forge_std/src/StdChains.sol
Normal file
@@ -0,0 +1,250 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
import {VmSafe} from "./Vm.sol";
|
||||
|
||||
/**
|
||||
* StdChains provides information about EVM compatible chains that can be used in scripts/tests.
|
||||
* For each chain, the chain's name, chain ID, and a default RPC URL are provided. Chains are
|
||||
* identified by their alias, which is the same as the alias in the `[rpc_endpoints]` section of
|
||||
* the `foundry.toml` file. For best UX, ensure the alias in the `foundry.toml` file match the
|
||||
* alias used in this contract, which can be found as the first argument to the
|
||||
* `setChainWithDefaultRpcUrl` call in the `initializeStdChains` function.
|
||||
*
|
||||
* There are two main ways to use this contract:
|
||||
* 1. Set a chain with `setChain(string memory chainAlias, ChainData memory chain)` or
|
||||
* `setChain(string memory chainAlias, Chain memory chain)`
|
||||
* 2. Get a chain with `getChain(string memory chainAlias)` or `getChain(uint256 chainId)`.
|
||||
*
|
||||
* The first time either of those are used, chains are initialized with the default set of RPC URLs.
|
||||
* This is done in `initializeStdChains`, which uses `setChainWithDefaultRpcUrl`. Defaults are recorded in
|
||||
* `defaultRpcUrls`.
|
||||
*
|
||||
* The `setChain` function is straightforward, and it simply saves off the given chain data.
|
||||
*
|
||||
* The `getChain` methods use `getChainWithUpdatedRpcUrl` to return a chain. For example, let's say
|
||||
* we want to retrieve the RPC URL for `mainnet`:
|
||||
* - If you have specified data with `setChain`, it will return that.
|
||||
* - If you have configured a mainnet RPC URL in `foundry.toml`, it will return the URL, provided it
|
||||
* is valid (e.g. a URL is specified, or an environment variable is given and exists).
|
||||
* - If neither of the above conditions is met, the default data is returned.
|
||||
*
|
||||
* Summarizing the above, the prioritization hierarchy is `setChain` -> `foundry.toml` -> environment variable -> defaults.
|
||||
*/
|
||||
abstract contract StdChains {
|
||||
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
bool private stdChainsInitialized;
|
||||
|
||||
struct ChainData {
|
||||
string name;
|
||||
uint256 chainId;
|
||||
string rpcUrl;
|
||||
}
|
||||
|
||||
struct Chain {
|
||||
// The chain name.
|
||||
string name;
|
||||
// The chain's Chain ID.
|
||||
uint256 chainId;
|
||||
// The chain's alias. (i.e. what gets specified in `foundry.toml`).
|
||||
string chainAlias;
|
||||
// A default RPC endpoint for this chain.
|
||||
// NOTE: This default RPC URL is included for convenience to facilitate quick tests and
|
||||
// experimentation. Do not use this RPC URL for production test suites, CI, or other heavy
|
||||
// usage as you will be throttled and this is a disservice to others who need this endpoint.
|
||||
string rpcUrl;
|
||||
}
|
||||
|
||||
// Maps from the chain's alias (matching the alias in the `foundry.toml` file) to chain data.
|
||||
mapping(string => Chain) private chains;
|
||||
// Maps from the chain's alias to it's default RPC URL.
|
||||
mapping(string => string) private defaultRpcUrls;
|
||||
// Maps from a chain ID to it's alias.
|
||||
mapping(uint256 => string) private idToAlias;
|
||||
|
||||
bool private fallbackToDefaultRpcUrls = true;
|
||||
|
||||
// The RPC URL will be fetched from config or defaultRpcUrls if possible.
|
||||
function getChain(string memory chainAlias) internal virtual returns (Chain memory chain) {
|
||||
require(bytes(chainAlias).length != 0, "StdChains getChain(string): Chain alias cannot be the empty string.");
|
||||
|
||||
initializeStdChains();
|
||||
chain = chains[chainAlias];
|
||||
require(
|
||||
chain.chainId != 0,
|
||||
string(abi.encodePacked("StdChains getChain(string): Chain with alias \"", chainAlias, "\" not found."))
|
||||
);
|
||||
|
||||
chain = getChainWithUpdatedRpcUrl(chainAlias, chain);
|
||||
}
|
||||
|
||||
function getChain(uint256 chainId) internal virtual returns (Chain memory chain) {
|
||||
require(chainId != 0, "StdChains getChain(uint256): Chain ID cannot be 0.");
|
||||
initializeStdChains();
|
||||
string memory chainAlias = idToAlias[chainId];
|
||||
|
||||
chain = chains[chainAlias];
|
||||
|
||||
require(
|
||||
chain.chainId != 0,
|
||||
string(abi.encodePacked("StdChains getChain(uint256): Chain with ID ", vm.toString(chainId), " not found."))
|
||||
);
|
||||
|
||||
chain = getChainWithUpdatedRpcUrl(chainAlias, chain);
|
||||
}
|
||||
|
||||
// set chain info, with priority to argument's rpcUrl field.
|
||||
function setChain(string memory chainAlias, ChainData memory chain) internal virtual {
|
||||
require(
|
||||
bytes(chainAlias).length != 0,
|
||||
"StdChains setChain(string,ChainData): Chain alias cannot be the empty string."
|
||||
);
|
||||
|
||||
require(chain.chainId != 0, "StdChains setChain(string,ChainData): Chain ID cannot be 0.");
|
||||
|
||||
initializeStdChains();
|
||||
string memory foundAlias = idToAlias[chain.chainId];
|
||||
|
||||
require(
|
||||
bytes(foundAlias).length == 0 || keccak256(bytes(foundAlias)) == keccak256(bytes(chainAlias)),
|
||||
string(
|
||||
abi.encodePacked(
|
||||
"StdChains setChain(string,ChainData): Chain ID ",
|
||||
vm.toString(chain.chainId),
|
||||
" already used by \"",
|
||||
foundAlias,
|
||||
"\"."
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
uint256 oldChainId = chains[chainAlias].chainId;
|
||||
delete idToAlias[oldChainId];
|
||||
|
||||
chains[chainAlias] =
|
||||
Chain({name: chain.name, chainId: chain.chainId, chainAlias: chainAlias, rpcUrl: chain.rpcUrl});
|
||||
idToAlias[chain.chainId] = chainAlias;
|
||||
}
|
||||
|
||||
// set chain info, with priority to argument's rpcUrl field.
|
||||
function setChain(string memory chainAlias, Chain memory chain) internal virtual {
|
||||
setChain(chainAlias, ChainData({name: chain.name, chainId: chain.chainId, rpcUrl: chain.rpcUrl}));
|
||||
}
|
||||
|
||||
function _toUpper(string memory str) private pure returns (string memory) {
|
||||
bytes memory strb = bytes(str);
|
||||
bytes memory copy = new bytes(strb.length);
|
||||
for (uint256 i = 0; i < strb.length; i++) {
|
||||
bytes1 b = strb[i];
|
||||
if (b >= 0x61 && b <= 0x7A) {
|
||||
copy[i] = bytes1(uint8(b) - 32);
|
||||
} else {
|
||||
copy[i] = b;
|
||||
}
|
||||
}
|
||||
return string(copy);
|
||||
}
|
||||
|
||||
// lookup rpcUrl, in descending order of priority:
|
||||
// current -> config (foundry.toml) -> environment variable -> default
|
||||
function getChainWithUpdatedRpcUrl(string memory chainAlias, Chain memory chain)
|
||||
private
|
||||
view
|
||||
returns (Chain memory)
|
||||
{
|
||||
if (bytes(chain.rpcUrl).length == 0) {
|
||||
try vm.rpcUrl(chainAlias) returns (string memory configRpcUrl) {
|
||||
chain.rpcUrl = configRpcUrl;
|
||||
} catch (bytes memory err) {
|
||||
string memory envName = string(abi.encodePacked(_toUpper(chainAlias), "_RPC_URL"));
|
||||
if (fallbackToDefaultRpcUrls) {
|
||||
chain.rpcUrl = vm.envOr(envName, defaultRpcUrls[chainAlias]);
|
||||
} else {
|
||||
chain.rpcUrl = vm.envString(envName);
|
||||
}
|
||||
// Distinguish 'not found' from 'cannot read'
|
||||
// The upstream error thrown by forge for failing cheats changed so we check both the old and new versions
|
||||
bytes memory oldNotFoundError =
|
||||
abi.encodeWithSignature("CheatCodeError", string(abi.encodePacked("invalid rpc url ", chainAlias)));
|
||||
bytes memory newNotFoundError = abi.encodeWithSignature(
|
||||
"CheatcodeError(string)", string(abi.encodePacked("invalid rpc url: ", chainAlias))
|
||||
);
|
||||
bytes32 errHash = keccak256(err);
|
||||
if (
|
||||
(errHash != keccak256(oldNotFoundError) && errHash != keccak256(newNotFoundError))
|
||||
|| bytes(chain.rpcUrl).length == 0
|
||||
) {
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
revert(add(32, err), mload(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
function setFallbackToDefaultRpcUrls(bool useDefault) internal {
|
||||
fallbackToDefaultRpcUrls = useDefault;
|
||||
}
|
||||
|
||||
function initializeStdChains() private {
|
||||
if (stdChainsInitialized) return;
|
||||
|
||||
stdChainsInitialized = true;
|
||||
|
||||
// If adding an RPC here, make sure to test the default RPC URL in `testRpcs`
|
||||
setChainWithDefaultRpcUrl("anvil", ChainData("Anvil", 31337, "http://127.0.0.1:8545"));
|
||||
setChainWithDefaultRpcUrl(
|
||||
"mainnet", ChainData("Mainnet", 1, "https://eth-mainnet.alchemyapi.io/v2/pwc5rmJhrdoaSEfimoKEmsvOjKSmPDrP")
|
||||
);
|
||||
setChainWithDefaultRpcUrl(
|
||||
"sepolia", ChainData("Sepolia", 11155111, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001")
|
||||
);
|
||||
setChainWithDefaultRpcUrl("holesky", ChainData("Holesky", 17000, "https://rpc.holesky.ethpandaops.io"));
|
||||
setChainWithDefaultRpcUrl("optimism", ChainData("Optimism", 10, "https://mainnet.optimism.io"));
|
||||
setChainWithDefaultRpcUrl(
|
||||
"optimism_sepolia", ChainData("Optimism Sepolia", 11155420, "https://sepolia.optimism.io")
|
||||
);
|
||||
setChainWithDefaultRpcUrl("arbitrum_one", ChainData("Arbitrum One", 42161, "https://arb1.arbitrum.io/rpc"));
|
||||
setChainWithDefaultRpcUrl(
|
||||
"arbitrum_one_sepolia", ChainData("Arbitrum One Sepolia", 421614, "https://sepolia-rollup.arbitrum.io/rpc")
|
||||
);
|
||||
setChainWithDefaultRpcUrl("arbitrum_nova", ChainData("Arbitrum Nova", 42170, "https://nova.arbitrum.io/rpc"));
|
||||
setChainWithDefaultRpcUrl("polygon", ChainData("Polygon", 137, "https://polygon-rpc.com"));
|
||||
setChainWithDefaultRpcUrl(
|
||||
"polygon_amoy", ChainData("Polygon Amoy", 80002, "https://rpc-amoy.polygon.technology")
|
||||
);
|
||||
setChainWithDefaultRpcUrl("avalanche", ChainData("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc"));
|
||||
setChainWithDefaultRpcUrl(
|
||||
"avalanche_fuji", ChainData("Avalanche Fuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc")
|
||||
);
|
||||
setChainWithDefaultRpcUrl(
|
||||
"bnb_smart_chain", ChainData("BNB Smart Chain", 56, "https://bsc-dataseed1.binance.org")
|
||||
);
|
||||
setChainWithDefaultRpcUrl(
|
||||
"bnb_smart_chain_testnet",
|
||||
ChainData("BNB Smart Chain Testnet", 97, "https://rpc.ankr.com/bsc_testnet_chapel")
|
||||
);
|
||||
setChainWithDefaultRpcUrl("gnosis_chain", ChainData("Gnosis Chain", 100, "https://rpc.gnosischain.com"));
|
||||
setChainWithDefaultRpcUrl("moonbeam", ChainData("Moonbeam", 1284, "https://rpc.api.moonbeam.network"));
|
||||
setChainWithDefaultRpcUrl(
|
||||
"moonriver", ChainData("Moonriver", 1285, "https://rpc.api.moonriver.moonbeam.network")
|
||||
);
|
||||
setChainWithDefaultRpcUrl("moonbase", ChainData("Moonbase", 1287, "https://rpc.testnet.moonbeam.network"));
|
||||
setChainWithDefaultRpcUrl("base_sepolia", ChainData("Base Sepolia", 84532, "https://sepolia.base.org"));
|
||||
setChainWithDefaultRpcUrl("base", ChainData("Base", 8453, "https://mainnet.base.org"));
|
||||
setChainWithDefaultRpcUrl("fraxtal", ChainData("Fraxtal", 252, "https://rpc.frax.com"));
|
||||
setChainWithDefaultRpcUrl("fraxtal_testnet", ChainData("Fraxtal Testnet", 2522, "https://rpc.testnet.frax.com"));
|
||||
}
|
||||
|
||||
// set chain info, with priority to chainAlias' rpc url in foundry.toml
|
||||
function setChainWithDefaultRpcUrl(string memory chainAlias, ChainData memory chain) private {
|
||||
string memory rpcUrl = chain.rpcUrl;
|
||||
defaultRpcUrls[chainAlias] = rpcUrl;
|
||||
chain.rpcUrl = "";
|
||||
setChain(chainAlias, chain);
|
||||
chain.rpcUrl = rpcUrl; // restore argument
|
||||
}
|
||||
}
|
||||
817
lib_forge_std/src/StdCheats.sol
Normal file
817
lib_forge_std/src/StdCheats.sol
Normal file
@@ -0,0 +1,817 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {StdStorage, stdStorage} from "./StdStorage.sol";
|
||||
import {console2} from "./console2.sol";
|
||||
import {Vm} from "./Vm.sol";
|
||||
|
||||
abstract contract StdCheatsSafe {
|
||||
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
uint256 private constant UINT256_MAX =
|
||||
115792089237316195423570985008687907853269984665640564039457584007913129639935;
|
||||
|
||||
bool private gasMeteringOff;
|
||||
|
||||
// Data structures to parse Transaction objects from the broadcast artifact
|
||||
// that conform to EIP1559. The Raw structs is what is parsed from the JSON
|
||||
// and then converted to the one that is used by the user for better UX.
|
||||
|
||||
struct RawTx1559 {
|
||||
string[] arguments;
|
||||
address contractAddress;
|
||||
string contractName;
|
||||
// json value name = function
|
||||
string functionSig;
|
||||
bytes32 hash;
|
||||
// json value name = tx
|
||||
RawTx1559Detail txDetail;
|
||||
// json value name = type
|
||||
string opcode;
|
||||
}
|
||||
|
||||
struct RawTx1559Detail {
|
||||
AccessList[] accessList;
|
||||
bytes data;
|
||||
address from;
|
||||
bytes gas;
|
||||
bytes nonce;
|
||||
address to;
|
||||
bytes txType;
|
||||
bytes value;
|
||||
}
|
||||
|
||||
struct Tx1559 {
|
||||
string[] arguments;
|
||||
address contractAddress;
|
||||
string contractName;
|
||||
string functionSig;
|
||||
bytes32 hash;
|
||||
Tx1559Detail txDetail;
|
||||
string opcode;
|
||||
}
|
||||
|
||||
struct Tx1559Detail {
|
||||
AccessList[] accessList;
|
||||
bytes data;
|
||||
address from;
|
||||
uint256 gas;
|
||||
uint256 nonce;
|
||||
address to;
|
||||
uint256 txType;
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
// Data structures to parse Transaction objects from the broadcast artifact
|
||||
// that DO NOT conform to EIP1559. The Raw structs is what is parsed from the JSON
|
||||
// and then converted to the one that is used by the user for better UX.
|
||||
|
||||
struct TxLegacy {
|
||||
string[] arguments;
|
||||
address contractAddress;
|
||||
string contractName;
|
||||
string functionSig;
|
||||
string hash;
|
||||
string opcode;
|
||||
TxDetailLegacy transaction;
|
||||
}
|
||||
|
||||
struct TxDetailLegacy {
|
||||
AccessList[] accessList;
|
||||
uint256 chainId;
|
||||
bytes data;
|
||||
address from;
|
||||
uint256 gas;
|
||||
uint256 gasPrice;
|
||||
bytes32 hash;
|
||||
uint256 nonce;
|
||||
bytes1 opcode;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
uint256 txType;
|
||||
address to;
|
||||
uint8 v;
|
||||
uint256 value;
|
||||
}
|
||||
|
||||
struct AccessList {
|
||||
address accessAddress;
|
||||
bytes32[] storageKeys;
|
||||
}
|
||||
|
||||
// Data structures to parse Receipt objects from the broadcast artifact.
|
||||
// The Raw structs is what is parsed from the JSON
|
||||
// and then converted to the one that is used by the user for better UX.
|
||||
|
||||
struct RawReceipt {
|
||||
bytes32 blockHash;
|
||||
bytes blockNumber;
|
||||
address contractAddress;
|
||||
bytes cumulativeGasUsed;
|
||||
bytes effectiveGasPrice;
|
||||
address from;
|
||||
bytes gasUsed;
|
||||
RawReceiptLog[] logs;
|
||||
bytes logsBloom;
|
||||
bytes status;
|
||||
address to;
|
||||
bytes32 transactionHash;
|
||||
bytes transactionIndex;
|
||||
}
|
||||
|
||||
struct Receipt {
|
||||
bytes32 blockHash;
|
||||
uint256 blockNumber;
|
||||
address contractAddress;
|
||||
uint256 cumulativeGasUsed;
|
||||
uint256 effectiveGasPrice;
|
||||
address from;
|
||||
uint256 gasUsed;
|
||||
ReceiptLog[] logs;
|
||||
bytes logsBloom;
|
||||
uint256 status;
|
||||
address to;
|
||||
bytes32 transactionHash;
|
||||
uint256 transactionIndex;
|
||||
}
|
||||
|
||||
// Data structures to parse the entire broadcast artifact, assuming the
|
||||
// transactions conform to EIP1559.
|
||||
|
||||
struct EIP1559ScriptArtifact {
|
||||
string[] libraries;
|
||||
string path;
|
||||
string[] pending;
|
||||
Receipt[] receipts;
|
||||
uint256 timestamp;
|
||||
Tx1559[] transactions;
|
||||
TxReturn[] txReturns;
|
||||
}
|
||||
|
||||
struct RawEIP1559ScriptArtifact {
|
||||
string[] libraries;
|
||||
string path;
|
||||
string[] pending;
|
||||
RawReceipt[] receipts;
|
||||
TxReturn[] txReturns;
|
||||
uint256 timestamp;
|
||||
RawTx1559[] transactions;
|
||||
}
|
||||
|
||||
struct RawReceiptLog {
|
||||
// json value = address
|
||||
address logAddress;
|
||||
bytes32 blockHash;
|
||||
bytes blockNumber;
|
||||
bytes data;
|
||||
bytes logIndex;
|
||||
bool removed;
|
||||
bytes32[] topics;
|
||||
bytes32 transactionHash;
|
||||
bytes transactionIndex;
|
||||
bytes transactionLogIndex;
|
||||
}
|
||||
|
||||
struct ReceiptLog {
|
||||
// json value = address
|
||||
address logAddress;
|
||||
bytes32 blockHash;
|
||||
uint256 blockNumber;
|
||||
bytes data;
|
||||
uint256 logIndex;
|
||||
bytes32[] topics;
|
||||
uint256 transactionIndex;
|
||||
uint256 transactionLogIndex;
|
||||
bool removed;
|
||||
}
|
||||
|
||||
struct TxReturn {
|
||||
string internalType;
|
||||
string value;
|
||||
}
|
||||
|
||||
struct Account {
|
||||
address addr;
|
||||
uint256 key;
|
||||
}
|
||||
|
||||
enum AddressType {
|
||||
Payable,
|
||||
NonPayable,
|
||||
ZeroAddress,
|
||||
Precompile,
|
||||
ForgeAddress
|
||||
}
|
||||
|
||||
// Checks that `addr` is not blacklisted by token contracts that have a blacklist.
|
||||
function assumeNotBlacklisted(address token, address addr) internal view virtual {
|
||||
// Nothing to check if `token` is not a contract.
|
||||
uint256 tokenCodeSize;
|
||||
assembly {
|
||||
tokenCodeSize := extcodesize(token)
|
||||
}
|
||||
require(tokenCodeSize > 0, "StdCheats assumeNotBlacklisted(address,address): Token address is not a contract.");
|
||||
|
||||
bool success;
|
||||
bytes memory returnData;
|
||||
|
||||
// 4-byte selector for `isBlacklisted(address)`, used by USDC.
|
||||
(success, returnData) = token.staticcall(abi.encodeWithSelector(0xfe575a87, addr));
|
||||
vm.assume(!success || abi.decode(returnData, (bool)) == false);
|
||||
|
||||
// 4-byte selector for `isBlackListed(address)`, used by USDT.
|
||||
(success, returnData) = token.staticcall(abi.encodeWithSelector(0xe47d6060, addr));
|
||||
vm.assume(!success || abi.decode(returnData, (bool)) == false);
|
||||
}
|
||||
|
||||
// Checks that `addr` is not blacklisted by token contracts that have a blacklist.
|
||||
// This is identical to `assumeNotBlacklisted(address,address)` but with a different name, for
|
||||
// backwards compatibility, since this name was used in the original PR which has already has
|
||||
// a release. This function can be removed in a future release once we want a breaking change.
|
||||
function assumeNoBlacklisted(address token, address addr) internal view virtual {
|
||||
assumeNotBlacklisted(token, addr);
|
||||
}
|
||||
|
||||
function assumeAddressIsNot(address addr, AddressType addressType) internal virtual {
|
||||
if (addressType == AddressType.Payable) {
|
||||
assumeNotPayable(addr);
|
||||
} else if (addressType == AddressType.NonPayable) {
|
||||
assumePayable(addr);
|
||||
} else if (addressType == AddressType.ZeroAddress) {
|
||||
assumeNotZeroAddress(addr);
|
||||
} else if (addressType == AddressType.Precompile) {
|
||||
assumeNotPrecompile(addr);
|
||||
} else if (addressType == AddressType.ForgeAddress) {
|
||||
assumeNotForgeAddress(addr);
|
||||
}
|
||||
}
|
||||
|
||||
function assumeAddressIsNot(address addr, AddressType addressType1, AddressType addressType2) internal virtual {
|
||||
assumeAddressIsNot(addr, addressType1);
|
||||
assumeAddressIsNot(addr, addressType2);
|
||||
}
|
||||
|
||||
function assumeAddressIsNot(
|
||||
address addr,
|
||||
AddressType addressType1,
|
||||
AddressType addressType2,
|
||||
AddressType addressType3
|
||||
) internal virtual {
|
||||
assumeAddressIsNot(addr, addressType1);
|
||||
assumeAddressIsNot(addr, addressType2);
|
||||
assumeAddressIsNot(addr, addressType3);
|
||||
}
|
||||
|
||||
function assumeAddressIsNot(
|
||||
address addr,
|
||||
AddressType addressType1,
|
||||
AddressType addressType2,
|
||||
AddressType addressType3,
|
||||
AddressType addressType4
|
||||
) internal virtual {
|
||||
assumeAddressIsNot(addr, addressType1);
|
||||
assumeAddressIsNot(addr, addressType2);
|
||||
assumeAddressIsNot(addr, addressType3);
|
||||
assumeAddressIsNot(addr, addressType4);
|
||||
}
|
||||
|
||||
// This function checks whether an address, `addr`, is payable. It works by sending 1 wei to
|
||||
// `addr` and checking the `success` return value.
|
||||
// NOTE: This function may result in state changes depending on the fallback/receive logic
|
||||
// implemented by `addr`, which should be taken into account when this function is used.
|
||||
function _isPayable(address addr) private returns (bool) {
|
||||
require(
|
||||
addr.balance < UINT256_MAX,
|
||||
"StdCheats _isPayable(address): Balance equals max uint256, so it cannot receive any more funds"
|
||||
);
|
||||
uint256 origBalanceTest = address(this).balance;
|
||||
uint256 origBalanceAddr = address(addr).balance;
|
||||
|
||||
vm.deal(address(this), 1);
|
||||
(bool success,) = payable(addr).call{value: 1}("");
|
||||
|
||||
// reset balances
|
||||
vm.deal(address(this), origBalanceTest);
|
||||
vm.deal(addr, origBalanceAddr);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// NOTE: This function may result in state changes depending on the fallback/receive logic
|
||||
// implemented by `addr`, which should be taken into account when this function is used. See the
|
||||
// `_isPayable` method for more information.
|
||||
function assumePayable(address addr) internal virtual {
|
||||
vm.assume(_isPayable(addr));
|
||||
}
|
||||
|
||||
function assumeNotPayable(address addr) internal virtual {
|
||||
vm.assume(!_isPayable(addr));
|
||||
}
|
||||
|
||||
function assumeNotZeroAddress(address addr) internal pure virtual {
|
||||
vm.assume(addr != address(0));
|
||||
}
|
||||
|
||||
function assumeNotPrecompile(address addr) internal pure virtual {
|
||||
assumeNotPrecompile(addr, _pureChainId());
|
||||
}
|
||||
|
||||
function assumeNotPrecompile(address addr, uint256 chainId) internal pure virtual {
|
||||
// Note: For some chains like Optimism these are technically predeploys (i.e. bytecode placed at a specific
|
||||
// address), but the same rationale for excluding them applies so we include those too.
|
||||
|
||||
// These should be present on all EVM-compatible chains.
|
||||
vm.assume(addr < address(0x1) || addr > address(0x9));
|
||||
|
||||
// forgefmt: disable-start
|
||||
if (chainId == 10 || chainId == 420) {
|
||||
// https://github.com/ethereum-optimism/optimism/blob/eaa371a0184b56b7ca6d9eb9cb0a2b78b2ccd864/op-bindings/predeploys/addresses.go#L6-L21
|
||||
vm.assume(addr < address(0x4200000000000000000000000000000000000000) || addr > address(0x4200000000000000000000000000000000000800));
|
||||
} else if (chainId == 42161 || chainId == 421613) {
|
||||
// https://developer.arbitrum.io/useful-addresses#arbitrum-precompiles-l2-same-on-all-arb-chains
|
||||
vm.assume(addr < address(0x0000000000000000000000000000000000000064) || addr > address(0x0000000000000000000000000000000000000068));
|
||||
} else if (chainId == 43114 || chainId == 43113) {
|
||||
// https://github.com/ava-labs/subnet-evm/blob/47c03fd007ecaa6de2c52ea081596e0a88401f58/precompile/params.go#L18-L59
|
||||
vm.assume(addr < address(0x0100000000000000000000000000000000000000) || addr > address(0x01000000000000000000000000000000000000ff));
|
||||
vm.assume(addr < address(0x0200000000000000000000000000000000000000) || addr > address(0x02000000000000000000000000000000000000FF));
|
||||
vm.assume(addr < address(0x0300000000000000000000000000000000000000) || addr > address(0x03000000000000000000000000000000000000Ff));
|
||||
}
|
||||
// forgefmt: disable-end
|
||||
}
|
||||
|
||||
function assumeNotForgeAddress(address addr) internal pure virtual {
|
||||
// vm, console, and Create2Deployer addresses
|
||||
vm.assume(
|
||||
addr != address(vm) && addr != 0x000000000000000000636F6e736F6c652e6c6f67
|
||||
&& addr != 0x4e59b44847b379578588920cA78FbF26c0B4956C
|
||||
);
|
||||
}
|
||||
|
||||
function readEIP1559ScriptArtifact(string memory path)
|
||||
internal
|
||||
view
|
||||
virtual
|
||||
returns (EIP1559ScriptArtifact memory)
|
||||
{
|
||||
string memory data = vm.readFile(path);
|
||||
bytes memory parsedData = vm.parseJson(data);
|
||||
RawEIP1559ScriptArtifact memory rawArtifact = abi.decode(parsedData, (RawEIP1559ScriptArtifact));
|
||||
EIP1559ScriptArtifact memory artifact;
|
||||
artifact.libraries = rawArtifact.libraries;
|
||||
artifact.path = rawArtifact.path;
|
||||
artifact.timestamp = rawArtifact.timestamp;
|
||||
artifact.pending = rawArtifact.pending;
|
||||
artifact.txReturns = rawArtifact.txReturns;
|
||||
artifact.receipts = rawToConvertedReceipts(rawArtifact.receipts);
|
||||
artifact.transactions = rawToConvertedEIPTx1559s(rawArtifact.transactions);
|
||||
return artifact;
|
||||
}
|
||||
|
||||
function rawToConvertedEIPTx1559s(RawTx1559[] memory rawTxs) internal pure virtual returns (Tx1559[] memory) {
|
||||
Tx1559[] memory txs = new Tx1559[](rawTxs.length);
|
||||
for (uint256 i; i < rawTxs.length; i++) {
|
||||
txs[i] = rawToConvertedEIPTx1559(rawTxs[i]);
|
||||
}
|
||||
return txs;
|
||||
}
|
||||
|
||||
function rawToConvertedEIPTx1559(RawTx1559 memory rawTx) internal pure virtual returns (Tx1559 memory) {
|
||||
Tx1559 memory transaction;
|
||||
transaction.arguments = rawTx.arguments;
|
||||
transaction.contractName = rawTx.contractName;
|
||||
transaction.functionSig = rawTx.functionSig;
|
||||
transaction.hash = rawTx.hash;
|
||||
transaction.txDetail = rawToConvertedEIP1559Detail(rawTx.txDetail);
|
||||
transaction.opcode = rawTx.opcode;
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function rawToConvertedEIP1559Detail(RawTx1559Detail memory rawDetail)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
returns (Tx1559Detail memory)
|
||||
{
|
||||
Tx1559Detail memory txDetail;
|
||||
txDetail.data = rawDetail.data;
|
||||
txDetail.from = rawDetail.from;
|
||||
txDetail.to = rawDetail.to;
|
||||
txDetail.nonce = _bytesToUint(rawDetail.nonce);
|
||||
txDetail.txType = _bytesToUint(rawDetail.txType);
|
||||
txDetail.value = _bytesToUint(rawDetail.value);
|
||||
txDetail.gas = _bytesToUint(rawDetail.gas);
|
||||
txDetail.accessList = rawDetail.accessList;
|
||||
return txDetail;
|
||||
}
|
||||
|
||||
function readTx1559s(string memory path) internal view virtual returns (Tx1559[] memory) {
|
||||
string memory deployData = vm.readFile(path);
|
||||
bytes memory parsedDeployData = vm.parseJson(deployData, ".transactions");
|
||||
RawTx1559[] memory rawTxs = abi.decode(parsedDeployData, (RawTx1559[]));
|
||||
return rawToConvertedEIPTx1559s(rawTxs);
|
||||
}
|
||||
|
||||
function readTx1559(string memory path, uint256 index) internal view virtual returns (Tx1559 memory) {
|
||||
string memory deployData = vm.readFile(path);
|
||||
string memory key = string(abi.encodePacked(".transactions[", vm.toString(index), "]"));
|
||||
bytes memory parsedDeployData = vm.parseJson(deployData, key);
|
||||
RawTx1559 memory rawTx = abi.decode(parsedDeployData, (RawTx1559));
|
||||
return rawToConvertedEIPTx1559(rawTx);
|
||||
}
|
||||
|
||||
// Analogous to readTransactions, but for receipts.
|
||||
function readReceipts(string memory path) internal view virtual returns (Receipt[] memory) {
|
||||
string memory deployData = vm.readFile(path);
|
||||
bytes memory parsedDeployData = vm.parseJson(deployData, ".receipts");
|
||||
RawReceipt[] memory rawReceipts = abi.decode(parsedDeployData, (RawReceipt[]));
|
||||
return rawToConvertedReceipts(rawReceipts);
|
||||
}
|
||||
|
||||
function readReceipt(string memory path, uint256 index) internal view virtual returns (Receipt memory) {
|
||||
string memory deployData = vm.readFile(path);
|
||||
string memory key = string(abi.encodePacked(".receipts[", vm.toString(index), "]"));
|
||||
bytes memory parsedDeployData = vm.parseJson(deployData, key);
|
||||
RawReceipt memory rawReceipt = abi.decode(parsedDeployData, (RawReceipt));
|
||||
return rawToConvertedReceipt(rawReceipt);
|
||||
}
|
||||
|
||||
function rawToConvertedReceipts(RawReceipt[] memory rawReceipts) internal pure virtual returns (Receipt[] memory) {
|
||||
Receipt[] memory receipts = new Receipt[](rawReceipts.length);
|
||||
for (uint256 i; i < rawReceipts.length; i++) {
|
||||
receipts[i] = rawToConvertedReceipt(rawReceipts[i]);
|
||||
}
|
||||
return receipts;
|
||||
}
|
||||
|
||||
function rawToConvertedReceipt(RawReceipt memory rawReceipt) internal pure virtual returns (Receipt memory) {
|
||||
Receipt memory receipt;
|
||||
receipt.blockHash = rawReceipt.blockHash;
|
||||
receipt.to = rawReceipt.to;
|
||||
receipt.from = rawReceipt.from;
|
||||
receipt.contractAddress = rawReceipt.contractAddress;
|
||||
receipt.effectiveGasPrice = _bytesToUint(rawReceipt.effectiveGasPrice);
|
||||
receipt.cumulativeGasUsed = _bytesToUint(rawReceipt.cumulativeGasUsed);
|
||||
receipt.gasUsed = _bytesToUint(rawReceipt.gasUsed);
|
||||
receipt.status = _bytesToUint(rawReceipt.status);
|
||||
receipt.transactionIndex = _bytesToUint(rawReceipt.transactionIndex);
|
||||
receipt.blockNumber = _bytesToUint(rawReceipt.blockNumber);
|
||||
receipt.logs = rawToConvertedReceiptLogs(rawReceipt.logs);
|
||||
receipt.logsBloom = rawReceipt.logsBloom;
|
||||
receipt.transactionHash = rawReceipt.transactionHash;
|
||||
return receipt;
|
||||
}
|
||||
|
||||
function rawToConvertedReceiptLogs(RawReceiptLog[] memory rawLogs)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
returns (ReceiptLog[] memory)
|
||||
{
|
||||
ReceiptLog[] memory logs = new ReceiptLog[](rawLogs.length);
|
||||
for (uint256 i; i < rawLogs.length; i++) {
|
||||
logs[i].logAddress = rawLogs[i].logAddress;
|
||||
logs[i].blockHash = rawLogs[i].blockHash;
|
||||
logs[i].blockNumber = _bytesToUint(rawLogs[i].blockNumber);
|
||||
logs[i].data = rawLogs[i].data;
|
||||
logs[i].logIndex = _bytesToUint(rawLogs[i].logIndex);
|
||||
logs[i].topics = rawLogs[i].topics;
|
||||
logs[i].transactionIndex = _bytesToUint(rawLogs[i].transactionIndex);
|
||||
logs[i].transactionLogIndex = _bytesToUint(rawLogs[i].transactionLogIndex);
|
||||
logs[i].removed = rawLogs[i].removed;
|
||||
}
|
||||
return logs;
|
||||
}
|
||||
|
||||
// Deploy a contract by fetching the contract bytecode from
|
||||
// the artifacts directory
|
||||
// e.g. `deployCode(code, abi.encode(arg1,arg2,arg3))`
|
||||
function deployCode(string memory what, bytes memory args) internal virtual returns (address addr) {
|
||||
bytes memory bytecode = abi.encodePacked(vm.getCode(what), args);
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
addr := create(0, add(bytecode, 0x20), mload(bytecode))
|
||||
}
|
||||
|
||||
require(addr != address(0), "StdCheats deployCode(string,bytes): Deployment failed.");
|
||||
}
|
||||
|
||||
function deployCode(string memory what) internal virtual returns (address addr) {
|
||||
bytes memory bytecode = vm.getCode(what);
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
addr := create(0, add(bytecode, 0x20), mload(bytecode))
|
||||
}
|
||||
|
||||
require(addr != address(0), "StdCheats deployCode(string): Deployment failed.");
|
||||
}
|
||||
|
||||
/// @dev deploy contract with value on construction
|
||||
function deployCode(string memory what, bytes memory args, uint256 val) internal virtual returns (address addr) {
|
||||
bytes memory bytecode = abi.encodePacked(vm.getCode(what), args);
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
addr := create(val, add(bytecode, 0x20), mload(bytecode))
|
||||
}
|
||||
|
||||
require(addr != address(0), "StdCheats deployCode(string,bytes,uint256): Deployment failed.");
|
||||
}
|
||||
|
||||
function deployCode(string memory what, uint256 val) internal virtual returns (address addr) {
|
||||
bytes memory bytecode = vm.getCode(what);
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
addr := create(val, add(bytecode, 0x20), mload(bytecode))
|
||||
}
|
||||
|
||||
require(addr != address(0), "StdCheats deployCode(string,uint256): Deployment failed.");
|
||||
}
|
||||
|
||||
// creates a labeled address and the corresponding private key
|
||||
function makeAddrAndKey(string memory name) internal virtual returns (address addr, uint256 privateKey) {
|
||||
privateKey = uint256(keccak256(abi.encodePacked(name)));
|
||||
addr = vm.addr(privateKey);
|
||||
vm.label(addr, name);
|
||||
}
|
||||
|
||||
// creates a labeled address
|
||||
function makeAddr(string memory name) internal virtual returns (address addr) {
|
||||
(addr,) = makeAddrAndKey(name);
|
||||
}
|
||||
|
||||
// Destroys an account immediately, sending the balance to beneficiary.
|
||||
// Destroying means: balance will be zero, code will be empty, and nonce will be 0
|
||||
// This is similar to selfdestruct but not identical: selfdestruct destroys code and nonce
|
||||
// only after tx ends, this will run immediately.
|
||||
function destroyAccount(address who, address beneficiary) internal virtual {
|
||||
uint256 currBalance = who.balance;
|
||||
vm.etch(who, abi.encode());
|
||||
vm.deal(who, 0);
|
||||
vm.resetNonce(who);
|
||||
|
||||
uint256 beneficiaryBalance = beneficiary.balance;
|
||||
vm.deal(beneficiary, currBalance + beneficiaryBalance);
|
||||
}
|
||||
|
||||
// creates a struct containing both a labeled address and the corresponding private key
|
||||
function makeAccount(string memory name) internal virtual returns (Account memory account) {
|
||||
(account.addr, account.key) = makeAddrAndKey(name);
|
||||
}
|
||||
|
||||
function deriveRememberKey(string memory mnemonic, uint32 index)
|
||||
internal
|
||||
virtual
|
||||
returns (address who, uint256 privateKey)
|
||||
{
|
||||
privateKey = vm.deriveKey(mnemonic, index);
|
||||
who = vm.rememberKey(privateKey);
|
||||
}
|
||||
|
||||
function _bytesToUint(bytes memory b) private pure returns (uint256) {
|
||||
require(b.length <= 32, "StdCheats _bytesToUint(bytes): Bytes length exceeds 32.");
|
||||
return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256));
|
||||
}
|
||||
|
||||
function isFork() internal view virtual returns (bool status) {
|
||||
try vm.activeFork() {
|
||||
status = true;
|
||||
} catch (bytes memory) {}
|
||||
}
|
||||
|
||||
modifier skipWhenForking() {
|
||||
if (!isFork()) {
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
modifier skipWhenNotForking() {
|
||||
if (isFork()) {
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
modifier noGasMetering() {
|
||||
vm.pauseGasMetering();
|
||||
// To prevent turning gas monitoring back on with nested functions that use this modifier,
|
||||
// we check if gasMetering started in the off position. If it did, we don't want to turn
|
||||
// it back on until we exit the top level function that used the modifier
|
||||
//
|
||||
// i.e. funcA() noGasMetering { funcB() }, where funcB has noGasMetering as well.
|
||||
// funcA will have `gasStartedOff` as false, funcB will have it as true,
|
||||
// so we only turn metering back on at the end of the funcA
|
||||
bool gasStartedOff = gasMeteringOff;
|
||||
gasMeteringOff = true;
|
||||
|
||||
_;
|
||||
|
||||
// if gas metering was on when this modifier was called, turn it back on at the end
|
||||
if (!gasStartedOff) {
|
||||
gasMeteringOff = false;
|
||||
vm.resumeGasMetering();
|
||||
}
|
||||
}
|
||||
|
||||
// We use this complex approach of `_viewChainId` and `_pureChainId` to ensure there are no
|
||||
// compiler warnings when accessing chain ID in any solidity version supported by forge-std. We
|
||||
// can't simply access the chain ID in a normal view or pure function because the solc View Pure
|
||||
// Checker changed `chainid` from pure to view in 0.8.0.
|
||||
function _viewChainId() private view returns (uint256 chainId) {
|
||||
// Assembly required since `block.chainid` was introduced in 0.8.0.
|
||||
assembly {
|
||||
chainId := chainid()
|
||||
}
|
||||
|
||||
address(this); // Silence warnings in older Solc versions.
|
||||
}
|
||||
|
||||
function _pureChainId() private pure returns (uint256 chainId) {
|
||||
function() internal view returns (uint256) fnIn = _viewChainId;
|
||||
function() internal pure returns (uint256) pureChainId;
|
||||
assembly {
|
||||
pureChainId := fnIn
|
||||
}
|
||||
chainId = pureChainId();
|
||||
}
|
||||
}
|
||||
|
||||
// Wrappers around cheatcodes to avoid footguns
|
||||
abstract contract StdCheats is StdCheatsSafe {
|
||||
using stdStorage for StdStorage;
|
||||
|
||||
StdStorage private stdstore;
|
||||
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
address private constant CONSOLE2_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67;
|
||||
|
||||
// Skip forward or rewind time by the specified number of seconds
|
||||
function skip(uint256 time) internal virtual {
|
||||
vm.warp(block.timestamp + time);
|
||||
}
|
||||
|
||||
function rewind(uint256 time) internal virtual {
|
||||
vm.warp(block.timestamp - time);
|
||||
}
|
||||
|
||||
// Setup a prank from an address that has some ether
|
||||
function hoax(address msgSender) internal virtual {
|
||||
vm.deal(msgSender, 1 << 128);
|
||||
vm.prank(msgSender);
|
||||
}
|
||||
|
||||
function hoax(address msgSender, uint256 give) internal virtual {
|
||||
vm.deal(msgSender, give);
|
||||
vm.prank(msgSender);
|
||||
}
|
||||
|
||||
function hoax(address msgSender, address origin) internal virtual {
|
||||
vm.deal(msgSender, 1 << 128);
|
||||
vm.prank(msgSender, origin);
|
||||
}
|
||||
|
||||
function hoax(address msgSender, address origin, uint256 give) internal virtual {
|
||||
vm.deal(msgSender, give);
|
||||
vm.prank(msgSender, origin);
|
||||
}
|
||||
|
||||
// Start perpetual prank from an address that has some ether
|
||||
function startHoax(address msgSender) internal virtual {
|
||||
vm.deal(msgSender, 1 << 128);
|
||||
vm.startPrank(msgSender);
|
||||
}
|
||||
|
||||
function startHoax(address msgSender, uint256 give) internal virtual {
|
||||
vm.deal(msgSender, give);
|
||||
vm.startPrank(msgSender);
|
||||
}
|
||||
|
||||
// Start perpetual prank from an address that has some ether
|
||||
// tx.origin is set to the origin parameter
|
||||
function startHoax(address msgSender, address origin) internal virtual {
|
||||
vm.deal(msgSender, 1 << 128);
|
||||
vm.startPrank(msgSender, origin);
|
||||
}
|
||||
|
||||
function startHoax(address msgSender, address origin, uint256 give) internal virtual {
|
||||
vm.deal(msgSender, give);
|
||||
vm.startPrank(msgSender, origin);
|
||||
}
|
||||
|
||||
function changePrank(address msgSender) internal virtual {
|
||||
console2_log_StdCheats("changePrank is deprecated. Please use vm.startPrank instead.");
|
||||
vm.stopPrank();
|
||||
vm.startPrank(msgSender);
|
||||
}
|
||||
|
||||
function changePrank(address msgSender, address txOrigin) internal virtual {
|
||||
vm.stopPrank();
|
||||
vm.startPrank(msgSender, txOrigin);
|
||||
}
|
||||
|
||||
// The same as Vm's `deal`
|
||||
// Use the alternative signature for ERC20 tokens
|
||||
function deal(address to, uint256 give) internal virtual {
|
||||
vm.deal(to, give);
|
||||
}
|
||||
|
||||
// Set the balance of an account for any ERC20 token
|
||||
// Use the alternative signature to update `totalSupply`
|
||||
function deal(address token, address to, uint256 give) internal virtual {
|
||||
deal(token, to, give, false);
|
||||
}
|
||||
|
||||
// Set the balance of an account for any ERC1155 token
|
||||
// Use the alternative signature to update `totalSupply`
|
||||
function dealERC1155(address token, address to, uint256 id, uint256 give) internal virtual {
|
||||
dealERC1155(token, to, id, give, false);
|
||||
}
|
||||
|
||||
function deal(address token, address to, uint256 give, bool adjust) internal virtual {
|
||||
// get current balance
|
||||
(, bytes memory balData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to));
|
||||
uint256 prevBal = abi.decode(balData, (uint256));
|
||||
|
||||
// update balance
|
||||
stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(give);
|
||||
|
||||
// update total supply
|
||||
if (adjust) {
|
||||
(, bytes memory totSupData) = token.staticcall(abi.encodeWithSelector(0x18160ddd));
|
||||
uint256 totSup = abi.decode(totSupData, (uint256));
|
||||
if (give < prevBal) {
|
||||
totSup -= (prevBal - give);
|
||||
} else {
|
||||
totSup += (give - prevBal);
|
||||
}
|
||||
stdstore.target(token).sig(0x18160ddd).checked_write(totSup);
|
||||
}
|
||||
}
|
||||
|
||||
function dealERC1155(address token, address to, uint256 id, uint256 give, bool adjust) internal virtual {
|
||||
// get current balance
|
||||
(, bytes memory balData) = token.staticcall(abi.encodeWithSelector(0x00fdd58e, to, id));
|
||||
uint256 prevBal = abi.decode(balData, (uint256));
|
||||
|
||||
// update balance
|
||||
stdstore.target(token).sig(0x00fdd58e).with_key(to).with_key(id).checked_write(give);
|
||||
|
||||
// update total supply
|
||||
if (adjust) {
|
||||
(, bytes memory totSupData) = token.staticcall(abi.encodeWithSelector(0xbd85b039, id));
|
||||
require(
|
||||
totSupData.length != 0,
|
||||
"StdCheats deal(address,address,uint,uint,bool): target contract is not ERC1155Supply."
|
||||
);
|
||||
uint256 totSup = abi.decode(totSupData, (uint256));
|
||||
if (give < prevBal) {
|
||||
totSup -= (prevBal - give);
|
||||
} else {
|
||||
totSup += (give - prevBal);
|
||||
}
|
||||
stdstore.target(token).sig(0xbd85b039).with_key(id).checked_write(totSup);
|
||||
}
|
||||
}
|
||||
|
||||
function dealERC721(address token, address to, uint256 id) internal virtual {
|
||||
// check if token id is already minted and the actual owner.
|
||||
(bool successMinted, bytes memory ownerData) = token.staticcall(abi.encodeWithSelector(0x6352211e, id));
|
||||
require(successMinted, "StdCheats deal(address,address,uint,bool): id not minted.");
|
||||
|
||||
// get owner current balance
|
||||
(, bytes memory fromBalData) =
|
||||
token.staticcall(abi.encodeWithSelector(0x70a08231, abi.decode(ownerData, (address))));
|
||||
uint256 fromPrevBal = abi.decode(fromBalData, (uint256));
|
||||
|
||||
// get new user current balance
|
||||
(, bytes memory toBalData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to));
|
||||
uint256 toPrevBal = abi.decode(toBalData, (uint256));
|
||||
|
||||
// update balances
|
||||
stdstore.target(token).sig(0x70a08231).with_key(abi.decode(ownerData, (address))).checked_write(--fromPrevBal);
|
||||
stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(++toPrevBal);
|
||||
|
||||
// update owner
|
||||
stdstore.target(token).sig(0x6352211e).with_key(id).checked_write(to);
|
||||
}
|
||||
|
||||
function deployCodeTo(string memory what, address where) internal virtual {
|
||||
deployCodeTo(what, "", 0, where);
|
||||
}
|
||||
|
||||
function deployCodeTo(string memory what, bytes memory args, address where) internal virtual {
|
||||
deployCodeTo(what, args, 0, where);
|
||||
}
|
||||
|
||||
function deployCodeTo(string memory what, bytes memory args, uint256 value, address where) internal virtual {
|
||||
bytes memory creationCode = vm.getCode(what);
|
||||
vm.etch(where, abi.encodePacked(creationCode, args));
|
||||
(bool success, bytes memory runtimeBytecode) = where.call{value: value}("");
|
||||
require(success, "StdCheats deployCodeTo(string,bytes,uint256,address): Failed to create runtime bytecode.");
|
||||
vm.etch(where, runtimeBytecode);
|
||||
}
|
||||
|
||||
// Used to prevent the compilation of console, which shortens the compilation time when console is not used elsewhere.
|
||||
function console2_log_StdCheats(string memory p0) private view {
|
||||
(bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string)", p0));
|
||||
status;
|
||||
}
|
||||
}
|
||||
15
lib_forge_std/src/StdError.sol
Normal file
15
lib_forge_std/src/StdError.sol
Normal file
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
library stdError {
|
||||
bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01);
|
||||
bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11);
|
||||
bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12);
|
||||
bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21);
|
||||
bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22);
|
||||
bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31);
|
||||
bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32);
|
||||
bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41);
|
||||
bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51);
|
||||
}
|
||||
107
lib_forge_std/src/StdInvariant.sol
Normal file
107
lib_forge_std/src/StdInvariant.sol
Normal file
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
abstract contract StdInvariant {
|
||||
struct FuzzSelector {
|
||||
address addr;
|
||||
bytes4[] selectors;
|
||||
}
|
||||
|
||||
struct FuzzInterface {
|
||||
address addr;
|
||||
string[] artifacts;
|
||||
}
|
||||
|
||||
address[] private _excludedContracts;
|
||||
address[] private _excludedSenders;
|
||||
address[] private _targetedContracts;
|
||||
address[] private _targetedSenders;
|
||||
|
||||
string[] private _excludedArtifacts;
|
||||
string[] private _targetedArtifacts;
|
||||
|
||||
FuzzSelector[] private _targetedArtifactSelectors;
|
||||
FuzzSelector[] private _targetedSelectors;
|
||||
|
||||
FuzzInterface[] private _targetedInterfaces;
|
||||
|
||||
// Functions for users:
|
||||
// These are intended to be called in tests.
|
||||
|
||||
function excludeContract(address newExcludedContract_) internal {
|
||||
_excludedContracts.push(newExcludedContract_);
|
||||
}
|
||||
|
||||
function excludeSender(address newExcludedSender_) internal {
|
||||
_excludedSenders.push(newExcludedSender_);
|
||||
}
|
||||
|
||||
function excludeArtifact(string memory newExcludedArtifact_) internal {
|
||||
_excludedArtifacts.push(newExcludedArtifact_);
|
||||
}
|
||||
|
||||
function targetArtifact(string memory newTargetedArtifact_) internal {
|
||||
_targetedArtifacts.push(newTargetedArtifact_);
|
||||
}
|
||||
|
||||
function targetArtifactSelector(FuzzSelector memory newTargetedArtifactSelector_) internal {
|
||||
_targetedArtifactSelectors.push(newTargetedArtifactSelector_);
|
||||
}
|
||||
|
||||
function targetContract(address newTargetedContract_) internal {
|
||||
_targetedContracts.push(newTargetedContract_);
|
||||
}
|
||||
|
||||
function targetSelector(FuzzSelector memory newTargetedSelector_) internal {
|
||||
_targetedSelectors.push(newTargetedSelector_);
|
||||
}
|
||||
|
||||
function targetSender(address newTargetedSender_) internal {
|
||||
_targetedSenders.push(newTargetedSender_);
|
||||
}
|
||||
|
||||
function targetInterface(FuzzInterface memory newTargetedInterface_) internal {
|
||||
_targetedInterfaces.push(newTargetedInterface_);
|
||||
}
|
||||
|
||||
// Functions for forge:
|
||||
// These are called by forge to run invariant tests and don't need to be called in tests.
|
||||
|
||||
function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) {
|
||||
excludedArtifacts_ = _excludedArtifacts;
|
||||
}
|
||||
|
||||
function excludeContracts() public view returns (address[] memory excludedContracts_) {
|
||||
excludedContracts_ = _excludedContracts;
|
||||
}
|
||||
|
||||
function excludeSenders() public view returns (address[] memory excludedSenders_) {
|
||||
excludedSenders_ = _excludedSenders;
|
||||
}
|
||||
|
||||
function targetArtifacts() public view returns (string[] memory targetedArtifacts_) {
|
||||
targetedArtifacts_ = _targetedArtifacts;
|
||||
}
|
||||
|
||||
function targetArtifactSelectors() public view returns (FuzzSelector[] memory targetedArtifactSelectors_) {
|
||||
targetedArtifactSelectors_ = _targetedArtifactSelectors;
|
||||
}
|
||||
|
||||
function targetContracts() public view returns (address[] memory targetedContracts_) {
|
||||
targetedContracts_ = _targetedContracts;
|
||||
}
|
||||
|
||||
function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) {
|
||||
targetedSelectors_ = _targetedSelectors;
|
||||
}
|
||||
|
||||
function targetSenders() public view returns (address[] memory targetedSenders_) {
|
||||
targetedSenders_ = _targetedSenders;
|
||||
}
|
||||
|
||||
function targetInterfaces() public view returns (FuzzInterface[] memory targetedInterfaces_) {
|
||||
targetedInterfaces_ = _targetedInterfaces;
|
||||
}
|
||||
}
|
||||
179
lib_forge_std/src/StdJson.sol
Normal file
179
lib_forge_std/src/StdJson.sol
Normal file
@@ -0,0 +1,179 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {VmSafe} from "./Vm.sol";
|
||||
|
||||
// Helpers for parsing and writing JSON files
|
||||
// To parse:
|
||||
// ```
|
||||
// using stdJson for string;
|
||||
// string memory json = vm.readFile("<some_path>");
|
||||
// json.readUint("<json_path>");
|
||||
// ```
|
||||
// To write:
|
||||
// ```
|
||||
// using stdJson for string;
|
||||
// string memory json = "json";
|
||||
// json.serialize("a", uint256(123));
|
||||
// string memory semiFinal = json.serialize("b", string("test"));
|
||||
// string memory finalJson = json.serialize("c", semiFinal);
|
||||
// finalJson.write("<some_path>");
|
||||
// ```
|
||||
|
||||
library stdJson {
|
||||
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
function parseRaw(string memory json, string memory key) internal pure returns (bytes memory) {
|
||||
return vm.parseJson(json, key);
|
||||
}
|
||||
|
||||
function readUint(string memory json, string memory key) internal pure returns (uint256) {
|
||||
return vm.parseJsonUint(json, key);
|
||||
}
|
||||
|
||||
function readUintArray(string memory json, string memory key) internal pure returns (uint256[] memory) {
|
||||
return vm.parseJsonUintArray(json, key);
|
||||
}
|
||||
|
||||
function readInt(string memory json, string memory key) internal pure returns (int256) {
|
||||
return vm.parseJsonInt(json, key);
|
||||
}
|
||||
|
||||
function readIntArray(string memory json, string memory key) internal pure returns (int256[] memory) {
|
||||
return vm.parseJsonIntArray(json, key);
|
||||
}
|
||||
|
||||
function readBytes32(string memory json, string memory key) internal pure returns (bytes32) {
|
||||
return vm.parseJsonBytes32(json, key);
|
||||
}
|
||||
|
||||
function readBytes32Array(string memory json, string memory key) internal pure returns (bytes32[] memory) {
|
||||
return vm.parseJsonBytes32Array(json, key);
|
||||
}
|
||||
|
||||
function readString(string memory json, string memory key) internal pure returns (string memory) {
|
||||
return vm.parseJsonString(json, key);
|
||||
}
|
||||
|
||||
function readStringArray(string memory json, string memory key) internal pure returns (string[] memory) {
|
||||
return vm.parseJsonStringArray(json, key);
|
||||
}
|
||||
|
||||
function readAddress(string memory json, string memory key) internal pure returns (address) {
|
||||
return vm.parseJsonAddress(json, key);
|
||||
}
|
||||
|
||||
function readAddressArray(string memory json, string memory key) internal pure returns (address[] memory) {
|
||||
return vm.parseJsonAddressArray(json, key);
|
||||
}
|
||||
|
||||
function readBool(string memory json, string memory key) internal pure returns (bool) {
|
||||
return vm.parseJsonBool(json, key);
|
||||
}
|
||||
|
||||
function readBoolArray(string memory json, string memory key) internal pure returns (bool[] memory) {
|
||||
return vm.parseJsonBoolArray(json, key);
|
||||
}
|
||||
|
||||
function readBytes(string memory json, string memory key) internal pure returns (bytes memory) {
|
||||
return vm.parseJsonBytes(json, key);
|
||||
}
|
||||
|
||||
function readBytesArray(string memory json, string memory key) internal pure returns (bytes[] memory) {
|
||||
return vm.parseJsonBytesArray(json, key);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory rootObject) internal returns (string memory) {
|
||||
return vm.serializeJson(jsonKey, rootObject);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) {
|
||||
return vm.serializeBool(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bool[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeBool(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) {
|
||||
return vm.serializeUint(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, uint256[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeUint(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) {
|
||||
return vm.serializeInt(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, int256[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeInt(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) {
|
||||
return vm.serializeAddress(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, address[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeAddress(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) {
|
||||
return vm.serializeBytes32(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes32[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeBytes32(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) {
|
||||
return vm.serializeBytes(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeBytes(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, string memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeString(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, string[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeString(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function write(string memory jsonKey, string memory path) internal {
|
||||
vm.writeJson(jsonKey, path);
|
||||
}
|
||||
|
||||
function write(string memory jsonKey, string memory path, string memory valueKey) internal {
|
||||
vm.writeJson(jsonKey, path, valueKey);
|
||||
}
|
||||
}
|
||||
43
lib_forge_std/src/StdMath.sol
Normal file
43
lib_forge_std/src/StdMath.sol
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
library stdMath {
|
||||
int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968;
|
||||
|
||||
function abs(int256 a) internal pure returns (uint256) {
|
||||
// Required or it will fail when `a = type(int256).min`
|
||||
if (a == INT256_MIN) {
|
||||
return 57896044618658097711785492504343953926634992332820282019728792003956564819968;
|
||||
}
|
||||
|
||||
return uint256(a > 0 ? a : -a);
|
||||
}
|
||||
|
||||
function delta(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a > b ? a - b : b - a;
|
||||
}
|
||||
|
||||
function delta(int256 a, int256 b) internal pure returns (uint256) {
|
||||
// a and b are of the same sign
|
||||
// this works thanks to two's complement, the left-most bit is the sign bit
|
||||
if ((a ^ b) > -1) {
|
||||
return delta(abs(a), abs(b));
|
||||
}
|
||||
|
||||
// a and b are of opposite signs
|
||||
return abs(a) + abs(b);
|
||||
}
|
||||
|
||||
function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
uint256 absDelta = delta(a, b);
|
||||
|
||||
return absDelta * 1e18 / b;
|
||||
}
|
||||
|
||||
function percentDelta(int256 a, int256 b) internal pure returns (uint256) {
|
||||
uint256 absDelta = delta(a, b);
|
||||
uint256 absB = abs(b);
|
||||
|
||||
return absDelta * 1e18 / absB;
|
||||
}
|
||||
}
|
||||
473
lib_forge_std/src/StdStorage.sol
Normal file
473
lib_forge_std/src/StdStorage.sol
Normal file
@@ -0,0 +1,473 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
import {Vm} from "./Vm.sol";
|
||||
|
||||
struct FindData {
|
||||
uint256 slot;
|
||||
uint256 offsetLeft;
|
||||
uint256 offsetRight;
|
||||
bool found;
|
||||
}
|
||||
|
||||
struct StdStorage {
|
||||
mapping(address => mapping(bytes4 => mapping(bytes32 => FindData))) finds;
|
||||
bytes32[] _keys;
|
||||
bytes4 _sig;
|
||||
uint256 _depth;
|
||||
address _target;
|
||||
bytes32 _set;
|
||||
bool _enable_packed_slots;
|
||||
bytes _calldata;
|
||||
}
|
||||
|
||||
library stdStorageSafe {
|
||||
event SlotFound(address who, bytes4 fsig, bytes32 keysHash, uint256 slot);
|
||||
event WARNING_UninitedSlot(address who, uint256 slot);
|
||||
|
||||
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
uint256 constant UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
|
||||
|
||||
function sigs(string memory sigStr) internal pure returns (bytes4) {
|
||||
return bytes4(keccak256(bytes(sigStr)));
|
||||
}
|
||||
|
||||
function getCallParams(StdStorage storage self) internal view returns (bytes memory) {
|
||||
if (self._calldata.length == 0) {
|
||||
return flatten(self._keys);
|
||||
} else {
|
||||
return self._calldata;
|
||||
}
|
||||
}
|
||||
|
||||
// Calls target contract with configured parameters
|
||||
function callTarget(StdStorage storage self) internal view returns (bool, bytes32) {
|
||||
bytes memory cald = abi.encodePacked(self._sig, getCallParams(self));
|
||||
(bool success, bytes memory rdat) = self._target.staticcall(cald);
|
||||
bytes32 result = bytesToBytes32(rdat, 32 * self._depth);
|
||||
|
||||
return (success, result);
|
||||
}
|
||||
|
||||
// Tries mutating slot value to determine if the targeted value is stored in it.
|
||||
// If current value is 0, then we are setting slot value to type(uint256).max
|
||||
// Otherwise, we set it to 0. That way, return value should always be affected.
|
||||
function checkSlotMutatesCall(StdStorage storage self, bytes32 slot) internal returns (bool) {
|
||||
bytes32 prevSlotValue = vm.load(self._target, slot);
|
||||
(bool success, bytes32 prevReturnValue) = callTarget(self);
|
||||
|
||||
bytes32 testVal = prevReturnValue == bytes32(0) ? bytes32(UINT256_MAX) : bytes32(0);
|
||||
vm.store(self._target, slot, testVal);
|
||||
|
||||
(, bytes32 newReturnValue) = callTarget(self);
|
||||
|
||||
vm.store(self._target, slot, prevSlotValue);
|
||||
|
||||
return (success && (prevReturnValue != newReturnValue));
|
||||
}
|
||||
|
||||
// Tries setting one of the bits in slot to 1 until return value changes.
|
||||
// Index of resulted bit is an offset packed slot has from left/right side
|
||||
function findOffset(StdStorage storage self, bytes32 slot, bool left) internal returns (bool, uint256) {
|
||||
for (uint256 offset = 0; offset < 256; offset++) {
|
||||
uint256 valueToPut = left ? (1 << (255 - offset)) : (1 << offset);
|
||||
vm.store(self._target, slot, bytes32(valueToPut));
|
||||
|
||||
(bool success, bytes32 data) = callTarget(self);
|
||||
|
||||
if (success && (uint256(data) > 0)) {
|
||||
return (true, offset);
|
||||
}
|
||||
}
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
function findOffsets(StdStorage storage self, bytes32 slot) internal returns (bool, uint256, uint256) {
|
||||
bytes32 prevSlotValue = vm.load(self._target, slot);
|
||||
|
||||
(bool foundLeft, uint256 offsetLeft) = findOffset(self, slot, true);
|
||||
(bool foundRight, uint256 offsetRight) = findOffset(self, slot, false);
|
||||
|
||||
// `findOffset` may mutate slot value, so we are setting it to initial value
|
||||
vm.store(self._target, slot, prevSlotValue);
|
||||
return (foundLeft && foundRight, offsetLeft, offsetRight);
|
||||
}
|
||||
|
||||
function find(StdStorage storage self) internal returns (FindData storage) {
|
||||
return find(self, true);
|
||||
}
|
||||
|
||||
/// @notice find an arbitrary storage slot given a function sig, input data, address of the contract and a value to check against
|
||||
// slot complexity:
|
||||
// if flat, will be bytes32(uint256(uint));
|
||||
// if map, will be keccak256(abi.encode(key, uint(slot)));
|
||||
// if deep map, will be keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))));
|
||||
// if map struct, will be bytes32(uint256(keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))))) + structFieldDepth);
|
||||
function find(StdStorage storage self, bool _clear) internal returns (FindData storage) {
|
||||
address who = self._target;
|
||||
bytes4 fsig = self._sig;
|
||||
uint256 field_depth = self._depth;
|
||||
bytes memory params = getCallParams(self);
|
||||
|
||||
// calldata to test against
|
||||
if (self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))].found) {
|
||||
if (_clear) {
|
||||
clear(self);
|
||||
}
|
||||
return self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))];
|
||||
}
|
||||
vm.record();
|
||||
(, bytes32 callResult) = callTarget(self);
|
||||
(bytes32[] memory reads,) = vm.accesses(address(who));
|
||||
|
||||
if (reads.length == 0) {
|
||||
revert("stdStorage find(StdStorage): No storage use detected for target.");
|
||||
} else {
|
||||
for (uint256 i = 0; i < reads.length; i++) {
|
||||
bytes32 prev = vm.load(who, reads[i]);
|
||||
if (prev == bytes32(0)) {
|
||||
emit WARNING_UninitedSlot(who, uint256(reads[i]));
|
||||
}
|
||||
|
||||
if (!checkSlotMutatesCall(self, reads[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
(uint256 offsetLeft, uint256 offsetRight) = (0, 0);
|
||||
|
||||
if (self._enable_packed_slots) {
|
||||
bool found;
|
||||
(found, offsetLeft, offsetRight) = findOffsets(self, reads[i]);
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that value between found offsets is equal to the current call result
|
||||
uint256 curVal = (uint256(prev) & getMaskByOffsets(offsetLeft, offsetRight)) >> offsetRight;
|
||||
|
||||
if (uint256(callResult) != curVal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
emit SlotFound(who, fsig, keccak256(abi.encodePacked(params, field_depth)), uint256(reads[i]));
|
||||
self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))] =
|
||||
FindData(uint256(reads[i]), offsetLeft, offsetRight, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
require(
|
||||
self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))].found,
|
||||
"stdStorage find(StdStorage): Slot(s) not found."
|
||||
);
|
||||
|
||||
if (_clear) {
|
||||
clear(self);
|
||||
}
|
||||
return self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))];
|
||||
}
|
||||
|
||||
function target(StdStorage storage self, address _target) internal returns (StdStorage storage) {
|
||||
self._target = _target;
|
||||
return self;
|
||||
}
|
||||
|
||||
function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) {
|
||||
self._sig = _sig;
|
||||
return self;
|
||||
}
|
||||
|
||||
function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) {
|
||||
self._sig = sigs(_sig);
|
||||
return self;
|
||||
}
|
||||
|
||||
function with_calldata(StdStorage storage self, bytes memory _calldata) internal returns (StdStorage storage) {
|
||||
self._calldata = _calldata;
|
||||
return self;
|
||||
}
|
||||
|
||||
function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) {
|
||||
self._keys.push(bytes32(uint256(uint160(who))));
|
||||
return self;
|
||||
}
|
||||
|
||||
function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) {
|
||||
self._keys.push(bytes32(amt));
|
||||
return self;
|
||||
}
|
||||
|
||||
function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) {
|
||||
self._keys.push(key);
|
||||
return self;
|
||||
}
|
||||
|
||||
function enable_packed_slots(StdStorage storage self) internal returns (StdStorage storage) {
|
||||
self._enable_packed_slots = true;
|
||||
return self;
|
||||
}
|
||||
|
||||
function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) {
|
||||
self._depth = _depth;
|
||||
return self;
|
||||
}
|
||||
|
||||
function read(StdStorage storage self) private returns (bytes memory) {
|
||||
FindData storage data = find(self, false);
|
||||
uint256 mask = getMaskByOffsets(data.offsetLeft, data.offsetRight);
|
||||
uint256 value = (uint256(vm.load(self._target, bytes32(data.slot))) & mask) >> data.offsetRight;
|
||||
clear(self);
|
||||
return abi.encode(value);
|
||||
}
|
||||
|
||||
function read_bytes32(StdStorage storage self) internal returns (bytes32) {
|
||||
return abi.decode(read(self), (bytes32));
|
||||
}
|
||||
|
||||
function read_bool(StdStorage storage self) internal returns (bool) {
|
||||
int256 v = read_int(self);
|
||||
if (v == 0) return false;
|
||||
if (v == 1) return true;
|
||||
revert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool.");
|
||||
}
|
||||
|
||||
function read_address(StdStorage storage self) internal returns (address) {
|
||||
return abi.decode(read(self), (address));
|
||||
}
|
||||
|
||||
function read_uint(StdStorage storage self) internal returns (uint256) {
|
||||
return abi.decode(read(self), (uint256));
|
||||
}
|
||||
|
||||
function read_int(StdStorage storage self) internal returns (int256) {
|
||||
return abi.decode(read(self), (int256));
|
||||
}
|
||||
|
||||
function parent(StdStorage storage self) internal returns (uint256, bytes32) {
|
||||
address who = self._target;
|
||||
uint256 field_depth = self._depth;
|
||||
vm.startMappingRecording();
|
||||
uint256 child = find(self, true).slot - field_depth;
|
||||
(bool found, bytes32 key, bytes32 parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(child));
|
||||
if (!found) {
|
||||
revert(
|
||||
"stdStorage read_bool(StdStorage): Cannot find parent. Make sure you give a slot and startMappingRecording() has been called."
|
||||
);
|
||||
}
|
||||
return (uint256(parent_slot), key);
|
||||
}
|
||||
|
||||
function root(StdStorage storage self) internal returns (uint256) {
|
||||
address who = self._target;
|
||||
uint256 field_depth = self._depth;
|
||||
vm.startMappingRecording();
|
||||
uint256 child = find(self, true).slot - field_depth;
|
||||
bool found;
|
||||
bytes32 root_slot;
|
||||
bytes32 parent_slot;
|
||||
(found,, parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(child));
|
||||
if (!found) {
|
||||
revert(
|
||||
"stdStorage read_bool(StdStorage): Cannot find parent. Make sure you give a slot and startMappingRecording() has been called."
|
||||
);
|
||||
}
|
||||
while (found) {
|
||||
root_slot = parent_slot;
|
||||
(found,, parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(root_slot));
|
||||
}
|
||||
return uint256(root_slot);
|
||||
}
|
||||
|
||||
function bytesToBytes32(bytes memory b, uint256 offset) private pure returns (bytes32) {
|
||||
bytes32 out;
|
||||
|
||||
uint256 max = b.length > 32 ? 32 : b.length;
|
||||
for (uint256 i = 0; i < max; i++) {
|
||||
out |= bytes32(b[offset + i] & 0xFF) >> (i * 8);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function flatten(bytes32[] memory b) private pure returns (bytes memory) {
|
||||
bytes memory result = new bytes(b.length * 32);
|
||||
for (uint256 i = 0; i < b.length; i++) {
|
||||
bytes32 k = b[i];
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
mstore(add(result, add(32, mul(32, i))), k)
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function clear(StdStorage storage self) internal {
|
||||
delete self._target;
|
||||
delete self._sig;
|
||||
delete self._keys;
|
||||
delete self._depth;
|
||||
delete self._enable_packed_slots;
|
||||
delete self._calldata;
|
||||
}
|
||||
|
||||
// Returns mask which contains non-zero bits for values between `offsetLeft` and `offsetRight`
|
||||
// (slotValue & mask) >> offsetRight will be the value of the given packed variable
|
||||
function getMaskByOffsets(uint256 offsetLeft, uint256 offsetRight) internal pure returns (uint256 mask) {
|
||||
// mask = ((1 << (256 - (offsetRight + offsetLeft))) - 1) << offsetRight;
|
||||
// using assembly because (1 << 256) causes overflow
|
||||
assembly {
|
||||
mask := shl(offsetRight, sub(shl(sub(256, add(offsetRight, offsetLeft)), 1), 1))
|
||||
}
|
||||
}
|
||||
|
||||
// Returns slot value with updated packed variable.
|
||||
function getUpdatedSlotValue(bytes32 curValue, uint256 varValue, uint256 offsetLeft, uint256 offsetRight)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 newValue)
|
||||
{
|
||||
return bytes32((uint256(curValue) & ~getMaskByOffsets(offsetLeft, offsetRight)) | (varValue << offsetRight));
|
||||
}
|
||||
}
|
||||
|
||||
library stdStorage {
|
||||
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
function sigs(string memory sigStr) internal pure returns (bytes4) {
|
||||
return stdStorageSafe.sigs(sigStr);
|
||||
}
|
||||
|
||||
function find(StdStorage storage self) internal returns (uint256) {
|
||||
return find(self, true);
|
||||
}
|
||||
|
||||
function find(StdStorage storage self, bool _clear) internal returns (uint256) {
|
||||
return stdStorageSafe.find(self, _clear).slot;
|
||||
}
|
||||
|
||||
function target(StdStorage storage self, address _target) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.target(self, _target);
|
||||
}
|
||||
|
||||
function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.sig(self, _sig);
|
||||
}
|
||||
|
||||
function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.sig(self, _sig);
|
||||
}
|
||||
|
||||
function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.with_key(self, who);
|
||||
}
|
||||
|
||||
function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.with_key(self, amt);
|
||||
}
|
||||
|
||||
function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.with_key(self, key);
|
||||
}
|
||||
|
||||
function with_calldata(StdStorage storage self, bytes memory _calldata) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.with_calldata(self, _calldata);
|
||||
}
|
||||
|
||||
function enable_packed_slots(StdStorage storage self) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.enable_packed_slots(self);
|
||||
}
|
||||
|
||||
function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) {
|
||||
return stdStorageSafe.depth(self, _depth);
|
||||
}
|
||||
|
||||
function clear(StdStorage storage self) internal {
|
||||
stdStorageSafe.clear(self);
|
||||
}
|
||||
|
||||
function checked_write(StdStorage storage self, address who) internal {
|
||||
checked_write(self, bytes32(uint256(uint160(who))));
|
||||
}
|
||||
|
||||
function checked_write(StdStorage storage self, uint256 amt) internal {
|
||||
checked_write(self, bytes32(amt));
|
||||
}
|
||||
|
||||
function checked_write_int(StdStorage storage self, int256 val) internal {
|
||||
checked_write(self, bytes32(uint256(val)));
|
||||
}
|
||||
|
||||
function checked_write(StdStorage storage self, bool write) internal {
|
||||
bytes32 t;
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
t := write
|
||||
}
|
||||
checked_write(self, t);
|
||||
}
|
||||
|
||||
function checked_write(StdStorage storage self, bytes32 set) internal {
|
||||
address who = self._target;
|
||||
bytes4 fsig = self._sig;
|
||||
uint256 field_depth = self._depth;
|
||||
bytes memory params = stdStorageSafe.getCallParams(self);
|
||||
|
||||
if (!self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))].found) {
|
||||
find(self, false);
|
||||
}
|
||||
FindData storage data = self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))];
|
||||
if ((data.offsetLeft + data.offsetRight) > 0) {
|
||||
uint256 maxVal = 2 ** (256 - (data.offsetLeft + data.offsetRight));
|
||||
require(
|
||||
uint256(set) < maxVal,
|
||||
string(
|
||||
abi.encodePacked(
|
||||
"stdStorage find(StdStorage): Packed slot. We can't fit value greater than ",
|
||||
vm.toString(maxVal)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
bytes32 curVal = vm.load(who, bytes32(data.slot));
|
||||
bytes32 valToSet = stdStorageSafe.getUpdatedSlotValue(curVal, uint256(set), data.offsetLeft, data.offsetRight);
|
||||
|
||||
vm.store(who, bytes32(data.slot), valToSet);
|
||||
|
||||
(bool success, bytes32 callResult) = stdStorageSafe.callTarget(self);
|
||||
|
||||
if (!success || callResult != set) {
|
||||
vm.store(who, bytes32(data.slot), curVal);
|
||||
revert("stdStorage find(StdStorage): Failed to write value.");
|
||||
}
|
||||
clear(self);
|
||||
}
|
||||
|
||||
function read_bytes32(StdStorage storage self) internal returns (bytes32) {
|
||||
return stdStorageSafe.read_bytes32(self);
|
||||
}
|
||||
|
||||
function read_bool(StdStorage storage self) internal returns (bool) {
|
||||
return stdStorageSafe.read_bool(self);
|
||||
}
|
||||
|
||||
function read_address(StdStorage storage self) internal returns (address) {
|
||||
return stdStorageSafe.read_address(self);
|
||||
}
|
||||
|
||||
function read_uint(StdStorage storage self) internal returns (uint256) {
|
||||
return stdStorageSafe.read_uint(self);
|
||||
}
|
||||
|
||||
function read_int(StdStorage storage self) internal returns (int256) {
|
||||
return stdStorageSafe.read_int(self);
|
||||
}
|
||||
|
||||
function parent(StdStorage storage self) internal returns (uint256, bytes32) {
|
||||
return stdStorageSafe.parent(self);
|
||||
}
|
||||
|
||||
function root(StdStorage storage self) internal returns (uint256) {
|
||||
return stdStorageSafe.root(self);
|
||||
}
|
||||
}
|
||||
333
lib_forge_std/src/StdStyle.sol
Normal file
333
lib_forge_std/src/StdStyle.sol
Normal file
@@ -0,0 +1,333 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.4.22 <0.9.0;
|
||||
|
||||
import {VmSafe} from "./Vm.sol";
|
||||
|
||||
library StdStyle {
|
||||
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
string constant RED = "\u001b[91m";
|
||||
string constant GREEN = "\u001b[92m";
|
||||
string constant YELLOW = "\u001b[93m";
|
||||
string constant BLUE = "\u001b[94m";
|
||||
string constant MAGENTA = "\u001b[95m";
|
||||
string constant CYAN = "\u001b[96m";
|
||||
string constant BOLD = "\u001b[1m";
|
||||
string constant DIM = "\u001b[2m";
|
||||
string constant ITALIC = "\u001b[3m";
|
||||
string constant UNDERLINE = "\u001b[4m";
|
||||
string constant INVERSE = "\u001b[7m";
|
||||
string constant RESET = "\u001b[0m";
|
||||
|
||||
function styleConcat(string memory style, string memory self) private pure returns (string memory) {
|
||||
return string(abi.encodePacked(style, self, RESET));
|
||||
}
|
||||
|
||||
function red(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(RED, self);
|
||||
}
|
||||
|
||||
function red(uint256 self) internal pure returns (string memory) {
|
||||
return red(vm.toString(self));
|
||||
}
|
||||
|
||||
function red(int256 self) internal pure returns (string memory) {
|
||||
return red(vm.toString(self));
|
||||
}
|
||||
|
||||
function red(address self) internal pure returns (string memory) {
|
||||
return red(vm.toString(self));
|
||||
}
|
||||
|
||||
function red(bool self) internal pure returns (string memory) {
|
||||
return red(vm.toString(self));
|
||||
}
|
||||
|
||||
function redBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return red(vm.toString(self));
|
||||
}
|
||||
|
||||
function redBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return red(vm.toString(self));
|
||||
}
|
||||
|
||||
function green(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(GREEN, self);
|
||||
}
|
||||
|
||||
function green(uint256 self) internal pure returns (string memory) {
|
||||
return green(vm.toString(self));
|
||||
}
|
||||
|
||||
function green(int256 self) internal pure returns (string memory) {
|
||||
return green(vm.toString(self));
|
||||
}
|
||||
|
||||
function green(address self) internal pure returns (string memory) {
|
||||
return green(vm.toString(self));
|
||||
}
|
||||
|
||||
function green(bool self) internal pure returns (string memory) {
|
||||
return green(vm.toString(self));
|
||||
}
|
||||
|
||||
function greenBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return green(vm.toString(self));
|
||||
}
|
||||
|
||||
function greenBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return green(vm.toString(self));
|
||||
}
|
||||
|
||||
function yellow(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(YELLOW, self);
|
||||
}
|
||||
|
||||
function yellow(uint256 self) internal pure returns (string memory) {
|
||||
return yellow(vm.toString(self));
|
||||
}
|
||||
|
||||
function yellow(int256 self) internal pure returns (string memory) {
|
||||
return yellow(vm.toString(self));
|
||||
}
|
||||
|
||||
function yellow(address self) internal pure returns (string memory) {
|
||||
return yellow(vm.toString(self));
|
||||
}
|
||||
|
||||
function yellow(bool self) internal pure returns (string memory) {
|
||||
return yellow(vm.toString(self));
|
||||
}
|
||||
|
||||
function yellowBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return yellow(vm.toString(self));
|
||||
}
|
||||
|
||||
function yellowBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return yellow(vm.toString(self));
|
||||
}
|
||||
|
||||
function blue(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(BLUE, self);
|
||||
}
|
||||
|
||||
function blue(uint256 self) internal pure returns (string memory) {
|
||||
return blue(vm.toString(self));
|
||||
}
|
||||
|
||||
function blue(int256 self) internal pure returns (string memory) {
|
||||
return blue(vm.toString(self));
|
||||
}
|
||||
|
||||
function blue(address self) internal pure returns (string memory) {
|
||||
return blue(vm.toString(self));
|
||||
}
|
||||
|
||||
function blue(bool self) internal pure returns (string memory) {
|
||||
return blue(vm.toString(self));
|
||||
}
|
||||
|
||||
function blueBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return blue(vm.toString(self));
|
||||
}
|
||||
|
||||
function blueBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return blue(vm.toString(self));
|
||||
}
|
||||
|
||||
function magenta(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(MAGENTA, self);
|
||||
}
|
||||
|
||||
function magenta(uint256 self) internal pure returns (string memory) {
|
||||
return magenta(vm.toString(self));
|
||||
}
|
||||
|
||||
function magenta(int256 self) internal pure returns (string memory) {
|
||||
return magenta(vm.toString(self));
|
||||
}
|
||||
|
||||
function magenta(address self) internal pure returns (string memory) {
|
||||
return magenta(vm.toString(self));
|
||||
}
|
||||
|
||||
function magenta(bool self) internal pure returns (string memory) {
|
||||
return magenta(vm.toString(self));
|
||||
}
|
||||
|
||||
function magentaBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return magenta(vm.toString(self));
|
||||
}
|
||||
|
||||
function magentaBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return magenta(vm.toString(self));
|
||||
}
|
||||
|
||||
function cyan(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(CYAN, self);
|
||||
}
|
||||
|
||||
function cyan(uint256 self) internal pure returns (string memory) {
|
||||
return cyan(vm.toString(self));
|
||||
}
|
||||
|
||||
function cyan(int256 self) internal pure returns (string memory) {
|
||||
return cyan(vm.toString(self));
|
||||
}
|
||||
|
||||
function cyan(address self) internal pure returns (string memory) {
|
||||
return cyan(vm.toString(self));
|
||||
}
|
||||
|
||||
function cyan(bool self) internal pure returns (string memory) {
|
||||
return cyan(vm.toString(self));
|
||||
}
|
||||
|
||||
function cyanBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return cyan(vm.toString(self));
|
||||
}
|
||||
|
||||
function cyanBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return cyan(vm.toString(self));
|
||||
}
|
||||
|
||||
function bold(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(BOLD, self);
|
||||
}
|
||||
|
||||
function bold(uint256 self) internal pure returns (string memory) {
|
||||
return bold(vm.toString(self));
|
||||
}
|
||||
|
||||
function bold(int256 self) internal pure returns (string memory) {
|
||||
return bold(vm.toString(self));
|
||||
}
|
||||
|
||||
function bold(address self) internal pure returns (string memory) {
|
||||
return bold(vm.toString(self));
|
||||
}
|
||||
|
||||
function bold(bool self) internal pure returns (string memory) {
|
||||
return bold(vm.toString(self));
|
||||
}
|
||||
|
||||
function boldBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return bold(vm.toString(self));
|
||||
}
|
||||
|
||||
function boldBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return bold(vm.toString(self));
|
||||
}
|
||||
|
||||
function dim(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(DIM, self);
|
||||
}
|
||||
|
||||
function dim(uint256 self) internal pure returns (string memory) {
|
||||
return dim(vm.toString(self));
|
||||
}
|
||||
|
||||
function dim(int256 self) internal pure returns (string memory) {
|
||||
return dim(vm.toString(self));
|
||||
}
|
||||
|
||||
function dim(address self) internal pure returns (string memory) {
|
||||
return dim(vm.toString(self));
|
||||
}
|
||||
|
||||
function dim(bool self) internal pure returns (string memory) {
|
||||
return dim(vm.toString(self));
|
||||
}
|
||||
|
||||
function dimBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return dim(vm.toString(self));
|
||||
}
|
||||
|
||||
function dimBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return dim(vm.toString(self));
|
||||
}
|
||||
|
||||
function italic(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(ITALIC, self);
|
||||
}
|
||||
|
||||
function italic(uint256 self) internal pure returns (string memory) {
|
||||
return italic(vm.toString(self));
|
||||
}
|
||||
|
||||
function italic(int256 self) internal pure returns (string memory) {
|
||||
return italic(vm.toString(self));
|
||||
}
|
||||
|
||||
function italic(address self) internal pure returns (string memory) {
|
||||
return italic(vm.toString(self));
|
||||
}
|
||||
|
||||
function italic(bool self) internal pure returns (string memory) {
|
||||
return italic(vm.toString(self));
|
||||
}
|
||||
|
||||
function italicBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return italic(vm.toString(self));
|
||||
}
|
||||
|
||||
function italicBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return italic(vm.toString(self));
|
||||
}
|
||||
|
||||
function underline(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(UNDERLINE, self);
|
||||
}
|
||||
|
||||
function underline(uint256 self) internal pure returns (string memory) {
|
||||
return underline(vm.toString(self));
|
||||
}
|
||||
|
||||
function underline(int256 self) internal pure returns (string memory) {
|
||||
return underline(vm.toString(self));
|
||||
}
|
||||
|
||||
function underline(address self) internal pure returns (string memory) {
|
||||
return underline(vm.toString(self));
|
||||
}
|
||||
|
||||
function underline(bool self) internal pure returns (string memory) {
|
||||
return underline(vm.toString(self));
|
||||
}
|
||||
|
||||
function underlineBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return underline(vm.toString(self));
|
||||
}
|
||||
|
||||
function underlineBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return underline(vm.toString(self));
|
||||
}
|
||||
|
||||
function inverse(string memory self) internal pure returns (string memory) {
|
||||
return styleConcat(INVERSE, self);
|
||||
}
|
||||
|
||||
function inverse(uint256 self) internal pure returns (string memory) {
|
||||
return inverse(vm.toString(self));
|
||||
}
|
||||
|
||||
function inverse(int256 self) internal pure returns (string memory) {
|
||||
return inverse(vm.toString(self));
|
||||
}
|
||||
|
||||
function inverse(address self) internal pure returns (string memory) {
|
||||
return inverse(vm.toString(self));
|
||||
}
|
||||
|
||||
function inverse(bool self) internal pure returns (string memory) {
|
||||
return inverse(vm.toString(self));
|
||||
}
|
||||
|
||||
function inverseBytes(bytes memory self) internal pure returns (string memory) {
|
||||
return inverse(vm.toString(self));
|
||||
}
|
||||
|
||||
function inverseBytes32(bytes32 self) internal pure returns (string memory) {
|
||||
return inverse(vm.toString(self));
|
||||
}
|
||||
}
|
||||
179
lib_forge_std/src/StdToml.sol
Normal file
179
lib_forge_std/src/StdToml.sol
Normal file
@@ -0,0 +1,179 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.0 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {VmSafe} from "./Vm.sol";
|
||||
|
||||
// Helpers for parsing and writing TOML files
|
||||
// To parse:
|
||||
// ```
|
||||
// using stdToml for string;
|
||||
// string memory toml = vm.readFile("<some_path>");
|
||||
// toml.readUint("<json_path>");
|
||||
// ```
|
||||
// To write:
|
||||
// ```
|
||||
// using stdToml for string;
|
||||
// string memory json = "json";
|
||||
// json.serialize("a", uint256(123));
|
||||
// string memory semiFinal = json.serialize("b", string("test"));
|
||||
// string memory finalJson = json.serialize("c", semiFinal);
|
||||
// finalJson.write("<some_path>");
|
||||
// ```
|
||||
|
||||
library stdToml {
|
||||
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
function parseRaw(string memory toml, string memory key) internal pure returns (bytes memory) {
|
||||
return vm.parseToml(toml, key);
|
||||
}
|
||||
|
||||
function readUint(string memory toml, string memory key) internal pure returns (uint256) {
|
||||
return vm.parseTomlUint(toml, key);
|
||||
}
|
||||
|
||||
function readUintArray(string memory toml, string memory key) internal pure returns (uint256[] memory) {
|
||||
return vm.parseTomlUintArray(toml, key);
|
||||
}
|
||||
|
||||
function readInt(string memory toml, string memory key) internal pure returns (int256) {
|
||||
return vm.parseTomlInt(toml, key);
|
||||
}
|
||||
|
||||
function readIntArray(string memory toml, string memory key) internal pure returns (int256[] memory) {
|
||||
return vm.parseTomlIntArray(toml, key);
|
||||
}
|
||||
|
||||
function readBytes32(string memory toml, string memory key) internal pure returns (bytes32) {
|
||||
return vm.parseTomlBytes32(toml, key);
|
||||
}
|
||||
|
||||
function readBytes32Array(string memory toml, string memory key) internal pure returns (bytes32[] memory) {
|
||||
return vm.parseTomlBytes32Array(toml, key);
|
||||
}
|
||||
|
||||
function readString(string memory toml, string memory key) internal pure returns (string memory) {
|
||||
return vm.parseTomlString(toml, key);
|
||||
}
|
||||
|
||||
function readStringArray(string memory toml, string memory key) internal pure returns (string[] memory) {
|
||||
return vm.parseTomlStringArray(toml, key);
|
||||
}
|
||||
|
||||
function readAddress(string memory toml, string memory key) internal pure returns (address) {
|
||||
return vm.parseTomlAddress(toml, key);
|
||||
}
|
||||
|
||||
function readAddressArray(string memory toml, string memory key) internal pure returns (address[] memory) {
|
||||
return vm.parseTomlAddressArray(toml, key);
|
||||
}
|
||||
|
||||
function readBool(string memory toml, string memory key) internal pure returns (bool) {
|
||||
return vm.parseTomlBool(toml, key);
|
||||
}
|
||||
|
||||
function readBoolArray(string memory toml, string memory key) internal pure returns (bool[] memory) {
|
||||
return vm.parseTomlBoolArray(toml, key);
|
||||
}
|
||||
|
||||
function readBytes(string memory toml, string memory key) internal pure returns (bytes memory) {
|
||||
return vm.parseTomlBytes(toml, key);
|
||||
}
|
||||
|
||||
function readBytesArray(string memory toml, string memory key) internal pure returns (bytes[] memory) {
|
||||
return vm.parseTomlBytesArray(toml, key);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory rootObject) internal returns (string memory) {
|
||||
return vm.serializeJson(jsonKey, rootObject);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) {
|
||||
return vm.serializeBool(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bool[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeBool(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) {
|
||||
return vm.serializeUint(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, uint256[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeUint(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) {
|
||||
return vm.serializeInt(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, int256[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeInt(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) {
|
||||
return vm.serializeAddress(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, address[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeAddress(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) {
|
||||
return vm.serializeBytes32(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes32[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeBytes32(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) {
|
||||
return vm.serializeBytes(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, bytes[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeBytes(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, string memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeString(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function serialize(string memory jsonKey, string memory key, string[] memory value)
|
||||
internal
|
||||
returns (string memory)
|
||||
{
|
||||
return vm.serializeString(jsonKey, key, value);
|
||||
}
|
||||
|
||||
function write(string memory jsonKey, string memory path) internal {
|
||||
vm.writeToml(jsonKey, path);
|
||||
}
|
||||
|
||||
function write(string memory jsonKey, string memory path, string memory valueKey) internal {
|
||||
vm.writeToml(jsonKey, path, valueKey);
|
||||
}
|
||||
}
|
||||
226
lib_forge_std/src/StdUtils.sol
Normal file
226
lib_forge_std/src/StdUtils.sol
Normal file
@@ -0,0 +1,226 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {IMulticall3} from "./interfaces/IMulticall3.sol";
|
||||
import {MockERC20} from "./mocks/MockERC20.sol";
|
||||
import {MockERC721} from "./mocks/MockERC721.sol";
|
||||
import {VmSafe} from "./Vm.sol";
|
||||
|
||||
abstract contract StdUtils {
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
CONSTANTS
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
IMulticall3 private constant multicall = IMulticall3(0xcA11bde05977b3631167028862bE2a173976CA11);
|
||||
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
address private constant CONSOLE2_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67;
|
||||
uint256 private constant INT256_MIN_ABS =
|
||||
57896044618658097711785492504343953926634992332820282019728792003956564819968;
|
||||
uint256 private constant SECP256K1_ORDER =
|
||||
115792089237316195423570985008687907852837564279074904382605163141518161494337;
|
||||
uint256 private constant UINT256_MAX =
|
||||
115792089237316195423570985008687907853269984665640564039457584007913129639935;
|
||||
|
||||
// Used by default when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy.
|
||||
address private constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
INTERNAL FUNCTIONS
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function _bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) {
|
||||
require(min <= max, "StdUtils bound(uint256,uint256,uint256): Max is less than min.");
|
||||
// If x is between min and max, return x directly. This is to ensure that dictionary values
|
||||
// do not get shifted if the min is nonzero. More info: https://github.com/foundry-rs/forge-std/issues/188
|
||||
if (x >= min && x <= max) return x;
|
||||
|
||||
uint256 size = max - min + 1;
|
||||
|
||||
// If the value is 0, 1, 2, 3, wrap that to min, min+1, min+2, min+3. Similarly for the UINT256_MAX side.
|
||||
// This helps ensure coverage of the min/max values.
|
||||
if (x <= 3 && size > x) return min + x;
|
||||
if (x >= UINT256_MAX - 3 && size > UINT256_MAX - x) return max - (UINT256_MAX - x);
|
||||
|
||||
// Otherwise, wrap x into the range [min, max], i.e. the range is inclusive.
|
||||
if (x > max) {
|
||||
uint256 diff = x - max;
|
||||
uint256 rem = diff % size;
|
||||
if (rem == 0) return max;
|
||||
result = min + rem - 1;
|
||||
} else if (x < min) {
|
||||
uint256 diff = min - x;
|
||||
uint256 rem = diff % size;
|
||||
if (rem == 0) return min;
|
||||
result = max - rem + 1;
|
||||
}
|
||||
}
|
||||
|
||||
function bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) {
|
||||
result = _bound(x, min, max);
|
||||
console2_log_StdUtils("Bound result", result);
|
||||
}
|
||||
|
||||
function _bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) {
|
||||
require(min <= max, "StdUtils bound(int256,int256,int256): Max is less than min.");
|
||||
|
||||
// Shifting all int256 values to uint256 to use _bound function. The range of two types are:
|
||||
// int256 : -(2**255) ~ (2**255 - 1)
|
||||
// uint256: 0 ~ (2**256 - 1)
|
||||
// So, add 2**255, INT256_MIN_ABS to the integer values.
|
||||
//
|
||||
// If the given integer value is -2**255, we cannot use `-uint256(-x)` because of the overflow.
|
||||
// So, use `~uint256(x) + 1` instead.
|
||||
uint256 _x = x < 0 ? (INT256_MIN_ABS - ~uint256(x) - 1) : (uint256(x) + INT256_MIN_ABS);
|
||||
uint256 _min = min < 0 ? (INT256_MIN_ABS - ~uint256(min) - 1) : (uint256(min) + INT256_MIN_ABS);
|
||||
uint256 _max = max < 0 ? (INT256_MIN_ABS - ~uint256(max) - 1) : (uint256(max) + INT256_MIN_ABS);
|
||||
|
||||
uint256 y = _bound(_x, _min, _max);
|
||||
|
||||
// To move it back to int256 value, subtract INT256_MIN_ABS at here.
|
||||
result = y < INT256_MIN_ABS ? int256(~(INT256_MIN_ABS - y) + 1) : int256(y - INT256_MIN_ABS);
|
||||
}
|
||||
|
||||
function bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) {
|
||||
result = _bound(x, min, max);
|
||||
console2_log_StdUtils("Bound result", vm.toString(result));
|
||||
}
|
||||
|
||||
function boundPrivateKey(uint256 privateKey) internal pure virtual returns (uint256 result) {
|
||||
result = _bound(privateKey, 1, SECP256K1_ORDER - 1);
|
||||
}
|
||||
|
||||
function bytesToUint(bytes memory b) internal pure virtual returns (uint256) {
|
||||
require(b.length <= 32, "StdUtils bytesToUint(bytes): Bytes length exceeds 32.");
|
||||
return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256));
|
||||
}
|
||||
|
||||
/// @dev Compute the address a contract will be deployed at for a given deployer address and nonce
|
||||
/// @notice adapted from Solmate implementation (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibRLP.sol)
|
||||
function computeCreateAddress(address deployer, uint256 nonce) internal pure virtual returns (address) {
|
||||
console2_log_StdUtils("computeCreateAddress is deprecated. Please use vm.computeCreateAddress instead.");
|
||||
return vm.computeCreateAddress(deployer, nonce);
|
||||
}
|
||||
|
||||
function computeCreate2Address(bytes32 salt, bytes32 initcodeHash, address deployer)
|
||||
internal
|
||||
pure
|
||||
virtual
|
||||
returns (address)
|
||||
{
|
||||
console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead.");
|
||||
return vm.computeCreate2Address(salt, initcodeHash, deployer);
|
||||
}
|
||||
|
||||
/// @dev returns the address of a contract created with CREATE2 using the default CREATE2 deployer
|
||||
function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) internal pure returns (address) {
|
||||
console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead.");
|
||||
return vm.computeCreate2Address(salt, initCodeHash);
|
||||
}
|
||||
|
||||
/// @dev returns an initialized mock ERC20 contract
|
||||
function deployMockERC20(string memory name, string memory symbol, uint8 decimals)
|
||||
internal
|
||||
returns (MockERC20 mock)
|
||||
{
|
||||
mock = new MockERC20();
|
||||
mock.initialize(name, symbol, decimals);
|
||||
}
|
||||
|
||||
/// @dev returns an initialized mock ERC721 contract
|
||||
function deployMockERC721(string memory name, string memory symbol) internal returns (MockERC721 mock) {
|
||||
mock = new MockERC721();
|
||||
mock.initialize(name, symbol);
|
||||
}
|
||||
|
||||
/// @dev returns the hash of the init code (creation code + no args) used in CREATE2 with no constructor arguments
|
||||
/// @param creationCode the creation code of a contract C, as returned by type(C).creationCode
|
||||
function hashInitCode(bytes memory creationCode) internal pure returns (bytes32) {
|
||||
return hashInitCode(creationCode, "");
|
||||
}
|
||||
|
||||
/// @dev returns the hash of the init code (creation code + ABI-encoded args) used in CREATE2
|
||||
/// @param creationCode the creation code of a contract C, as returned by type(C).creationCode
|
||||
/// @param args the ABI-encoded arguments to the constructor of C
|
||||
function hashInitCode(bytes memory creationCode, bytes memory args) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encodePacked(creationCode, args));
|
||||
}
|
||||
|
||||
// Performs a single call with Multicall3 to query the ERC-20 token balances of the given addresses.
|
||||
function getTokenBalances(address token, address[] memory addresses)
|
||||
internal
|
||||
virtual
|
||||
returns (uint256[] memory balances)
|
||||
{
|
||||
uint256 tokenCodeSize;
|
||||
assembly {
|
||||
tokenCodeSize := extcodesize(token)
|
||||
}
|
||||
require(tokenCodeSize > 0, "StdUtils getTokenBalances(address,address[]): Token address is not a contract.");
|
||||
|
||||
// ABI encode the aggregate call to Multicall3.
|
||||
uint256 length = addresses.length;
|
||||
IMulticall3.Call[] memory calls = new IMulticall3.Call[](length);
|
||||
for (uint256 i = 0; i < length; ++i) {
|
||||
// 0x70a08231 = bytes4("balanceOf(address)"))
|
||||
calls[i] = IMulticall3.Call({target: token, callData: abi.encodeWithSelector(0x70a08231, (addresses[i]))});
|
||||
}
|
||||
|
||||
// Make the aggregate call.
|
||||
(, bytes[] memory returnData) = multicall.aggregate(calls);
|
||||
|
||||
// ABI decode the return data and return the balances.
|
||||
balances = new uint256[](length);
|
||||
for (uint256 i = 0; i < length; ++i) {
|
||||
balances[i] = abi.decode(returnData[i], (uint256));
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
PRIVATE FUNCTIONS
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function addressFromLast20Bytes(bytes32 bytesValue) private pure returns (address) {
|
||||
return address(uint160(uint256(bytesValue)));
|
||||
}
|
||||
|
||||
// This section is used to prevent the compilation of console, which shortens the compilation time when console is
|
||||
// not used elsewhere. We also trick the compiler into letting us make the console log methods as `pure` to avoid
|
||||
// any breaking changes to function signatures.
|
||||
function _castLogPayloadViewToPure(function(bytes memory) internal view fnIn)
|
||||
internal
|
||||
pure
|
||||
returns (function(bytes memory) internal pure fnOut)
|
||||
{
|
||||
assembly {
|
||||
fnOut := fnIn
|
||||
}
|
||||
}
|
||||
|
||||
function _sendLogPayload(bytes memory payload) internal pure {
|
||||
_castLogPayloadViewToPure(_sendLogPayloadView)(payload);
|
||||
}
|
||||
|
||||
function _sendLogPayloadView(bytes memory payload) private view {
|
||||
uint256 payloadLength = payload.length;
|
||||
address consoleAddress = CONSOLE2_ADDRESS;
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
let payloadStart := add(payload, 32)
|
||||
let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function console2_log_StdUtils(string memory p0) private pure {
|
||||
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
|
||||
}
|
||||
|
||||
function console2_log_StdUtils(string memory p0, uint256 p1) private pure {
|
||||
_sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
|
||||
}
|
||||
|
||||
function console2_log_StdUtils(string memory p0, string memory p1) private pure {
|
||||
_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
|
||||
}
|
||||
}
|
||||
33
lib_forge_std/src/Test.sol
Normal file
33
lib_forge_std/src/Test.sol
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
// 💬 ABOUT
|
||||
// Forge Std's default Test.
|
||||
|
||||
// 🧩 MODULES
|
||||
import {console} from "./console.sol";
|
||||
import {console2} from "./console2.sol";
|
||||
import {safeconsole} from "./safeconsole.sol";
|
||||
import {StdAssertions} from "./StdAssertions.sol";
|
||||
import {StdChains} from "./StdChains.sol";
|
||||
import {StdCheats} from "./StdCheats.sol";
|
||||
import {stdError} from "./StdError.sol";
|
||||
import {StdInvariant} from "./StdInvariant.sol";
|
||||
import {stdJson} from "./StdJson.sol";
|
||||
import {stdMath} from "./StdMath.sol";
|
||||
import {StdStorage, stdStorage} from "./StdStorage.sol";
|
||||
import {StdStyle} from "./StdStyle.sol";
|
||||
import {stdToml} from "./StdToml.sol";
|
||||
import {StdUtils} from "./StdUtils.sol";
|
||||
import {Vm} from "./Vm.sol";
|
||||
|
||||
// 📦 BOILERPLATE
|
||||
import {TestBase} from "./Base.sol";
|
||||
|
||||
// ⭐️ TEST
|
||||
abstract contract Test is TestBase, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils {
|
||||
// Note: IS_TEST() must return true.
|
||||
bool public IS_TEST = true;
|
||||
}
|
||||
1748
lib_forge_std/src/Vm.sol
Normal file
1748
lib_forge_std/src/Vm.sol
Normal file
File diff suppressed because it is too large
Load Diff
1533
lib_forge_std/src/console.sol
Normal file
1533
lib_forge_std/src/console.sol
Normal file
File diff suppressed because it is too large
Load Diff
1558
lib_forge_std/src/console2.sol
Normal file
1558
lib_forge_std/src/console2.sol
Normal file
File diff suppressed because it is too large
Load Diff
105
lib_forge_std/src/interfaces/IERC1155.sol
Normal file
105
lib_forge_std/src/interfaces/IERC1155.sol
Normal file
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2;
|
||||
|
||||
import "./IERC165.sol";
|
||||
|
||||
/// @title ERC-1155 Multi Token Standard
|
||||
/// @dev See https://eips.ethereum.org/EIPS/eip-1155
|
||||
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
|
||||
interface IERC1155 is IERC165 {
|
||||
/// @dev
|
||||
/// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
|
||||
/// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
|
||||
/// - The `_from` argument MUST be the address of the holder whose balance is decreased.
|
||||
/// - The `_to` argument MUST be the address of the recipient whose balance is increased.
|
||||
/// - The `_id` argument MUST be the token type being transferred.
|
||||
/// - The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
|
||||
/// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
|
||||
/// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
|
||||
event TransferSingle(
|
||||
address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value
|
||||
);
|
||||
|
||||
/// @dev
|
||||
/// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
|
||||
/// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
|
||||
/// - The `_from` argument MUST be the address of the holder whose balance is decreased.
|
||||
/// - The `_to` argument MUST be the address of the recipient whose balance is increased.
|
||||
/// - The `_ids` argument MUST be the list of tokens being transferred.
|
||||
/// - The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
|
||||
/// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
|
||||
/// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
|
||||
event TransferBatch(
|
||||
address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values
|
||||
);
|
||||
|
||||
/// @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled).
|
||||
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
|
||||
|
||||
/// @dev MUST emit when the URI is updated for a token ID. URIs are defined in RFC 3986.
|
||||
/// The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
|
||||
event URI(string _value, uint256 indexed _id);
|
||||
|
||||
/// @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
|
||||
/// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
|
||||
/// - MUST revert if `_to` is the zero address.
|
||||
/// - MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
|
||||
/// - MUST revert on any other error.
|
||||
/// - MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
|
||||
/// - After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
|
||||
/// @param _from Source address
|
||||
/// @param _to Target address
|
||||
/// @param _id ID of the token type
|
||||
/// @param _value Transfer amount
|
||||
/// @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
|
||||
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
|
||||
|
||||
/// @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
|
||||
/// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
|
||||
/// - MUST revert if `_to` is the zero address.
|
||||
/// - MUST revert if length of `_ids` is not the same as length of `_values`.
|
||||
/// - MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
|
||||
/// - MUST revert on any other error.
|
||||
/// - MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
|
||||
/// - Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
|
||||
/// - After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
|
||||
/// @param _from Source address
|
||||
/// @param _to Target address
|
||||
/// @param _ids IDs of each token type (order and length must match _values array)
|
||||
/// @param _values Transfer amounts per token type (order and length must match _ids array)
|
||||
/// @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
|
||||
function safeBatchTransferFrom(
|
||||
address _from,
|
||||
address _to,
|
||||
uint256[] calldata _ids,
|
||||
uint256[] calldata _values,
|
||||
bytes calldata _data
|
||||
) external;
|
||||
|
||||
/// @notice Get the balance of an account's tokens.
|
||||
/// @param _owner The address of the token holder
|
||||
/// @param _id ID of the token
|
||||
/// @return The _owner's balance of the token type requested
|
||||
function balanceOf(address _owner, uint256 _id) external view returns (uint256);
|
||||
|
||||
/// @notice Get the balance of multiple account/token pairs
|
||||
/// @param _owners The addresses of the token holders
|
||||
/// @param _ids ID of the tokens
|
||||
/// @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
|
||||
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory);
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
|
||||
/// @dev MUST emit the ApprovalForAll event on success.
|
||||
/// @param _operator Address to add to the set of authorized operators
|
||||
/// @param _approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address _operator, bool _approved) external;
|
||||
|
||||
/// @notice Queries the approval status of an operator for a given owner.
|
||||
/// @param _owner The owner of the tokens
|
||||
/// @param _operator Address of authorized operator
|
||||
/// @return True if the operator is approved, false if not
|
||||
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
|
||||
}
|
||||
12
lib_forge_std/src/interfaces/IERC165.sol
Normal file
12
lib_forge_std/src/interfaces/IERC165.sol
Normal file
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2;
|
||||
|
||||
interface IERC165 {
|
||||
/// @notice Query if a contract implements an interface
|
||||
/// @param interfaceID The interface identifier, as specified in ERC-165
|
||||
/// @dev Interface identification is specified in ERC-165. This function
|
||||
/// uses less than 30,000 gas.
|
||||
/// @return `true` if the contract implements `interfaceID` and
|
||||
/// `interfaceID` is not 0xffffffff, `false` otherwise
|
||||
function supportsInterface(bytes4 interfaceID) external view returns (bool);
|
||||
}
|
||||
43
lib_forge_std/src/interfaces/IERC20.sol
Normal file
43
lib_forge_std/src/interfaces/IERC20.sol
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2;
|
||||
|
||||
/// @dev Interface of the ERC20 standard as defined in the EIP.
|
||||
/// @dev This includes the optional name, symbol, and decimals metadata.
|
||||
interface IERC20 {
|
||||
/// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
|
||||
/// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
|
||||
/// is the new allowance.
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
|
||||
/// @notice Returns the amount of tokens in existence.
|
||||
function totalSupply() external view returns (uint256);
|
||||
|
||||
/// @notice Returns the amount of tokens owned by `account`.
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
|
||||
/// @notice Moves `amount` tokens from the caller's account to `to`.
|
||||
function transfer(address to, uint256 amount) external returns (bool);
|
||||
|
||||
/// @notice Returns the remaining number of tokens that `spender` is allowed
|
||||
/// to spend on behalf of `owner`
|
||||
function allowance(address owner, address spender) external view returns (uint256);
|
||||
|
||||
/// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
|
||||
/// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
function approve(address spender, uint256 amount) external returns (bool);
|
||||
|
||||
/// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
|
||||
/// `amount` is then deducted from the caller's allowance.
|
||||
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
||||
|
||||
/// @notice Returns the name of the token.
|
||||
function name() external view returns (string memory);
|
||||
|
||||
/// @notice Returns the symbol of the token.
|
||||
function symbol() external view returns (string memory);
|
||||
|
||||
/// @notice Returns the decimals places of the token.
|
||||
function decimals() external view returns (uint8);
|
||||
}
|
||||
190
lib_forge_std/src/interfaces/IERC4626.sol
Normal file
190
lib_forge_std/src/interfaces/IERC4626.sol
Normal file
@@ -0,0 +1,190 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2;
|
||||
|
||||
import "./IERC20.sol";
|
||||
|
||||
/// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
|
||||
/// https://eips.ethereum.org/EIPS/eip-4626
|
||||
interface IERC4626 is IERC20 {
|
||||
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
|
||||
|
||||
event Withdraw(
|
||||
address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
|
||||
);
|
||||
|
||||
/// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
|
||||
/// @dev
|
||||
/// - MUST be an ERC-20 token contract.
|
||||
/// - MUST NOT revert.
|
||||
function asset() external view returns (address assetTokenAddress);
|
||||
|
||||
/// @notice Returns the total amount of the underlying asset that is “managed” by Vault.
|
||||
/// @dev
|
||||
/// - SHOULD include any compounding that occurs from yield.
|
||||
/// - MUST be inclusive of any fees that are charged against assets in the Vault.
|
||||
/// - MUST NOT revert.
|
||||
function totalAssets() external view returns (uint256 totalManagedAssets);
|
||||
|
||||
/// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
|
||||
/// scenario where all the conditions are met.
|
||||
/// @dev
|
||||
/// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
|
||||
/// - MUST NOT show any variations depending on the caller.
|
||||
/// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
|
||||
/// - MUST NOT revert.
|
||||
///
|
||||
/// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
|
||||
/// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
|
||||
/// from.
|
||||
function convertToShares(uint256 assets) external view returns (uint256 shares);
|
||||
|
||||
/// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
|
||||
/// scenario where all the conditions are met.
|
||||
/// @dev
|
||||
/// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
|
||||
/// - MUST NOT show any variations depending on the caller.
|
||||
/// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
|
||||
/// - MUST NOT revert.
|
||||
///
|
||||
/// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
|
||||
/// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
|
||||
/// from.
|
||||
function convertToAssets(uint256 shares) external view returns (uint256 assets);
|
||||
|
||||
/// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
|
||||
/// through a deposit call.
|
||||
/// @dev
|
||||
/// - MUST return a limited value if receiver is subject to some deposit limit.
|
||||
/// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
|
||||
/// - MUST NOT revert.
|
||||
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
|
||||
|
||||
/// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
|
||||
/// current on-chain conditions.
|
||||
/// @dev
|
||||
/// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
|
||||
/// call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
|
||||
/// in the same transaction.
|
||||
/// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
|
||||
/// deposit would be accepted, regardless if the user has enough tokens approved, etc.
|
||||
/// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
|
||||
/// - MUST NOT revert.
|
||||
///
|
||||
/// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
|
||||
/// share price or some other type of condition, meaning the depositor will lose assets by depositing.
|
||||
function previewDeposit(uint256 assets) external view returns (uint256 shares);
|
||||
|
||||
/// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
|
||||
/// @dev
|
||||
/// - MUST emit the Deposit event.
|
||||
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
|
||||
/// deposit execution, and are accounted for during deposit.
|
||||
/// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
|
||||
/// approving enough underlying tokens to the Vault contract, etc).
|
||||
///
|
||||
/// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
|
||||
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
|
||||
|
||||
/// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
|
||||
/// @dev
|
||||
/// - MUST return a limited value if receiver is subject to some mint limit.
|
||||
/// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
|
||||
/// - MUST NOT revert.
|
||||
function maxMint(address receiver) external view returns (uint256 maxShares);
|
||||
|
||||
/// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
|
||||
/// current on-chain conditions.
|
||||
/// @dev
|
||||
/// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
|
||||
/// in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
|
||||
/// same transaction.
|
||||
/// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
|
||||
/// would be accepted, regardless if the user has enough tokens approved, etc.
|
||||
/// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
|
||||
/// - MUST NOT revert.
|
||||
///
|
||||
/// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
|
||||
/// share price or some other type of condition, meaning the depositor will lose assets by minting.
|
||||
function previewMint(uint256 shares) external view returns (uint256 assets);
|
||||
|
||||
/// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
|
||||
/// @dev
|
||||
/// - MUST emit the Deposit event.
|
||||
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
|
||||
/// execution, and are accounted for during mint.
|
||||
/// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
|
||||
/// approving enough underlying tokens to the Vault contract, etc).
|
||||
///
|
||||
/// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
|
||||
function mint(uint256 shares, address receiver) external returns (uint256 assets);
|
||||
|
||||
/// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
|
||||
/// Vault, through a withdraw call.
|
||||
/// @dev
|
||||
/// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
|
||||
/// - MUST NOT revert.
|
||||
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
|
||||
|
||||
/// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
|
||||
/// given current on-chain conditions.
|
||||
/// @dev
|
||||
/// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
|
||||
/// call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
|
||||
/// called
|
||||
/// in the same transaction.
|
||||
/// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
|
||||
/// the withdrawal would be accepted, regardless if the user has enough shares, etc.
|
||||
/// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
|
||||
/// - MUST NOT revert.
|
||||
///
|
||||
/// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
|
||||
/// share price or some other type of condition, meaning the depositor will lose assets by depositing.
|
||||
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
|
||||
|
||||
/// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
|
||||
/// @dev
|
||||
/// - MUST emit the Withdraw event.
|
||||
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
|
||||
/// withdraw execution, and are accounted for during withdraw.
|
||||
/// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
|
||||
/// not having enough shares, etc).
|
||||
///
|
||||
/// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
|
||||
/// Those methods should be performed separately.
|
||||
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
|
||||
|
||||
/// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
|
||||
/// through a redeem call.
|
||||
/// @dev
|
||||
/// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
|
||||
/// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
|
||||
/// - MUST NOT revert.
|
||||
function maxRedeem(address owner) external view returns (uint256 maxShares);
|
||||
|
||||
/// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
|
||||
/// given current on-chain conditions.
|
||||
/// @dev
|
||||
/// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
|
||||
/// in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
|
||||
/// same transaction.
|
||||
/// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
|
||||
/// redemption would be accepted, regardless if the user has enough shares, etc.
|
||||
/// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
|
||||
/// - MUST NOT revert.
|
||||
///
|
||||
/// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
|
||||
/// share price or some other type of condition, meaning the depositor will lose assets by redeeming.
|
||||
function previewRedeem(uint256 shares) external view returns (uint256 assets);
|
||||
|
||||
/// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
|
||||
/// @dev
|
||||
/// - MUST emit the Withdraw event.
|
||||
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
|
||||
/// redeem execution, and are accounted for during redeem.
|
||||
/// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
|
||||
/// not having enough shares, etc).
|
||||
///
|
||||
/// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
|
||||
/// Those methods should be performed separately.
|
||||
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
|
||||
}
|
||||
164
lib_forge_std/src/interfaces/IERC721.sol
Normal file
164
lib_forge_std/src/interfaces/IERC721.sol
Normal file
@@ -0,0 +1,164 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2;
|
||||
|
||||
import "./IERC165.sol";
|
||||
|
||||
/// @title ERC-721 Non-Fungible Token Standard
|
||||
/// @dev See https://eips.ethereum.org/EIPS/eip-721
|
||||
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
|
||||
interface IERC721 is IERC165 {
|
||||
/// @dev This emits when ownership of any NFT changes by any mechanism.
|
||||
/// This event emits when NFTs are created (`from` == 0) and destroyed
|
||||
/// (`to` == 0). Exception: during contract creation, any number of NFTs
|
||||
/// may be created and assigned without emitting Transfer. At the time of
|
||||
/// any transfer, the approved address for that NFT (if any) is reset to none.
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
|
||||
|
||||
/// @dev This emits when the approved address for an NFT is changed or
|
||||
/// reaffirmed. The zero address indicates there is no approved address.
|
||||
/// When a Transfer event emits, this also indicates that the approved
|
||||
/// address for that NFT (if any) is reset to none.
|
||||
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
|
||||
|
||||
/// @dev This emits when an operator is enabled or disabled for an owner.
|
||||
/// The operator can manage all NFTs of the owner.
|
||||
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
|
||||
|
||||
/// @notice Count all NFTs assigned to an owner
|
||||
/// @dev NFTs assigned to the zero address are considered invalid, and this
|
||||
/// function throws for queries about the zero address.
|
||||
/// @param _owner An address for whom to query the balance
|
||||
/// @return The number of NFTs owned by `_owner`, possibly zero
|
||||
function balanceOf(address _owner) external view returns (uint256);
|
||||
|
||||
/// @notice Find the owner of an NFT
|
||||
/// @dev NFTs assigned to zero address are considered invalid, and queries
|
||||
/// about them do throw.
|
||||
/// @param _tokenId The identifier for an NFT
|
||||
/// @return The address of the owner of the NFT
|
||||
function ownerOf(uint256 _tokenId) external view returns (address);
|
||||
|
||||
/// @notice Transfers the ownership of an NFT from one address to another address
|
||||
/// @dev Throws unless `msg.sender` is the current owner, an authorized
|
||||
/// operator, or the approved address for this NFT. Throws if `_from` is
|
||||
/// not the current owner. Throws if `_to` is the zero address. Throws if
|
||||
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
|
||||
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
|
||||
/// `onERC721Received` on `_to` and throws if the return value is not
|
||||
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
/// @param data Additional data with no specified format, sent in call to `_to`
|
||||
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
|
||||
|
||||
/// @notice Transfers the ownership of an NFT from one address to another address
|
||||
/// @dev This works identically to the other function with an extra data parameter,
|
||||
/// except this function just sets data to "".
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
|
||||
|
||||
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
|
||||
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
|
||||
/// THEY MAY BE PERMANENTLY LOST
|
||||
/// @dev Throws unless `msg.sender` is the current owner, an authorized
|
||||
/// operator, or the approved address for this NFT. Throws if `_from` is
|
||||
/// not the current owner. Throws if `_to` is the zero address. Throws if
|
||||
/// `_tokenId` is not a valid NFT.
|
||||
/// @param _from The current owner of the NFT
|
||||
/// @param _to The new owner
|
||||
/// @param _tokenId The NFT to transfer
|
||||
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
|
||||
|
||||
/// @notice Change or reaffirm the approved address for an NFT
|
||||
/// @dev The zero address indicates there is no approved address.
|
||||
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
|
||||
/// operator of the current owner.
|
||||
/// @param _approved The new approved NFT controller
|
||||
/// @param _tokenId The NFT to approve
|
||||
function approve(address _approved, uint256 _tokenId) external payable;
|
||||
|
||||
/// @notice Enable or disable approval for a third party ("operator") to manage
|
||||
/// all of `msg.sender`'s assets
|
||||
/// @dev Emits the ApprovalForAll event. The contract MUST allow
|
||||
/// multiple operators per owner.
|
||||
/// @param _operator Address to add to the set of authorized operators
|
||||
/// @param _approved True if the operator is approved, false to revoke approval
|
||||
function setApprovalForAll(address _operator, bool _approved) external;
|
||||
|
||||
/// @notice Get the approved address for a single NFT
|
||||
/// @dev Throws if `_tokenId` is not a valid NFT.
|
||||
/// @param _tokenId The NFT to find the approved address for
|
||||
/// @return The approved address for this NFT, or the zero address if there is none
|
||||
function getApproved(uint256 _tokenId) external view returns (address);
|
||||
|
||||
/// @notice Query if an address is an authorized operator for another address
|
||||
/// @param _owner The address that owns the NFTs
|
||||
/// @param _operator The address that acts on behalf of the owner
|
||||
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
|
||||
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
|
||||
}
|
||||
|
||||
/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
|
||||
interface IERC721TokenReceiver {
|
||||
/// @notice Handle the receipt of an NFT
|
||||
/// @dev The ERC721 smart contract calls this function on the recipient
|
||||
/// after a `transfer`. This function MAY throw to revert and reject the
|
||||
/// transfer. Return of other than the magic value MUST result in the
|
||||
/// transaction being reverted.
|
||||
/// Note: the contract address is always the message sender.
|
||||
/// @param _operator The address which called `safeTransferFrom` function
|
||||
/// @param _from The address which previously owned the token
|
||||
/// @param _tokenId The NFT identifier which is being transferred
|
||||
/// @param _data Additional data with no specified format
|
||||
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
|
||||
/// unless throwing
|
||||
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data)
|
||||
external
|
||||
returns (bytes4);
|
||||
}
|
||||
|
||||
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
|
||||
/// @dev See https://eips.ethereum.org/EIPS/eip-721
|
||||
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
|
||||
interface IERC721Metadata is IERC721 {
|
||||
/// @notice A descriptive name for a collection of NFTs in this contract
|
||||
function name() external view returns (string memory _name);
|
||||
|
||||
/// @notice An abbreviated name for NFTs in this contract
|
||||
function symbol() external view returns (string memory _symbol);
|
||||
|
||||
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
|
||||
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
|
||||
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
|
||||
/// Metadata JSON Schema".
|
||||
function tokenURI(uint256 _tokenId) external view returns (string memory);
|
||||
}
|
||||
|
||||
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
|
||||
/// @dev See https://eips.ethereum.org/EIPS/eip-721
|
||||
/// Note: the ERC-165 identifier for this interface is 0x780e9d63.
|
||||
interface IERC721Enumerable is IERC721 {
|
||||
/// @notice Count NFTs tracked by this contract
|
||||
/// @return A count of valid NFTs tracked by this contract, where each one of
|
||||
/// them has an assigned and queryable owner not equal to the zero address
|
||||
function totalSupply() external view returns (uint256);
|
||||
|
||||
/// @notice Enumerate valid NFTs
|
||||
/// @dev Throws if `_index` >= `totalSupply()`.
|
||||
/// @param _index A counter less than `totalSupply()`
|
||||
/// @return The token identifier for the `_index`th NFT,
|
||||
/// (sort order not specified)
|
||||
function tokenByIndex(uint256 _index) external view returns (uint256);
|
||||
|
||||
/// @notice Enumerate NFTs assigned to an owner
|
||||
/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
|
||||
/// `_owner` is the zero address, representing invalid NFTs.
|
||||
/// @param _owner An address where we are interested in NFTs owned by them
|
||||
/// @param _index A counter less than `balanceOf(_owner)`
|
||||
/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
|
||||
/// (sort order not specified)
|
||||
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
|
||||
}
|
||||
73
lib_forge_std/src/interfaces/IMulticall3.sol
Normal file
73
lib_forge_std/src/interfaces/IMulticall3.sol
Normal file
@@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface IMulticall3 {
|
||||
struct Call {
|
||||
address target;
|
||||
bytes callData;
|
||||
}
|
||||
|
||||
struct Call3 {
|
||||
address target;
|
||||
bool allowFailure;
|
||||
bytes callData;
|
||||
}
|
||||
|
||||
struct Call3Value {
|
||||
address target;
|
||||
bool allowFailure;
|
||||
uint256 value;
|
||||
bytes callData;
|
||||
}
|
||||
|
||||
struct Result {
|
||||
bool success;
|
||||
bytes returnData;
|
||||
}
|
||||
|
||||
function aggregate(Call[] calldata calls)
|
||||
external
|
||||
payable
|
||||
returns (uint256 blockNumber, bytes[] memory returnData);
|
||||
|
||||
function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData);
|
||||
|
||||
function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData);
|
||||
|
||||
function blockAndAggregate(Call[] calldata calls)
|
||||
external
|
||||
payable
|
||||
returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData);
|
||||
|
||||
function getBasefee() external view returns (uint256 basefee);
|
||||
|
||||
function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash);
|
||||
|
||||
function getBlockNumber() external view returns (uint256 blockNumber);
|
||||
|
||||
function getChainId() external view returns (uint256 chainid);
|
||||
|
||||
function getCurrentBlockCoinbase() external view returns (address coinbase);
|
||||
|
||||
function getCurrentBlockDifficulty() external view returns (uint256 difficulty);
|
||||
|
||||
function getCurrentBlockGasLimit() external view returns (uint256 gaslimit);
|
||||
|
||||
function getCurrentBlockTimestamp() external view returns (uint256 timestamp);
|
||||
|
||||
function getEthBalance(address addr) external view returns (uint256 balance);
|
||||
|
||||
function getLastBlockHash() external view returns (bytes32 blockHash);
|
||||
|
||||
function tryAggregate(bool requireSuccess, Call[] calldata calls)
|
||||
external
|
||||
payable
|
||||
returns (Result[] memory returnData);
|
||||
|
||||
function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls)
|
||||
external
|
||||
payable
|
||||
returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData);
|
||||
}
|
||||
234
lib_forge_std/src/mocks/MockERC20.sol
Normal file
234
lib_forge_std/src/mocks/MockERC20.sol
Normal file
@@ -0,0 +1,234 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
import {IERC20} from "../interfaces/IERC20.sol";
|
||||
|
||||
/// @notice This is a mock contract of the ERC20 standard for testing purposes only, it SHOULD NOT be used in production.
|
||||
/// @dev Forked from: https://github.com/transmissions11/solmate/blob/0384dbaaa4fcb5715738a9254a7c0a4cb62cf458/src/tokens/ERC20.sol
|
||||
contract MockERC20 is IERC20 {
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
METADATA STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
string internal _name;
|
||||
|
||||
string internal _symbol;
|
||||
|
||||
uint8 internal _decimals;
|
||||
|
||||
function name() external view override returns (string memory) {
|
||||
return _name;
|
||||
}
|
||||
|
||||
function symbol() external view override returns (string memory) {
|
||||
return _symbol;
|
||||
}
|
||||
|
||||
function decimals() external view override returns (uint8) {
|
||||
return _decimals;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC20 STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
uint256 internal _totalSupply;
|
||||
|
||||
mapping(address => uint256) internal _balanceOf;
|
||||
|
||||
mapping(address => mapping(address => uint256)) internal _allowance;
|
||||
|
||||
function totalSupply() external view override returns (uint256) {
|
||||
return _totalSupply;
|
||||
}
|
||||
|
||||
function balanceOf(address owner) external view override returns (uint256) {
|
||||
return _balanceOf[owner];
|
||||
}
|
||||
|
||||
function allowance(address owner, address spender) external view override returns (uint256) {
|
||||
return _allowance[owner][spender];
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
EIP-2612 STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
uint256 internal INITIAL_CHAIN_ID;
|
||||
|
||||
bytes32 internal INITIAL_DOMAIN_SEPARATOR;
|
||||
|
||||
mapping(address => uint256) public nonces;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
INITIALIZE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
/// @dev A bool to track whether the contract has been initialized.
|
||||
bool private initialized;
|
||||
|
||||
/// @dev To hide constructor warnings across solc versions due to different constructor visibility requirements and
|
||||
/// syntaxes, we add an initialization function that can be called only once.
|
||||
function initialize(string memory name_, string memory symbol_, uint8 decimals_) public {
|
||||
require(!initialized, "ALREADY_INITIALIZED");
|
||||
|
||||
_name = name_;
|
||||
_symbol = symbol_;
|
||||
_decimals = decimals_;
|
||||
|
||||
INITIAL_CHAIN_ID = _pureChainId();
|
||||
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC20 LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function approve(address spender, uint256 amount) public virtual override returns (bool) {
|
||||
_allowance[msg.sender][spender] = amount;
|
||||
|
||||
emit Approval(msg.sender, spender, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function transfer(address to, uint256 amount) public virtual override returns (bool) {
|
||||
_balanceOf[msg.sender] = _sub(_balanceOf[msg.sender], amount);
|
||||
_balanceOf[to] = _add(_balanceOf[to], amount);
|
||||
|
||||
emit Transfer(msg.sender, to, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
|
||||
uint256 allowed = _allowance[from][msg.sender]; // Saves gas for limited approvals.
|
||||
|
||||
if (allowed != ~uint256(0)) _allowance[from][msg.sender] = _sub(allowed, amount);
|
||||
|
||||
_balanceOf[from] = _sub(_balanceOf[from], amount);
|
||||
_balanceOf[to] = _add(_balanceOf[to], amount);
|
||||
|
||||
emit Transfer(from, to, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
EIP-2612 LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
|
||||
public
|
||||
virtual
|
||||
{
|
||||
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
|
||||
|
||||
address recoveredAddress = ecrecover(
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
DOMAIN_SEPARATOR(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
keccak256(
|
||||
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
|
||||
),
|
||||
owner,
|
||||
spender,
|
||||
value,
|
||||
nonces[owner]++,
|
||||
deadline
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
|
||||
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
|
||||
|
||||
_allowance[recoveredAddress][spender] = value;
|
||||
|
||||
emit Approval(owner, spender, value);
|
||||
}
|
||||
|
||||
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
|
||||
return _pureChainId() == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
|
||||
}
|
||||
|
||||
function computeDomainSeparator() internal view virtual returns (bytes32) {
|
||||
return keccak256(
|
||||
abi.encode(
|
||||
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
|
||||
keccak256(bytes(_name)),
|
||||
keccak256("1"),
|
||||
_pureChainId(),
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
INTERNAL MINT/BURN LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function _mint(address to, uint256 amount) internal virtual {
|
||||
_totalSupply = _add(_totalSupply, amount);
|
||||
_balanceOf[to] = _add(_balanceOf[to], amount);
|
||||
|
||||
emit Transfer(address(0), to, amount);
|
||||
}
|
||||
|
||||
function _burn(address from, uint256 amount) internal virtual {
|
||||
_balanceOf[from] = _sub(_balanceOf[from], amount);
|
||||
_totalSupply = _sub(_totalSupply, amount);
|
||||
|
||||
emit Transfer(from, address(0), amount);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
INTERNAL SAFE MATH LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function _add(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
uint256 c = a + b;
|
||||
require(c >= a, "ERC20: addition overflow");
|
||||
return c;
|
||||
}
|
||||
|
||||
function _sub(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
require(a >= b, "ERC20: subtraction underflow");
|
||||
return a - b;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
HELPERS
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// We use this complex approach of `_viewChainId` and `_pureChainId` to ensure there are no
|
||||
// compiler warnings when accessing chain ID in any solidity version supported by forge-std. We
|
||||
// can't simply access the chain ID in a normal view or pure function because the solc View Pure
|
||||
// Checker changed `chainid` from pure to view in 0.8.0.
|
||||
function _viewChainId() private view returns (uint256 chainId) {
|
||||
// Assembly required since `block.chainid` was introduced in 0.8.0.
|
||||
assembly {
|
||||
chainId := chainid()
|
||||
}
|
||||
|
||||
address(this); // Silence warnings in older Solc versions.
|
||||
}
|
||||
|
||||
function _pureChainId() private pure returns (uint256 chainId) {
|
||||
function() internal view returns (uint256) fnIn = _viewChainId;
|
||||
function() internal pure returns (uint256) pureChainId;
|
||||
assembly {
|
||||
pureChainId := fnIn
|
||||
}
|
||||
chainId = pureChainId();
|
||||
}
|
||||
}
|
||||
235
lib_forge_std/src/mocks/MockERC721.sol
Normal file
235
lib_forge_std/src/mocks/MockERC721.sol
Normal file
@@ -0,0 +1,235 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
import {IERC721Metadata} from "../interfaces/IERC721.sol";
|
||||
|
||||
/// @notice This is a mock contract of the ERC721 standard for testing purposes only, it SHOULD NOT be used in production.
|
||||
/// @dev Forked from: https://github.com/transmissions11/solmate/blob/0384dbaaa4fcb5715738a9254a7c0a4cb62cf458/src/tokens/ERC721.sol
|
||||
contract MockERC721 is IERC721Metadata {
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
METADATA STORAGE/LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
string internal _name;
|
||||
|
||||
string internal _symbol;
|
||||
|
||||
function name() external view override returns (string memory) {
|
||||
return _name;
|
||||
}
|
||||
|
||||
function symbol() external view override returns (string memory) {
|
||||
return _symbol;
|
||||
}
|
||||
|
||||
function tokenURI(uint256 id) public view virtual override returns (string memory) {}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC721 BALANCE/OWNER STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
mapping(uint256 => address) internal _ownerOf;
|
||||
|
||||
mapping(address => uint256) internal _balanceOf;
|
||||
|
||||
function ownerOf(uint256 id) public view virtual override returns (address owner) {
|
||||
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
|
||||
}
|
||||
|
||||
function balanceOf(address owner) public view virtual override returns (uint256) {
|
||||
require(owner != address(0), "ZERO_ADDRESS");
|
||||
|
||||
return _balanceOf[owner];
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC721 APPROVAL STORAGE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
mapping(uint256 => address) internal _getApproved;
|
||||
|
||||
mapping(address => mapping(address => bool)) internal _isApprovedForAll;
|
||||
|
||||
function getApproved(uint256 id) public view virtual override returns (address) {
|
||||
return _getApproved[id];
|
||||
}
|
||||
|
||||
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
|
||||
return _isApprovedForAll[owner][operator];
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
INITIALIZE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
/// @dev A bool to track whether the contract has been initialized.
|
||||
bool private initialized;
|
||||
|
||||
/// @dev To hide constructor warnings across solc versions due to different constructor visibility requirements and
|
||||
/// syntaxes, we add an initialization function that can be called only once.
|
||||
function initialize(string memory name_, string memory symbol_) public {
|
||||
require(!initialized, "ALREADY_INITIALIZED");
|
||||
|
||||
_name = name_;
|
||||
_symbol = symbol_;
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC721 LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function approve(address spender, uint256 id) public payable virtual override {
|
||||
address owner = _ownerOf[id];
|
||||
|
||||
require(msg.sender == owner || _isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
|
||||
|
||||
_getApproved[id] = spender;
|
||||
|
||||
emit Approval(owner, spender, id);
|
||||
}
|
||||
|
||||
function setApprovalForAll(address operator, bool approved) public virtual override {
|
||||
_isApprovedForAll[msg.sender][operator] = approved;
|
||||
|
||||
emit ApprovalForAll(msg.sender, operator, approved);
|
||||
}
|
||||
|
||||
function transferFrom(address from, address to, uint256 id) public payable virtual override {
|
||||
require(from == _ownerOf[id], "WRONG_FROM");
|
||||
|
||||
require(to != address(0), "INVALID_RECIPIENT");
|
||||
|
||||
require(
|
||||
msg.sender == from || _isApprovedForAll[from][msg.sender] || msg.sender == _getApproved[id],
|
||||
"NOT_AUTHORIZED"
|
||||
);
|
||||
|
||||
// Underflow of the sender's balance is impossible because we check for
|
||||
// ownership above and the recipient's balance can't realistically overflow.
|
||||
_balanceOf[from]--;
|
||||
|
||||
_balanceOf[to]++;
|
||||
|
||||
_ownerOf[id] = to;
|
||||
|
||||
delete _getApproved[id];
|
||||
|
||||
emit Transfer(from, to, id);
|
||||
}
|
||||
|
||||
function safeTransferFrom(address from, address to, uint256 id) public payable virtual override {
|
||||
transferFrom(from, to, id);
|
||||
|
||||
require(
|
||||
!_isContract(to)
|
||||
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "")
|
||||
== IERC721TokenReceiver.onERC721Received.selector,
|
||||
"UNSAFE_RECIPIENT"
|
||||
);
|
||||
}
|
||||
|
||||
function safeTransferFrom(address from, address to, uint256 id, bytes memory data)
|
||||
public
|
||||
payable
|
||||
virtual
|
||||
override
|
||||
{
|
||||
transferFrom(from, to, id);
|
||||
|
||||
require(
|
||||
!_isContract(to)
|
||||
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data)
|
||||
== IERC721TokenReceiver.onERC721Received.selector,
|
||||
"UNSAFE_RECIPIENT"
|
||||
);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
ERC165 LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
||||
return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165
|
||||
|| interfaceId == 0x80ac58cd // ERC165 Interface ID for ERC721
|
||||
|| interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
INTERNAL MINT/BURN LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function _mint(address to, uint256 id) internal virtual {
|
||||
require(to != address(0), "INVALID_RECIPIENT");
|
||||
|
||||
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
|
||||
|
||||
// Counter overflow is incredibly unrealistic.
|
||||
|
||||
_balanceOf[to]++;
|
||||
|
||||
_ownerOf[id] = to;
|
||||
|
||||
emit Transfer(address(0), to, id);
|
||||
}
|
||||
|
||||
function _burn(uint256 id) internal virtual {
|
||||
address owner = _ownerOf[id];
|
||||
|
||||
require(owner != address(0), "NOT_MINTED");
|
||||
|
||||
_balanceOf[owner]--;
|
||||
|
||||
delete _ownerOf[id];
|
||||
|
||||
delete _getApproved[id];
|
||||
|
||||
emit Transfer(owner, address(0), id);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
INTERNAL SAFE MINT LOGIC
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function _safeMint(address to, uint256 id) internal virtual {
|
||||
_mint(to, id);
|
||||
|
||||
require(
|
||||
!_isContract(to)
|
||||
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "")
|
||||
== IERC721TokenReceiver.onERC721Received.selector,
|
||||
"UNSAFE_RECIPIENT"
|
||||
);
|
||||
}
|
||||
|
||||
function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
|
||||
_mint(to, id);
|
||||
|
||||
require(
|
||||
!_isContract(to)
|
||||
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data)
|
||||
== IERC721TokenReceiver.onERC721Received.selector,
|
||||
"UNSAFE_RECIPIENT"
|
||||
);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////
|
||||
HELPERS
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
function _isContract(address _addr) private view returns (bool) {
|
||||
uint256 codeLength;
|
||||
|
||||
// Assembly required for versions < 0.8.0 to check extcodesize.
|
||||
assembly {
|
||||
codeLength := extcodesize(_addr)
|
||||
}
|
||||
|
||||
return codeLength > 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface IERC721TokenReceiver {
|
||||
function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4);
|
||||
}
|
||||
13248
lib_forge_std/src/safeconsole.sol
Normal file
13248
lib_forge_std/src/safeconsole.sol
Normal file
File diff suppressed because it is too large
Load Diff
145
lib_forge_std/test/StdAssertions.t.sol
Normal file
145
lib_forge_std/test/StdAssertions.t.sol
Normal file
@@ -0,0 +1,145 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/StdAssertions.sol";
|
||||
import {Vm} from "../src/Vm.sol";
|
||||
|
||||
interface VmInternal is Vm {
|
||||
function _expectCheatcodeRevert(bytes memory message) external;
|
||||
}
|
||||
|
||||
contract StdAssertionsTest is StdAssertions {
|
||||
string constant errorMessage = "User provided message";
|
||||
uint256 constant maxDecimals = 77;
|
||||
|
||||
bool constant SHOULD_REVERT = true;
|
||||
bool constant SHOULD_RETURN = false;
|
||||
|
||||
bool constant STRICT_REVERT_DATA = true;
|
||||
bool constant NON_STRICT_REVERT_DATA = false;
|
||||
|
||||
VmInternal constant vm = VmInternal(address(uint160(uint256(keccak256("hevm cheat code")))));
|
||||
|
||||
function testFuzz_AssertEqCall_Return_Pass(
|
||||
bytes memory callDataA,
|
||||
bytes memory callDataB,
|
||||
bytes memory returnData,
|
||||
bool strictRevertData
|
||||
) external {
|
||||
address targetA = address(new TestMockCall(returnData, SHOULD_RETURN));
|
||||
address targetB = address(new TestMockCall(returnData, SHOULD_RETURN));
|
||||
|
||||
assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData);
|
||||
}
|
||||
|
||||
function testFuzz_RevertWhenCalled_AssertEqCall_Return_Fail(
|
||||
bytes memory callDataA,
|
||||
bytes memory callDataB,
|
||||
bytes memory returnDataA,
|
||||
bytes memory returnDataB,
|
||||
bool strictRevertData
|
||||
) external {
|
||||
vm.assume(keccak256(returnDataA) != keccak256(returnDataB));
|
||||
|
||||
address targetA = address(new TestMockCall(returnDataA, SHOULD_RETURN));
|
||||
address targetB = address(new TestMockCall(returnDataB, SHOULD_RETURN));
|
||||
|
||||
vm._expectCheatcodeRevert(
|
||||
bytes(
|
||||
string.concat(
|
||||
"Call return data does not match: ", vm.toString(returnDataA), " != ", vm.toString(returnDataB)
|
||||
)
|
||||
)
|
||||
);
|
||||
assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData);
|
||||
}
|
||||
|
||||
function testFuzz_AssertEqCall_Revert_Pass(
|
||||
bytes memory callDataA,
|
||||
bytes memory callDataB,
|
||||
bytes memory revertDataA,
|
||||
bytes memory revertDataB
|
||||
) external {
|
||||
address targetA = address(new TestMockCall(revertDataA, SHOULD_REVERT));
|
||||
address targetB = address(new TestMockCall(revertDataB, SHOULD_REVERT));
|
||||
|
||||
assertEqCall(targetA, callDataA, targetB, callDataB, NON_STRICT_REVERT_DATA);
|
||||
}
|
||||
|
||||
function testFuzz_RevertWhenCalled_AssertEqCall_Revert_Fail(
|
||||
bytes memory callDataA,
|
||||
bytes memory callDataB,
|
||||
bytes memory revertDataA,
|
||||
bytes memory revertDataB
|
||||
) external {
|
||||
vm.assume(keccak256(revertDataA) != keccak256(revertDataB));
|
||||
|
||||
address targetA = address(new TestMockCall(revertDataA, SHOULD_REVERT));
|
||||
address targetB = address(new TestMockCall(revertDataB, SHOULD_REVERT));
|
||||
|
||||
vm._expectCheatcodeRevert(
|
||||
bytes(
|
||||
string.concat(
|
||||
"Call revert data does not match: ", vm.toString(revertDataA), " != ", vm.toString(revertDataB)
|
||||
)
|
||||
)
|
||||
);
|
||||
assertEqCall(targetA, callDataA, targetB, callDataB, STRICT_REVERT_DATA);
|
||||
}
|
||||
|
||||
function testFuzz_RevertWhenCalled_AssertEqCall_Fail(
|
||||
bytes memory callDataA,
|
||||
bytes memory callDataB,
|
||||
bytes memory returnDataA,
|
||||
bytes memory returnDataB,
|
||||
bool strictRevertData
|
||||
) external {
|
||||
address targetA = address(new TestMockCall(returnDataA, SHOULD_RETURN));
|
||||
address targetB = address(new TestMockCall(returnDataB, SHOULD_REVERT));
|
||||
|
||||
vm.expectRevert(bytes("assertion failed"));
|
||||
this.assertEqCallExternal(targetA, callDataA, targetB, callDataB, strictRevertData);
|
||||
|
||||
vm.expectRevert(bytes("assertion failed"));
|
||||
this.assertEqCallExternal(targetB, callDataB, targetA, callDataA, strictRevertData);
|
||||
}
|
||||
|
||||
// Helper function to test outcome of assertEqCall via `expect` cheatcodes
|
||||
function assertEqCallExternal(
|
||||
address targetA,
|
||||
bytes memory callDataA,
|
||||
address targetB,
|
||||
bytes memory callDataB,
|
||||
bool strictRevertData
|
||||
) public {
|
||||
assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData);
|
||||
}
|
||||
|
||||
function testFailFail() public {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
contract TestMockCall {
|
||||
bytes returnData;
|
||||
bool shouldRevert;
|
||||
|
||||
constructor(bytes memory returnData_, bool shouldRevert_) {
|
||||
returnData = returnData_;
|
||||
shouldRevert = shouldRevert_;
|
||||
}
|
||||
|
||||
fallback() external payable {
|
||||
bytes memory returnData_ = returnData;
|
||||
|
||||
if (shouldRevert) {
|
||||
assembly {
|
||||
revert(add(returnData_, 0x20), mload(returnData_))
|
||||
}
|
||||
} else {
|
||||
assembly {
|
||||
return(add(returnData_, 0x20), mload(returnData_))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
221
lib_forge_std/test/StdChains.t.sol
Normal file
221
lib_forge_std/test/StdChains.t.sol
Normal file
@@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdChainsMock is Test {
|
||||
function exposed_getChain(string memory chainAlias) public returns (Chain memory) {
|
||||
return getChain(chainAlias);
|
||||
}
|
||||
|
||||
function exposed_getChain(uint256 chainId) public returns (Chain memory) {
|
||||
return getChain(chainId);
|
||||
}
|
||||
|
||||
function exposed_setChain(string memory chainAlias, ChainData memory chainData) public {
|
||||
setChain(chainAlias, chainData);
|
||||
}
|
||||
|
||||
function exposed_setFallbackToDefaultRpcUrls(bool useDefault) public {
|
||||
setFallbackToDefaultRpcUrls(useDefault);
|
||||
}
|
||||
}
|
||||
|
||||
contract StdChainsTest is Test {
|
||||
function test_ChainRpcInitialization() public {
|
||||
// RPCs specified in `foundry.toml` should be updated.
|
||||
assertEq(getChain(1).rpcUrl, "https://eth-mainnet.alchemyapi.io/v2/WV407BEiBmjNJfKo9Uo_55u0z0ITyCOX");
|
||||
assertEq(getChain("optimism_sepolia").rpcUrl, "https://sepolia.optimism.io/");
|
||||
assertEq(getChain("arbitrum_one_sepolia").rpcUrl, "https://sepolia-rollup.arbitrum.io/rpc/");
|
||||
|
||||
// Environment variables should be the next fallback
|
||||
assertEq(getChain("arbitrum_nova").rpcUrl, "https://nova.arbitrum.io/rpc");
|
||||
vm.setEnv("ARBITRUM_NOVA_RPC_URL", "myoverride");
|
||||
assertEq(getChain("arbitrum_nova").rpcUrl, "myoverride");
|
||||
vm.setEnv("ARBITRUM_NOVA_RPC_URL", "https://nova.arbitrum.io/rpc");
|
||||
|
||||
// Cannot override RPCs defined in `foundry.toml`
|
||||
vm.setEnv("MAINNET_RPC_URL", "myoverride2");
|
||||
assertEq(getChain("mainnet").rpcUrl, "https://eth-mainnet.alchemyapi.io/v2/WV407BEiBmjNJfKo9Uo_55u0z0ITyCOX");
|
||||
|
||||
// Other RPCs should remain unchanged.
|
||||
assertEq(getChain(31337).rpcUrl, "http://127.0.0.1:8545");
|
||||
assertEq(getChain("sepolia").rpcUrl, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001");
|
||||
}
|
||||
|
||||
// Named with a leading underscore to clarify this is not intended to be run as a normal test,
|
||||
// and is intended to be used in the below `test_Rpcs` test.
|
||||
function _testRpc(string memory rpcAlias) internal {
|
||||
string memory rpcUrl = getChain(rpcAlias).rpcUrl;
|
||||
vm.createSelectFork(rpcUrl);
|
||||
}
|
||||
|
||||
// Ensure we can connect to the default RPC URL for each chain.
|
||||
// Currently commented out since this is slow and public RPCs are flaky, often resulting in failing CI.
|
||||
// function test_Rpcs() public {
|
||||
// _testRpc("mainnet");
|
||||
// _testRpc("sepolia");
|
||||
// _testRpc("holesky");
|
||||
// _testRpc("optimism");
|
||||
// _testRpc("optimism_sepolia");
|
||||
// _testRpc("arbitrum_one");
|
||||
// _testRpc("arbitrum_one_sepolia");
|
||||
// _testRpc("arbitrum_nova");
|
||||
// _testRpc("polygon");
|
||||
// _testRpc("polygon_amoy");
|
||||
// _testRpc("avalanche");
|
||||
// _testRpc("avalanche_fuji");
|
||||
// _testRpc("bnb_smart_chain");
|
||||
// _testRpc("bnb_smart_chain_testnet");
|
||||
// _testRpc("gnosis_chain");
|
||||
// _testRpc("moonbeam");
|
||||
// _testRpc("moonriver");
|
||||
// _testRpc("moonbase");
|
||||
// _testRpc("base_sepolia");
|
||||
// _testRpc("base");
|
||||
// _testRpc("fraxtal");
|
||||
// _testRpc("fraxtal_testnet");
|
||||
// }
|
||||
|
||||
function test_ChainNoDefault() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains getChain(string): Chain with alias \"does_not_exist\" not found.");
|
||||
stdChainsMock.exposed_getChain("does_not_exist");
|
||||
}
|
||||
|
||||
function test_SetChainFirstFails() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains setChain(string,ChainData): Chain ID 31337 already used by \"anvil\".");
|
||||
stdChainsMock.exposed_setChain("anvil2", ChainData("Anvil", 31337, "URL"));
|
||||
}
|
||||
|
||||
function test_ChainBubbleUp() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
stdChainsMock.exposed_setChain("needs_undefined_env_var", ChainData("", 123456789, ""));
|
||||
vm.expectRevert(
|
||||
"Failed to resolve env var `UNDEFINED_RPC_URL_PLACEHOLDER` in `${UNDEFINED_RPC_URL_PLACEHOLDER}`: environment variable not found"
|
||||
);
|
||||
stdChainsMock.exposed_getChain("needs_undefined_env_var");
|
||||
}
|
||||
|
||||
function test_CannotSetChain_ChainIdExists() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
stdChainsMock.exposed_setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
|
||||
|
||||
vm.expectRevert('StdChains setChain(string,ChainData): Chain ID 123456789 already used by "custom_chain".');
|
||||
|
||||
stdChainsMock.exposed_setChain("another_custom_chain", ChainData("", 123456789, ""));
|
||||
}
|
||||
|
||||
function test_SetChain() public {
|
||||
setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
|
||||
Chain memory customChain = getChain("custom_chain");
|
||||
assertEq(customChain.name, "Custom Chain");
|
||||
assertEq(customChain.chainId, 123456789);
|
||||
assertEq(customChain.chainAlias, "custom_chain");
|
||||
assertEq(customChain.rpcUrl, "https://custom.chain/");
|
||||
Chain memory chainById = getChain(123456789);
|
||||
assertEq(chainById.name, customChain.name);
|
||||
assertEq(chainById.chainId, customChain.chainId);
|
||||
assertEq(chainById.chainAlias, customChain.chainAlias);
|
||||
assertEq(chainById.rpcUrl, customChain.rpcUrl);
|
||||
customChain.name = "Another Custom Chain";
|
||||
customChain.chainId = 987654321;
|
||||
setChain("another_custom_chain", customChain);
|
||||
Chain memory anotherCustomChain = getChain("another_custom_chain");
|
||||
assertEq(anotherCustomChain.name, "Another Custom Chain");
|
||||
assertEq(anotherCustomChain.chainId, 987654321);
|
||||
assertEq(anotherCustomChain.chainAlias, "another_custom_chain");
|
||||
assertEq(anotherCustomChain.rpcUrl, "https://custom.chain/");
|
||||
// Verify the first chain data was not overwritten
|
||||
chainById = getChain(123456789);
|
||||
assertEq(chainById.name, "Custom Chain");
|
||||
assertEq(chainById.chainId, 123456789);
|
||||
}
|
||||
|
||||
function test_SetNoEmptyAlias() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains setChain(string,ChainData): Chain alias cannot be the empty string.");
|
||||
stdChainsMock.exposed_setChain("", ChainData("", 123456789, ""));
|
||||
}
|
||||
|
||||
function test_SetNoChainId0() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains setChain(string,ChainData): Chain ID cannot be 0.");
|
||||
stdChainsMock.exposed_setChain("alias", ChainData("", 0, ""));
|
||||
}
|
||||
|
||||
function test_GetNoChainId0() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains getChain(uint256): Chain ID cannot be 0.");
|
||||
stdChainsMock.exposed_getChain(0);
|
||||
}
|
||||
|
||||
function test_GetNoEmptyAlias() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains getChain(string): Chain alias cannot be the empty string.");
|
||||
stdChainsMock.exposed_getChain("");
|
||||
}
|
||||
|
||||
function test_ChainIdNotFound() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains getChain(string): Chain with alias \"no_such_alias\" not found.");
|
||||
stdChainsMock.exposed_getChain("no_such_alias");
|
||||
}
|
||||
|
||||
function test_ChainAliasNotFound() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
vm.expectRevert("StdChains getChain(uint256): Chain with ID 321 not found.");
|
||||
|
||||
stdChainsMock.exposed_getChain(321);
|
||||
}
|
||||
|
||||
function test_SetChain_ExistingOne() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
|
||||
assertEq(getChain(123456789).chainId, 123456789);
|
||||
|
||||
setChain("custom_chain", ChainData("Modified Chain", 999999999, "https://modified.chain/"));
|
||||
vm.expectRevert("StdChains getChain(uint256): Chain with ID 123456789 not found.");
|
||||
stdChainsMock.exposed_getChain(123456789);
|
||||
|
||||
Chain memory modifiedChain = getChain(999999999);
|
||||
assertEq(modifiedChain.name, "Modified Chain");
|
||||
assertEq(modifiedChain.chainId, 999999999);
|
||||
assertEq(modifiedChain.rpcUrl, "https://modified.chain/");
|
||||
}
|
||||
|
||||
function test_DontUseDefaultRpcUrl() public {
|
||||
// We deploy a mock to properly test the revert.
|
||||
StdChainsMock stdChainsMock = new StdChainsMock();
|
||||
|
||||
// Should error if default RPCs flag is set to false.
|
||||
stdChainsMock.exposed_setFallbackToDefaultRpcUrls(false);
|
||||
vm.expectRevert();
|
||||
stdChainsMock.exposed_getChain(31337);
|
||||
vm.expectRevert();
|
||||
stdChainsMock.exposed_getChain("sepolia");
|
||||
}
|
||||
}
|
||||
618
lib_forge_std/test/StdCheats.t.sol
Normal file
618
lib_forge_std/test/StdCheats.t.sol
Normal file
@@ -0,0 +1,618 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/StdCheats.sol";
|
||||
import "../src/Test.sol";
|
||||
import "../src/StdJson.sol";
|
||||
import "../src/StdToml.sol";
|
||||
import "../src/interfaces/IERC20.sol";
|
||||
|
||||
contract StdCheatsTest is Test {
|
||||
Bar test;
|
||||
|
||||
using stdJson for string;
|
||||
|
||||
function setUp() public {
|
||||
test = new Bar();
|
||||
}
|
||||
|
||||
function test_Skip() public {
|
||||
vm.warp(100);
|
||||
skip(25);
|
||||
assertEq(block.timestamp, 125);
|
||||
}
|
||||
|
||||
function test_Rewind() public {
|
||||
vm.warp(100);
|
||||
rewind(25);
|
||||
assertEq(block.timestamp, 75);
|
||||
}
|
||||
|
||||
function test_Hoax() public {
|
||||
hoax(address(1337));
|
||||
test.bar{value: 100}(address(1337));
|
||||
}
|
||||
|
||||
function test_HoaxOrigin() public {
|
||||
hoax(address(1337), address(1337));
|
||||
test.origin{value: 100}(address(1337));
|
||||
}
|
||||
|
||||
function test_HoaxDifferentAddresses() public {
|
||||
hoax(address(1337), address(7331));
|
||||
test.origin{value: 100}(address(1337), address(7331));
|
||||
}
|
||||
|
||||
function test_StartHoax() public {
|
||||
startHoax(address(1337));
|
||||
test.bar{value: 100}(address(1337));
|
||||
test.bar{value: 100}(address(1337));
|
||||
vm.stopPrank();
|
||||
test.bar(address(this));
|
||||
}
|
||||
|
||||
function test_StartHoaxOrigin() public {
|
||||
startHoax(address(1337), address(1337));
|
||||
test.origin{value: 100}(address(1337));
|
||||
test.origin{value: 100}(address(1337));
|
||||
vm.stopPrank();
|
||||
test.bar(address(this));
|
||||
}
|
||||
|
||||
function test_ChangePrankMsgSender() public {
|
||||
vm.startPrank(address(1337));
|
||||
test.bar(address(1337));
|
||||
changePrank(address(0xdead));
|
||||
test.bar(address(0xdead));
|
||||
changePrank(address(1337));
|
||||
test.bar(address(1337));
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_ChangePrankMsgSenderAndTxOrigin() public {
|
||||
vm.startPrank(address(1337), address(1338));
|
||||
test.origin(address(1337), address(1338));
|
||||
changePrank(address(0xdead), address(0xbeef));
|
||||
test.origin(address(0xdead), address(0xbeef));
|
||||
changePrank(address(1337), address(1338));
|
||||
test.origin(address(1337), address(1338));
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_MakeAccountEquivalence() public {
|
||||
Account memory account = makeAccount("1337");
|
||||
(address addr, uint256 key) = makeAddrAndKey("1337");
|
||||
assertEq(account.addr, addr);
|
||||
assertEq(account.key, key);
|
||||
}
|
||||
|
||||
function test_MakeAddrEquivalence() public {
|
||||
(address addr,) = makeAddrAndKey("1337");
|
||||
assertEq(makeAddr("1337"), addr);
|
||||
}
|
||||
|
||||
function test_MakeAddrSigning() public {
|
||||
(address addr, uint256 key) = makeAddrAndKey("1337");
|
||||
bytes32 hash = keccak256("some_message");
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(key, hash);
|
||||
assertEq(ecrecover(hash, v, r, s), addr);
|
||||
}
|
||||
|
||||
function test_Deal() public {
|
||||
deal(address(this), 1 ether);
|
||||
assertEq(address(this).balance, 1 ether);
|
||||
}
|
||||
|
||||
function test_DealToken() public {
|
||||
Bar barToken = new Bar();
|
||||
address bar = address(barToken);
|
||||
deal(bar, address(this), 10000e18);
|
||||
assertEq(barToken.balanceOf(address(this)), 10000e18);
|
||||
}
|
||||
|
||||
function test_DealTokenAdjustTotalSupply() public {
|
||||
Bar barToken = new Bar();
|
||||
address bar = address(barToken);
|
||||
deal(bar, address(this), 10000e18, true);
|
||||
assertEq(barToken.balanceOf(address(this)), 10000e18);
|
||||
assertEq(barToken.totalSupply(), 20000e18);
|
||||
deal(bar, address(this), 0, true);
|
||||
assertEq(barToken.balanceOf(address(this)), 0);
|
||||
assertEq(barToken.totalSupply(), 10000e18);
|
||||
}
|
||||
|
||||
function test_DealERC1155Token() public {
|
||||
BarERC1155 barToken = new BarERC1155();
|
||||
address bar = address(barToken);
|
||||
dealERC1155(bar, address(this), 0, 10000e18, false);
|
||||
assertEq(barToken.balanceOf(address(this), 0), 10000e18);
|
||||
}
|
||||
|
||||
function test_DealERC1155TokenAdjustTotalSupply() public {
|
||||
BarERC1155 barToken = new BarERC1155();
|
||||
address bar = address(barToken);
|
||||
dealERC1155(bar, address(this), 0, 10000e18, true);
|
||||
assertEq(barToken.balanceOf(address(this), 0), 10000e18);
|
||||
assertEq(barToken.totalSupply(0), 20000e18);
|
||||
dealERC1155(bar, address(this), 0, 0, true);
|
||||
assertEq(barToken.balanceOf(address(this), 0), 0);
|
||||
assertEq(barToken.totalSupply(0), 10000e18);
|
||||
}
|
||||
|
||||
function test_DealERC721Token() public {
|
||||
BarERC721 barToken = new BarERC721();
|
||||
address bar = address(barToken);
|
||||
dealERC721(bar, address(2), 1);
|
||||
assertEq(barToken.balanceOf(address(2)), 1);
|
||||
assertEq(barToken.balanceOf(address(1)), 0);
|
||||
dealERC721(bar, address(1), 2);
|
||||
assertEq(barToken.balanceOf(address(1)), 1);
|
||||
assertEq(barToken.balanceOf(bar), 1);
|
||||
}
|
||||
|
||||
function test_DeployCode() public {
|
||||
address deployed = deployCode("StdCheats.t.sol:Bar", bytes(""));
|
||||
assertEq(string(getCode(deployed)), string(getCode(address(test))));
|
||||
}
|
||||
|
||||
function test_DestroyAccount() public {
|
||||
// deploy something to destroy it
|
||||
BarERC721 barToken = new BarERC721();
|
||||
address bar = address(barToken);
|
||||
vm.setNonce(bar, 10);
|
||||
deal(bar, 100);
|
||||
|
||||
uint256 prevThisBalance = address(this).balance;
|
||||
uint256 size;
|
||||
assembly {
|
||||
size := extcodesize(bar)
|
||||
}
|
||||
|
||||
assertGt(size, 0);
|
||||
assertEq(bar.balance, 100);
|
||||
assertEq(vm.getNonce(bar), 10);
|
||||
|
||||
destroyAccount(bar, address(this));
|
||||
assembly {
|
||||
size := extcodesize(bar)
|
||||
}
|
||||
assertEq(address(this).balance, prevThisBalance + 100);
|
||||
assertEq(vm.getNonce(bar), 0);
|
||||
assertEq(size, 0);
|
||||
assertEq(bar.balance, 0);
|
||||
}
|
||||
|
||||
function test_DeployCodeNoArgs() public {
|
||||
address deployed = deployCode("StdCheats.t.sol:Bar");
|
||||
assertEq(string(getCode(deployed)), string(getCode(address(test))));
|
||||
}
|
||||
|
||||
function test_DeployCodeVal() public {
|
||||
address deployed = deployCode("StdCheats.t.sol:Bar", bytes(""), 1 ether);
|
||||
assertEq(string(getCode(deployed)), string(getCode(address(test))));
|
||||
assertEq(deployed.balance, 1 ether);
|
||||
}
|
||||
|
||||
function test_DeployCodeValNoArgs() public {
|
||||
address deployed = deployCode("StdCheats.t.sol:Bar", 1 ether);
|
||||
assertEq(string(getCode(deployed)), string(getCode(address(test))));
|
||||
assertEq(deployed.balance, 1 ether);
|
||||
}
|
||||
|
||||
// We need this so we can call "this.deployCode" rather than "deployCode" directly
|
||||
function deployCodeHelper(string memory what) external {
|
||||
deployCode(what);
|
||||
}
|
||||
|
||||
function test_DeployCodeFail() public {
|
||||
vm.expectRevert(bytes("StdCheats deployCode(string): Deployment failed."));
|
||||
this.deployCodeHelper("StdCheats.t.sol:RevertingContract");
|
||||
}
|
||||
|
||||
function getCode(address who) internal view returns (bytes memory o_code) {
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
// retrieve the size of the code, this needs assembly
|
||||
let size := extcodesize(who)
|
||||
// allocate output byte array - this could also be done without assembly
|
||||
// by using o_code = new bytes(size)
|
||||
o_code := mload(0x40)
|
||||
// new "memory end" including padding
|
||||
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
|
||||
// store length in memory
|
||||
mstore(o_code, size)
|
||||
// actually retrieve the code, this needs assembly
|
||||
extcodecopy(who, add(o_code, 0x20), 0, size)
|
||||
}
|
||||
}
|
||||
|
||||
function test_DeriveRememberKey() public {
|
||||
string memory mnemonic = "test test test test test test test test test test test junk";
|
||||
|
||||
(address deployer, uint256 privateKey) = deriveRememberKey(mnemonic, 0);
|
||||
assertEq(deployer, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
|
||||
assertEq(privateKey, 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80);
|
||||
}
|
||||
|
||||
function test_BytesToUint() public pure {
|
||||
assertEq(3, bytesToUint_test(hex"03"));
|
||||
assertEq(2, bytesToUint_test(hex"02"));
|
||||
assertEq(255, bytesToUint_test(hex"ff"));
|
||||
assertEq(29625, bytesToUint_test(hex"73b9"));
|
||||
}
|
||||
|
||||
function test_ParseJsonTxDetail() public view {
|
||||
string memory root = vm.projectRoot();
|
||||
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
|
||||
string memory json = vm.readFile(path);
|
||||
bytes memory transactionDetails = json.parseRaw(".transactions[0].tx");
|
||||
RawTx1559Detail memory rawTxDetail = abi.decode(transactionDetails, (RawTx1559Detail));
|
||||
Tx1559Detail memory txDetail = rawToConvertedEIP1559Detail(rawTxDetail);
|
||||
assertEq(txDetail.from, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
|
||||
assertEq(txDetail.to, 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512);
|
||||
assertEq(
|
||||
txDetail.data,
|
||||
hex"23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004"
|
||||
);
|
||||
assertEq(txDetail.nonce, 3);
|
||||
assertEq(txDetail.txType, 2);
|
||||
assertEq(txDetail.gas, 29625);
|
||||
assertEq(txDetail.value, 0);
|
||||
}
|
||||
|
||||
function test_ReadEIP1559Transaction() public view {
|
||||
string memory root = vm.projectRoot();
|
||||
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
|
||||
uint256 index = 0;
|
||||
Tx1559 memory transaction = readTx1559(path, index);
|
||||
transaction;
|
||||
}
|
||||
|
||||
function test_ReadEIP1559Transactions() public view {
|
||||
string memory root = vm.projectRoot();
|
||||
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
|
||||
Tx1559[] memory transactions = readTx1559s(path);
|
||||
transactions;
|
||||
}
|
||||
|
||||
function test_ReadReceipt() public view {
|
||||
string memory root = vm.projectRoot();
|
||||
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
|
||||
uint256 index = 5;
|
||||
Receipt memory receipt = readReceipt(path, index);
|
||||
assertEq(
|
||||
receipt.logsBloom,
|
||||
hex"00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100"
|
||||
);
|
||||
}
|
||||
|
||||
function test_ReadReceipts() public view {
|
||||
string memory root = vm.projectRoot();
|
||||
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
|
||||
Receipt[] memory receipts = readReceipts(path);
|
||||
receipts;
|
||||
}
|
||||
|
||||
function test_GasMeteringModifier() public {
|
||||
uint256 gas_start_normal = gasleft();
|
||||
addInLoop();
|
||||
uint256 gas_used_normal = gas_start_normal - gasleft();
|
||||
|
||||
uint256 gas_start_single = gasleft();
|
||||
addInLoopNoGas();
|
||||
uint256 gas_used_single = gas_start_single - gasleft();
|
||||
|
||||
uint256 gas_start_double = gasleft();
|
||||
addInLoopNoGasNoGas();
|
||||
uint256 gas_used_double = gas_start_double - gasleft();
|
||||
|
||||
assertTrue(gas_used_double + gas_used_single < gas_used_normal);
|
||||
}
|
||||
|
||||
function addInLoop() internal pure returns (uint256) {
|
||||
uint256 b;
|
||||
for (uint256 i; i < 10000; i++) {
|
||||
b += i;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
function addInLoopNoGas() internal noGasMetering returns (uint256) {
|
||||
return addInLoop();
|
||||
}
|
||||
|
||||
function addInLoopNoGasNoGas() internal noGasMetering returns (uint256) {
|
||||
return addInLoopNoGas();
|
||||
}
|
||||
|
||||
function bytesToUint_test(bytes memory b) private pure returns (uint256) {
|
||||
uint256 number;
|
||||
for (uint256 i = 0; i < b.length; i++) {
|
||||
number = number + uint256(uint8(b[i])) * (2 ** (8 * (b.length - (i + 1))));
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
function testFuzz_AssumeAddressIsNot(address addr) external {
|
||||
// skip over Payable and NonPayable enums
|
||||
for (uint8 i = 2; i < uint8(type(AddressType).max); i++) {
|
||||
assumeAddressIsNot(addr, AddressType(i));
|
||||
}
|
||||
assertTrue(addr != address(0));
|
||||
assertTrue(addr < address(1) || addr > address(9));
|
||||
assertTrue(addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67);
|
||||
}
|
||||
|
||||
function test_AssumePayable() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdCheatsMock stdCheatsMock = new StdCheatsMock();
|
||||
|
||||
// all should revert since these addresses are not payable
|
||||
|
||||
// VM address
|
||||
vm.expectRevert();
|
||||
stdCheatsMock.exposed_assumePayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
|
||||
|
||||
// Console address
|
||||
vm.expectRevert();
|
||||
stdCheatsMock.exposed_assumePayable(0x000000000000000000636F6e736F6c652e6c6f67);
|
||||
|
||||
// Create2Deployer
|
||||
vm.expectRevert();
|
||||
stdCheatsMock.exposed_assumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C);
|
||||
|
||||
// all should pass since these addresses are payable
|
||||
|
||||
// vitalik.eth
|
||||
stdCheatsMock.exposed_assumePayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);
|
||||
|
||||
// mock payable contract
|
||||
MockContractPayable cp = new MockContractPayable();
|
||||
stdCheatsMock.exposed_assumePayable(address(cp));
|
||||
}
|
||||
|
||||
function test_AssumeNotPayable() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdCheatsMock stdCheatsMock = new StdCheatsMock();
|
||||
|
||||
// all should pass since these addresses are not payable
|
||||
|
||||
// VM address
|
||||
stdCheatsMock.exposed_assumeNotPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
|
||||
|
||||
// Console address
|
||||
stdCheatsMock.exposed_assumeNotPayable(0x000000000000000000636F6e736F6c652e6c6f67);
|
||||
|
||||
// Create2Deployer
|
||||
stdCheatsMock.exposed_assumeNotPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C);
|
||||
|
||||
// all should revert since these addresses are payable
|
||||
|
||||
// vitalik.eth
|
||||
vm.expectRevert();
|
||||
stdCheatsMock.exposed_assumeNotPayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);
|
||||
|
||||
// mock payable contract
|
||||
MockContractPayable cp = new MockContractPayable();
|
||||
vm.expectRevert();
|
||||
stdCheatsMock.exposed_assumeNotPayable(address(cp));
|
||||
}
|
||||
|
||||
function testFuzz_AssumeNotPrecompile(address addr) external {
|
||||
assumeNotPrecompile(addr, getChain("optimism_sepolia").chainId);
|
||||
assertTrue(
|
||||
addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000))
|
||||
|| addr > address(0x4200000000000000000000000000000000000800)
|
||||
);
|
||||
}
|
||||
|
||||
function testFuzz_AssumeNotForgeAddress(address addr) external pure {
|
||||
assumeNotForgeAddress(addr);
|
||||
assertTrue(
|
||||
addr != address(vm) && addr != 0x000000000000000000636F6e736F6c652e6c6f67
|
||||
&& addr != 0x4e59b44847b379578588920cA78FbF26c0B4956C
|
||||
);
|
||||
}
|
||||
|
||||
function test_CannotDeployCodeTo() external {
|
||||
vm.expectRevert("StdCheats deployCodeTo(string,bytes,uint256,address): Failed to create runtime bytecode.");
|
||||
this._revertDeployCodeTo();
|
||||
}
|
||||
|
||||
function _revertDeployCodeTo() external {
|
||||
deployCodeTo("StdCheats.t.sol:RevertingContract", address(0));
|
||||
}
|
||||
|
||||
function test_DeployCodeTo() external {
|
||||
address arbitraryAddress = makeAddr("arbitraryAddress");
|
||||
|
||||
deployCodeTo(
|
||||
"StdCheats.t.sol:MockContractWithConstructorArgs",
|
||||
abi.encode(uint256(6), true, bytes20(arbitraryAddress)),
|
||||
1 ether,
|
||||
arbitraryAddress
|
||||
);
|
||||
|
||||
MockContractWithConstructorArgs ct = MockContractWithConstructorArgs(arbitraryAddress);
|
||||
|
||||
assertEq(arbitraryAddress.balance, 1 ether);
|
||||
assertEq(ct.x(), 6);
|
||||
assertTrue(ct.y());
|
||||
assertEq(ct.z(), bytes20(arbitraryAddress));
|
||||
}
|
||||
}
|
||||
|
||||
contract StdCheatsMock is StdCheats {
|
||||
function exposed_assumePayable(address addr) external {
|
||||
assumePayable(addr);
|
||||
}
|
||||
|
||||
function exposed_assumeNotPayable(address addr) external {
|
||||
assumeNotPayable(addr);
|
||||
}
|
||||
|
||||
// We deploy a mock version so we can properly test expected reverts.
|
||||
function exposed_assumeNotBlacklisted(address token, address addr) external view {
|
||||
return assumeNotBlacklisted(token, addr);
|
||||
}
|
||||
}
|
||||
|
||||
contract StdCheatsForkTest is Test {
|
||||
address internal constant SHIB = 0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE;
|
||||
address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
|
||||
address internal constant USDC_BLACKLISTED_USER = 0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD;
|
||||
address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
|
||||
address internal constant USDT_BLACKLISTED_USER = 0x8f8a8F4B54a2aAC7799d7bc81368aC27b852822A;
|
||||
|
||||
function setUp() public {
|
||||
// All tests of the `assumeNotBlacklisted` method are fork tests using live contracts.
|
||||
vm.createSelectFork({urlOrAlias: "mainnet", blockNumber: 16_428_900});
|
||||
}
|
||||
|
||||
function test_CannotAssumeNoBlacklisted_EOA() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdCheatsMock stdCheatsMock = new StdCheatsMock();
|
||||
address eoa = vm.addr({privateKey: 1});
|
||||
vm.expectRevert("StdCheats assumeNotBlacklisted(address,address): Token address is not a contract.");
|
||||
stdCheatsMock.exposed_assumeNotBlacklisted(eoa, address(0));
|
||||
}
|
||||
|
||||
function testFuzz_AssumeNotBlacklisted_TokenWithoutBlacklist(address addr) external view {
|
||||
assumeNotBlacklisted(SHIB, addr);
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
function test_AssumeNoBlacklisted_USDC() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdCheatsMock stdCheatsMock = new StdCheatsMock();
|
||||
vm.expectRevert();
|
||||
stdCheatsMock.exposed_assumeNotBlacklisted(USDC, USDC_BLACKLISTED_USER);
|
||||
}
|
||||
|
||||
function testFuzz_AssumeNotBlacklisted_USDC(address addr) external view {
|
||||
assumeNotBlacklisted(USDC, addr);
|
||||
assertFalse(USDCLike(USDC).isBlacklisted(addr));
|
||||
}
|
||||
|
||||
function test_AssumeNoBlacklisted_USDT() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdCheatsMock stdCheatsMock = new StdCheatsMock();
|
||||
vm.expectRevert();
|
||||
stdCheatsMock.exposed_assumeNotBlacklisted(USDT, USDT_BLACKLISTED_USER);
|
||||
}
|
||||
|
||||
function testFuzz_AssumeNotBlacklisted_USDT(address addr) external view {
|
||||
assumeNotBlacklisted(USDT, addr);
|
||||
assertFalse(USDTLike(USDT).isBlackListed(addr));
|
||||
}
|
||||
|
||||
function test_dealUSDC() external {
|
||||
// roll fork to the point when USDC contract updated to store balance in packed slots
|
||||
vm.rollFork(19279215);
|
||||
|
||||
uint256 balance = 100e6;
|
||||
deal(USDC, address(this), balance);
|
||||
assertEq(IERC20(USDC).balanceOf(address(this)), balance);
|
||||
}
|
||||
}
|
||||
|
||||
contract Bar {
|
||||
constructor() payable {
|
||||
/// `DEAL` STDCHEAT
|
||||
totalSupply = 10000e18;
|
||||
balanceOf[address(this)] = totalSupply;
|
||||
}
|
||||
|
||||
/// `HOAX` and `CHANGEPRANK` STDCHEATS
|
||||
function bar(address expectedSender) public payable {
|
||||
require(msg.sender == expectedSender, "!prank");
|
||||
}
|
||||
|
||||
function origin(address expectedSender) public payable {
|
||||
require(msg.sender == expectedSender, "!prank");
|
||||
require(tx.origin == expectedSender, "!prank");
|
||||
}
|
||||
|
||||
function origin(address expectedSender, address expectedOrigin) public payable {
|
||||
require(msg.sender == expectedSender, "!prank");
|
||||
require(tx.origin == expectedOrigin, "!prank");
|
||||
}
|
||||
|
||||
/// `DEAL` STDCHEAT
|
||||
mapping(address => uint256) public balanceOf;
|
||||
uint256 public totalSupply;
|
||||
}
|
||||
|
||||
contract BarERC1155 {
|
||||
constructor() payable {
|
||||
/// `DEALERC1155` STDCHEAT
|
||||
_totalSupply[0] = 10000e18;
|
||||
_balances[0][address(this)] = _totalSupply[0];
|
||||
}
|
||||
|
||||
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
|
||||
return _balances[id][account];
|
||||
}
|
||||
|
||||
function totalSupply(uint256 id) public view virtual returns (uint256) {
|
||||
return _totalSupply[id];
|
||||
}
|
||||
|
||||
/// `DEALERC1155` STDCHEAT
|
||||
mapping(uint256 => mapping(address => uint256)) private _balances;
|
||||
mapping(uint256 => uint256) private _totalSupply;
|
||||
}
|
||||
|
||||
contract BarERC721 {
|
||||
constructor() payable {
|
||||
/// `DEALERC721` STDCHEAT
|
||||
_owners[1] = address(1);
|
||||
_balances[address(1)] = 1;
|
||||
_owners[2] = address(this);
|
||||
_owners[3] = address(this);
|
||||
_balances[address(this)] = 2;
|
||||
}
|
||||
|
||||
function balanceOf(address owner) public view virtual returns (uint256) {
|
||||
return _balances[owner];
|
||||
}
|
||||
|
||||
function ownerOf(uint256 tokenId) public view virtual returns (address) {
|
||||
address owner = _owners[tokenId];
|
||||
return owner;
|
||||
}
|
||||
|
||||
mapping(uint256 => address) private _owners;
|
||||
mapping(address => uint256) private _balances;
|
||||
}
|
||||
|
||||
interface USDCLike {
|
||||
function isBlacklisted(address) external view returns (bool);
|
||||
}
|
||||
|
||||
interface USDTLike {
|
||||
function isBlackListed(address) external view returns (bool);
|
||||
}
|
||||
|
||||
contract RevertingContract {
|
||||
constructor() {
|
||||
revert();
|
||||
}
|
||||
}
|
||||
|
||||
contract MockContractWithConstructorArgs {
|
||||
uint256 public immutable x;
|
||||
bool public y;
|
||||
bytes20 public z;
|
||||
|
||||
constructor(uint256 _x, bool _y, bytes20 _z) payable {
|
||||
x = _x;
|
||||
y = _y;
|
||||
z = _z;
|
||||
}
|
||||
}
|
||||
|
||||
contract MockContractPayable {
|
||||
receive() external payable {}
|
||||
}
|
||||
120
lib_forge_std/test/StdError.t.sol
Normal file
120
lib_forge_std/test/StdError.t.sol
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
import "../src/StdError.sol";
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdErrorsTest is Test {
|
||||
ErrorsTest test;
|
||||
|
||||
function setUp() public {
|
||||
test = new ErrorsTest();
|
||||
}
|
||||
|
||||
function test_ExpectAssertion() public {
|
||||
vm.expectRevert(stdError.assertionError);
|
||||
test.assertionError();
|
||||
}
|
||||
|
||||
function test_ExpectArithmetic() public {
|
||||
vm.expectRevert(stdError.arithmeticError);
|
||||
test.arithmeticError(10);
|
||||
}
|
||||
|
||||
function test_ExpectDiv() public {
|
||||
vm.expectRevert(stdError.divisionError);
|
||||
test.divError(0);
|
||||
}
|
||||
|
||||
function test_ExpectMod() public {
|
||||
vm.expectRevert(stdError.divisionError);
|
||||
test.modError(0);
|
||||
}
|
||||
|
||||
function test_ExpectEnum() public {
|
||||
vm.expectRevert(stdError.enumConversionError);
|
||||
test.enumConversion(1);
|
||||
}
|
||||
|
||||
function test_ExpectEncodeStg() public {
|
||||
vm.expectRevert(stdError.encodeStorageError);
|
||||
test.encodeStgError();
|
||||
}
|
||||
|
||||
function test_ExpectPop() public {
|
||||
vm.expectRevert(stdError.popError);
|
||||
test.pop();
|
||||
}
|
||||
|
||||
function test_ExpectOOB() public {
|
||||
vm.expectRevert(stdError.indexOOBError);
|
||||
test.indexOOBError(1);
|
||||
}
|
||||
|
||||
function test_ExpectMem() public {
|
||||
vm.expectRevert(stdError.memOverflowError);
|
||||
test.mem();
|
||||
}
|
||||
|
||||
function test_ExpectIntern() public {
|
||||
vm.expectRevert(stdError.zeroVarError);
|
||||
test.intern();
|
||||
}
|
||||
}
|
||||
|
||||
contract ErrorsTest {
|
||||
enum T {
|
||||
T1
|
||||
}
|
||||
|
||||
uint256[] public someArr;
|
||||
bytes someBytes;
|
||||
|
||||
function assertionError() public pure {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
function arithmeticError(uint256 a) public pure {
|
||||
a -= 100;
|
||||
}
|
||||
|
||||
function divError(uint256 a) public pure {
|
||||
100 / a;
|
||||
}
|
||||
|
||||
function modError(uint256 a) public pure {
|
||||
100 % a;
|
||||
}
|
||||
|
||||
function enumConversion(uint256 a) public pure {
|
||||
T(a);
|
||||
}
|
||||
|
||||
function encodeStgError() public {
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
sstore(someBytes.slot, 1)
|
||||
}
|
||||
keccak256(someBytes);
|
||||
}
|
||||
|
||||
function pop() public {
|
||||
someArr.pop();
|
||||
}
|
||||
|
||||
function indexOOBError(uint256 a) public pure {
|
||||
uint256[] memory t = new uint256[](0);
|
||||
t[a];
|
||||
}
|
||||
|
||||
function mem() public pure {
|
||||
uint256 l = 2 ** 256 / 32;
|
||||
new uint256[](l);
|
||||
}
|
||||
|
||||
function intern() public returns (uint256) {
|
||||
function(uint256) internal returns (uint256) x;
|
||||
x(2);
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
49
lib_forge_std/test/StdJson.t.sol
Normal file
49
lib_forge_std/test/StdJson.t.sol
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdJsonTest is Test {
|
||||
using stdJson for string;
|
||||
|
||||
string root;
|
||||
string path;
|
||||
|
||||
function setUp() public {
|
||||
root = vm.projectRoot();
|
||||
path = string.concat(root, "/test/fixtures/test.json");
|
||||
}
|
||||
|
||||
struct SimpleJson {
|
||||
uint256 a;
|
||||
string b;
|
||||
}
|
||||
|
||||
struct NestedJson {
|
||||
uint256 a;
|
||||
string b;
|
||||
SimpleJson c;
|
||||
}
|
||||
|
||||
function test_readJson() public view {
|
||||
string memory json = vm.readFile(path);
|
||||
assertEq(json.readUint(".a"), 123);
|
||||
}
|
||||
|
||||
function test_writeJson() public {
|
||||
string memory json = "json";
|
||||
json.serialize("a", uint256(123));
|
||||
string memory semiFinal = json.serialize("b", string("test"));
|
||||
string memory finalJson = json.serialize("c", semiFinal);
|
||||
finalJson.write(path);
|
||||
|
||||
string memory json_ = vm.readFile(path);
|
||||
bytes memory data = json_.parseRaw("$");
|
||||
NestedJson memory decodedData = abi.decode(data, (NestedJson));
|
||||
|
||||
assertEq(decodedData.a, 123);
|
||||
assertEq(decodedData.b, "test");
|
||||
assertEq(decodedData.c.a, 123);
|
||||
assertEq(decodedData.c.b, "test");
|
||||
}
|
||||
}
|
||||
212
lib_forge_std/test/StdMath.t.sol
Normal file
212
lib_forge_std/test/StdMath.t.sol
Normal file
@@ -0,0 +1,212 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
import "../src/StdMath.sol";
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdMathMock is Test {
|
||||
function exposed_percentDelta(uint256 a, uint256 b) public pure returns (uint256) {
|
||||
return stdMath.percentDelta(a, b);
|
||||
}
|
||||
|
||||
function exposed_percentDelta(int256 a, int256 b) public pure returns (uint256) {
|
||||
return stdMath.percentDelta(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
contract StdMathTest is Test {
|
||||
function test_GetAbs() external pure {
|
||||
assertEq(stdMath.abs(-50), 50);
|
||||
assertEq(stdMath.abs(50), 50);
|
||||
assertEq(stdMath.abs(-1337), 1337);
|
||||
assertEq(stdMath.abs(0), 0);
|
||||
|
||||
assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1);
|
||||
assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1));
|
||||
}
|
||||
|
||||
function testFuzz_GetAbs(int256 a) external pure {
|
||||
uint256 manualAbs = getAbs(a);
|
||||
|
||||
uint256 abs = stdMath.abs(a);
|
||||
|
||||
assertEq(abs, manualAbs);
|
||||
}
|
||||
|
||||
function test_GetDelta_Uint() external pure {
|
||||
assertEq(stdMath.delta(uint256(0), uint256(0)), 0);
|
||||
assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337);
|
||||
assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max);
|
||||
assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max);
|
||||
assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max);
|
||||
|
||||
assertEq(stdMath.delta(0, uint256(0)), 0);
|
||||
assertEq(stdMath.delta(1337, uint256(0)), 1337);
|
||||
assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max);
|
||||
assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max);
|
||||
assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max);
|
||||
|
||||
assertEq(stdMath.delta(1337, uint256(1337)), 0);
|
||||
assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0);
|
||||
assertEq(stdMath.delta(5000, uint256(1250)), 3750);
|
||||
}
|
||||
|
||||
function testFuzz_GetDelta_Uint(uint256 a, uint256 b) external pure {
|
||||
uint256 manualDelta;
|
||||
if (a > b) {
|
||||
manualDelta = a - b;
|
||||
} else {
|
||||
manualDelta = b - a;
|
||||
}
|
||||
|
||||
uint256 delta = stdMath.delta(a, b);
|
||||
|
||||
assertEq(delta, manualDelta);
|
||||
}
|
||||
|
||||
function test_GetDelta_Int() external pure {
|
||||
assertEq(stdMath.delta(int256(0), int256(0)), 0);
|
||||
assertEq(stdMath.delta(int256(0), int256(1337)), 1337);
|
||||
assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1);
|
||||
assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1);
|
||||
assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1);
|
||||
|
||||
assertEq(stdMath.delta(0, int256(0)), 0);
|
||||
assertEq(stdMath.delta(1337, int256(0)), 1337);
|
||||
assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1);
|
||||
assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1);
|
||||
assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1);
|
||||
|
||||
assertEq(stdMath.delta(-0, int256(0)), 0);
|
||||
assertEq(stdMath.delta(-1337, int256(0)), 1337);
|
||||
assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1);
|
||||
assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1);
|
||||
assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1);
|
||||
|
||||
assertEq(stdMath.delta(int256(0), -0), 0);
|
||||
assertEq(stdMath.delta(int256(0), -1337), 1337);
|
||||
assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1);
|
||||
assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1);
|
||||
assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1);
|
||||
|
||||
assertEq(stdMath.delta(1337, int256(1337)), 0);
|
||||
assertEq(stdMath.delta(type(int256).max, type(int256).max), 0);
|
||||
assertEq(stdMath.delta(type(int256).min, type(int256).min), 0);
|
||||
assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max);
|
||||
assertEq(stdMath.delta(5000, int256(1250)), 3750);
|
||||
}
|
||||
|
||||
function testFuzz_GetDelta_Int(int256 a, int256 b) external pure {
|
||||
uint256 absA = getAbs(a);
|
||||
uint256 absB = getAbs(b);
|
||||
uint256 absDelta = absA > absB ? absA - absB : absB - absA;
|
||||
|
||||
uint256 manualDelta;
|
||||
if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) {
|
||||
manualDelta = absDelta;
|
||||
}
|
||||
// (a < 0 && b >= 0) || (a >= 0 && b < 0)
|
||||
else {
|
||||
manualDelta = absA + absB;
|
||||
}
|
||||
|
||||
uint256 delta = stdMath.delta(a, b);
|
||||
|
||||
assertEq(delta, manualDelta);
|
||||
}
|
||||
|
||||
function test_GetPercentDelta_Uint() external {
|
||||
StdMathMock stdMathMock = new StdMathMock();
|
||||
|
||||
assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18);
|
||||
assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18);
|
||||
assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18);
|
||||
assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18);
|
||||
|
||||
assertEq(stdMath.percentDelta(1337, uint256(1337)), 0);
|
||||
assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0);
|
||||
assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18);
|
||||
assertEq(stdMath.percentDelta(2500, uint256(2500)), 0);
|
||||
assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18);
|
||||
assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18);
|
||||
|
||||
vm.expectRevert(stdError.divisionError);
|
||||
stdMathMock.exposed_percentDelta(uint256(1), 0);
|
||||
}
|
||||
|
||||
function testFuzz_GetPercentDelta_Uint(uint192 a, uint192 b) external pure {
|
||||
vm.assume(b != 0);
|
||||
uint256 manualDelta;
|
||||
if (a > b) {
|
||||
manualDelta = a - b;
|
||||
} else {
|
||||
manualDelta = b - a;
|
||||
}
|
||||
|
||||
uint256 manualPercentDelta = manualDelta * 1e18 / b;
|
||||
uint256 percentDelta = stdMath.percentDelta(a, b);
|
||||
|
||||
assertEq(percentDelta, manualPercentDelta);
|
||||
}
|
||||
|
||||
function test_GetPercentDelta_Int() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdMathMock stdMathMock = new StdMathMock();
|
||||
|
||||
assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18);
|
||||
assertEq(stdMath.percentDelta(int256(0), -1337), 1e18);
|
||||
assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18);
|
||||
assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18);
|
||||
assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18);
|
||||
assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18);
|
||||
assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18);
|
||||
assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18);
|
||||
|
||||
assertEq(stdMath.percentDelta(1337, int256(1337)), 0);
|
||||
assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0);
|
||||
assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0);
|
||||
|
||||
assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down
|
||||
assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down
|
||||
assertEq(stdMath.percentDelta(0, int256(2500)), 1e18);
|
||||
assertEq(stdMath.percentDelta(2500, int256(2500)), 0);
|
||||
assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18);
|
||||
assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18);
|
||||
|
||||
vm.expectRevert(stdError.divisionError);
|
||||
stdMathMock.exposed_percentDelta(int256(1), 0);
|
||||
}
|
||||
|
||||
function testFuzz_GetPercentDelta_Int(int192 a, int192 b) external pure {
|
||||
vm.assume(b != 0);
|
||||
uint256 absA = getAbs(a);
|
||||
uint256 absB = getAbs(b);
|
||||
uint256 absDelta = absA > absB ? absA - absB : absB - absA;
|
||||
|
||||
uint256 manualDelta;
|
||||
if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) {
|
||||
manualDelta = absDelta;
|
||||
}
|
||||
// (a < 0 && b >= 0) || (a >= 0 && b < 0)
|
||||
else {
|
||||
manualDelta = absA + absB;
|
||||
}
|
||||
|
||||
uint256 manualPercentDelta = manualDelta * 1e18 / absB;
|
||||
uint256 percentDelta = stdMath.percentDelta(a, b);
|
||||
|
||||
assertEq(percentDelta, manualPercentDelta);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
HELPERS
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function getAbs(int256 a) private pure returns (uint256) {
|
||||
if (a < 0) {
|
||||
return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a);
|
||||
}
|
||||
|
||||
return uint256(a);
|
||||
}
|
||||
}
|
||||
463
lib_forge_std/test/StdStorage.t.sol
Normal file
463
lib_forge_std/test/StdStorage.t.sol
Normal file
@@ -0,0 +1,463 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/StdStorage.sol";
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdStorageTest is Test {
|
||||
using stdStorage for StdStorage;
|
||||
|
||||
StorageTest internal test;
|
||||
|
||||
function setUp() public {
|
||||
test = new StorageTest();
|
||||
}
|
||||
|
||||
function test_StorageHidden() public {
|
||||
assertEq(uint256(keccak256("my.random.var")), stdstore.target(address(test)).sig("hidden()").find());
|
||||
}
|
||||
|
||||
function test_StorageObvious() public {
|
||||
assertEq(uint256(0), stdstore.target(address(test)).sig("exists()").find());
|
||||
}
|
||||
|
||||
function test_StorageExtraSload() public {
|
||||
assertEq(16, stdstore.target(address(test)).sig(test.extra_sload.selector).find());
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteHidden() public {
|
||||
stdstore.target(address(test)).sig(test.hidden.selector).checked_write(100);
|
||||
assertEq(uint256(test.hidden()), 100);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteObvious() public {
|
||||
stdstore.target(address(test)).sig(test.exists.selector).checked_write(100);
|
||||
assertEq(test.exists(), 100);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteSignedIntegerHidden() public {
|
||||
stdstore.target(address(test)).sig(test.hidden.selector).checked_write_int(-100);
|
||||
assertEq(int256(uint256(test.hidden())), -100);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteSignedIntegerObvious() public {
|
||||
stdstore.target(address(test)).sig(test.tG.selector).checked_write_int(-100);
|
||||
assertEq(test.tG(), -100);
|
||||
}
|
||||
|
||||
function test_StorageMapStructA() public {
|
||||
uint256 slot =
|
||||
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).find();
|
||||
assertEq(uint256(keccak256(abi.encode(address(this), 4))), slot);
|
||||
}
|
||||
|
||||
function test_StorageMapStructB() public {
|
||||
uint256 slot =
|
||||
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).find();
|
||||
assertEq(uint256(keccak256(abi.encode(address(this), 4))) + 1, slot);
|
||||
}
|
||||
|
||||
function test_StorageDeepMap() public {
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key(
|
||||
address(this)
|
||||
).find();
|
||||
assertEq(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(5)))))), slot);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteDeepMap() public {
|
||||
stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key(address(this))
|
||||
.checked_write(100);
|
||||
assertEq(100, test.deep_map(address(this), address(this)));
|
||||
}
|
||||
|
||||
function test_StorageDeepMapStructA() public {
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this))
|
||||
.with_key(address(this)).depth(0).find();
|
||||
assertEq(
|
||||
bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 0),
|
||||
bytes32(slot)
|
||||
);
|
||||
}
|
||||
|
||||
function test_StorageDeepMapStructB() public {
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this))
|
||||
.with_key(address(this)).depth(1).find();
|
||||
assertEq(
|
||||
bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 1),
|
||||
bytes32(slot)
|
||||
);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteDeepMapStructA() public {
|
||||
stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key(
|
||||
address(this)
|
||||
).depth(0).checked_write(100);
|
||||
(uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this));
|
||||
assertEq(100, a);
|
||||
assertEq(0, b);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteDeepMapStructB() public {
|
||||
stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key(
|
||||
address(this)
|
||||
).depth(1).checked_write(100);
|
||||
(uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this));
|
||||
assertEq(0, a);
|
||||
assertEq(100, b);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteMapStructA() public {
|
||||
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).checked_write(100);
|
||||
(uint256 a, uint256 b) = test.map_struct(address(this));
|
||||
assertEq(a, 100);
|
||||
assertEq(b, 0);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteMapStructB() public {
|
||||
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).checked_write(100);
|
||||
(uint256 a, uint256 b) = test.map_struct(address(this));
|
||||
assertEq(a, 0);
|
||||
assertEq(b, 100);
|
||||
}
|
||||
|
||||
function test_StorageStructA() public {
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(0).find();
|
||||
assertEq(uint256(7), slot);
|
||||
}
|
||||
|
||||
function test_StorageStructB() public {
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(1).find();
|
||||
assertEq(uint256(7) + 1, slot);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteStructA() public {
|
||||
stdstore.target(address(test)).sig(test.basic.selector).depth(0).checked_write(100);
|
||||
(uint256 a, uint256 b) = test.basic();
|
||||
assertEq(a, 100);
|
||||
assertEq(b, 1337);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteStructB() public {
|
||||
stdstore.target(address(test)).sig(test.basic.selector).depth(1).checked_write(100);
|
||||
(uint256 a, uint256 b) = test.basic();
|
||||
assertEq(a, 1337);
|
||||
assertEq(b, 100);
|
||||
}
|
||||
|
||||
function test_StorageMapAddrFound() public {
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).find();
|
||||
assertEq(uint256(keccak256(abi.encode(address(this), uint256(1)))), slot);
|
||||
}
|
||||
|
||||
function test_StorageMapAddrRoot() public {
|
||||
(uint256 slot, bytes32 key) =
|
||||
stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).parent();
|
||||
assertEq(address(uint160(uint256(key))), address(this));
|
||||
assertEq(uint256(1), slot);
|
||||
slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).root();
|
||||
assertEq(uint256(1), slot);
|
||||
}
|
||||
|
||||
function test_StorageMapUintFound() public {
|
||||
uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find();
|
||||
assertEq(uint256(keccak256(abi.encode(100, uint256(2)))), slot);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteMapUint() public {
|
||||
stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).checked_write(100);
|
||||
assertEq(100, test.map_uint(100));
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteMapAddr() public {
|
||||
stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).checked_write(100);
|
||||
assertEq(100, test.map_addr(address(this)));
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteMapBool() public {
|
||||
stdstore.target(address(test)).sig(test.map_bool.selector).with_key(address(this)).checked_write(true);
|
||||
assertTrue(test.map_bool(address(this)));
|
||||
}
|
||||
|
||||
function testFuzz_StorageCheckedWriteMapPacked(address addr, uint128 value) public {
|
||||
stdstore.enable_packed_slots().target(address(test)).sig(test.read_struct_lower.selector).with_key(addr)
|
||||
.checked_write(value);
|
||||
assertEq(test.read_struct_lower(addr), value);
|
||||
|
||||
stdstore.enable_packed_slots().target(address(test)).sig(test.read_struct_upper.selector).with_key(addr)
|
||||
.checked_write(value);
|
||||
assertEq(test.read_struct_upper(addr), value);
|
||||
}
|
||||
|
||||
function test_StorageCheckedWriteMapPackedFullSuccess() public {
|
||||
uint256 full = test.map_packed(address(1337));
|
||||
// keep upper 128, set lower 128 to 1337
|
||||
full = (full & (uint256((1 << 128) - 1) << 128)) | 1337;
|
||||
stdstore.target(address(test)).sig(test.map_packed.selector).with_key(address(uint160(1337))).checked_write(
|
||||
full
|
||||
);
|
||||
assertEq(1337, test.read_struct_lower(address(1337)));
|
||||
}
|
||||
|
||||
function testFail_StorageConst() public {
|
||||
// vm.expectRevert(abi.encodeWithSignature("NotStorage(bytes4)", bytes4(keccak256("const()"))));
|
||||
stdstore.target(address(test)).sig("const()").find();
|
||||
}
|
||||
|
||||
function testFuzz_StorageNativePack(uint248 val1, uint248 val2, bool boolVal1, bool boolVal2) public {
|
||||
stdstore.enable_packed_slots().target(address(test)).sig(test.tA.selector).checked_write(val1);
|
||||
stdstore.enable_packed_slots().target(address(test)).sig(test.tB.selector).checked_write(boolVal1);
|
||||
stdstore.enable_packed_slots().target(address(test)).sig(test.tC.selector).checked_write(boolVal2);
|
||||
stdstore.enable_packed_slots().target(address(test)).sig(test.tD.selector).checked_write(val2);
|
||||
|
||||
assertEq(test.tA(), val1);
|
||||
assertEq(test.tB(), boolVal1);
|
||||
assertEq(test.tC(), boolVal2);
|
||||
assertEq(test.tD(), val2);
|
||||
}
|
||||
|
||||
function test_StorageReadBytes32() public {
|
||||
bytes32 val = stdstore.target(address(test)).sig(test.tE.selector).read_bytes32();
|
||||
assertEq(val, hex"1337");
|
||||
}
|
||||
|
||||
function test_StorageReadBool_False() public {
|
||||
bool val = stdstore.target(address(test)).sig(test.tB.selector).read_bool();
|
||||
assertEq(val, false);
|
||||
}
|
||||
|
||||
function test_StorageReadBool_True() public {
|
||||
bool val = stdstore.target(address(test)).sig(test.tH.selector).read_bool();
|
||||
assertEq(val, true);
|
||||
}
|
||||
|
||||
function test_StorageReadBool_Revert() public {
|
||||
vm.expectRevert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool.");
|
||||
this.readNonBoolValue();
|
||||
}
|
||||
|
||||
function readNonBoolValue() public {
|
||||
stdstore.target(address(test)).sig(test.tE.selector).read_bool();
|
||||
}
|
||||
|
||||
function test_StorageReadAddress() public {
|
||||
address val = stdstore.target(address(test)).sig(test.tF.selector).read_address();
|
||||
assertEq(val, address(1337));
|
||||
}
|
||||
|
||||
function test_StorageReadUint() public {
|
||||
uint256 val = stdstore.target(address(test)).sig(test.exists.selector).read_uint();
|
||||
assertEq(val, 1);
|
||||
}
|
||||
|
||||
function test_StorageReadInt() public {
|
||||
int256 val = stdstore.target(address(test)).sig(test.tG.selector).read_int();
|
||||
assertEq(val, type(int256).min);
|
||||
}
|
||||
|
||||
function testFuzzPacked(uint256 val, uint8 elemToGet) public {
|
||||
// This function tries an assortment of packed slots, shifts meaning number of elements
|
||||
// that are packed. Shiftsizes are the size of each element, i.e. 8 means a data type that is 8 bits, 16 == 16 bits, etc.
|
||||
// Combined, these determine how a slot is packed. Making it random is too hard to avoid global rejection limit
|
||||
// and make it performant.
|
||||
|
||||
// change the number of shifts
|
||||
for (uint256 i = 1; i < 5; i++) {
|
||||
uint256 shifts = i;
|
||||
|
||||
elemToGet = uint8(bound(elemToGet, 0, shifts - 1));
|
||||
|
||||
uint256[] memory shiftSizes = new uint256[](shifts);
|
||||
for (uint256 j; j < shifts; j++) {
|
||||
shiftSizes[j] = 8 * (j + 1);
|
||||
}
|
||||
|
||||
test.setRandomPacking(val);
|
||||
|
||||
uint256 leftBits;
|
||||
uint256 rightBits;
|
||||
for (uint256 j; j < shiftSizes.length; j++) {
|
||||
if (j < elemToGet) {
|
||||
leftBits += shiftSizes[j];
|
||||
} else if (elemToGet != j) {
|
||||
rightBits += shiftSizes[j];
|
||||
}
|
||||
}
|
||||
|
||||
// we may have some right bits unaccounted for
|
||||
leftBits += 256 - (leftBits + shiftSizes[elemToGet] + rightBits);
|
||||
// clear left bits, then clear right bits and realign
|
||||
uint256 expectedValToRead = (val << leftBits) >> (leftBits + rightBits);
|
||||
|
||||
uint256 readVal = stdstore.target(address(test)).enable_packed_slots().sig(
|
||||
"getRandomPacked(uint8,uint8[],uint8)"
|
||||
).with_calldata(abi.encode(shifts, shiftSizes, elemToGet)).read_uint();
|
||||
|
||||
assertEq(readVal, expectedValToRead);
|
||||
}
|
||||
}
|
||||
|
||||
function testFuzzPacked2(uint256 nvars, uint256 seed) public {
|
||||
// Number of random variables to generate.
|
||||
nvars = bound(nvars, 1, 20);
|
||||
|
||||
// This will decrease as we generate values in the below loop.
|
||||
uint256 bitsRemaining = 256;
|
||||
|
||||
// Generate a random value and size for each variable.
|
||||
uint256[] memory vals = new uint256[](nvars);
|
||||
uint256[] memory sizes = new uint256[](nvars);
|
||||
uint256[] memory offsets = new uint256[](nvars);
|
||||
|
||||
for (uint256 i = 0; i < nvars; i++) {
|
||||
// Generate a random value and size.
|
||||
offsets[i] = i == 0 ? 0 : offsets[i - 1] + sizes[i - 1];
|
||||
|
||||
uint256 nvarsRemaining = nvars - i;
|
||||
uint256 maxVarSize = bitsRemaining - nvarsRemaining + 1;
|
||||
sizes[i] = bound(uint256(keccak256(abi.encodePacked(seed, i + 256))), 1, maxVarSize);
|
||||
bitsRemaining -= sizes[i];
|
||||
|
||||
uint256 maxVal;
|
||||
uint256 varSize = sizes[i];
|
||||
assembly {
|
||||
// mask = (1 << varSize) - 1
|
||||
maxVal := sub(shl(varSize, 1), 1)
|
||||
}
|
||||
vals[i] = bound(uint256(keccak256(abi.encodePacked(seed, i))), 0, maxVal);
|
||||
}
|
||||
|
||||
// Pack all values into the slot.
|
||||
for (uint256 i = 0; i < nvars; i++) {
|
||||
stdstore.enable_packed_slots().target(address(test)).sig("getRandomPacked(uint256,uint256)").with_key(
|
||||
sizes[i]
|
||||
).with_key(offsets[i]).checked_write(vals[i]);
|
||||
}
|
||||
|
||||
// Verify the read data matches.
|
||||
for (uint256 i = 0; i < nvars; i++) {
|
||||
uint256 readVal = stdstore.enable_packed_slots().target(address(test)).sig(
|
||||
"getRandomPacked(uint256,uint256)"
|
||||
).with_key(sizes[i]).with_key(offsets[i]).read_uint();
|
||||
|
||||
uint256 retVal = test.getRandomPacked(sizes[i], offsets[i]);
|
||||
|
||||
assertEq(readVal, vals[i]);
|
||||
assertEq(retVal, vals[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contract StorageTest {
|
||||
uint256 public exists = 1;
|
||||
mapping(address => uint256) public map_addr;
|
||||
mapping(uint256 => uint256) public map_uint;
|
||||
mapping(address => uint256) public map_packed;
|
||||
mapping(address => UnpackedStruct) public map_struct;
|
||||
mapping(address => mapping(address => uint256)) public deep_map;
|
||||
mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct;
|
||||
UnpackedStruct public basic;
|
||||
|
||||
uint248 public tA;
|
||||
bool public tB;
|
||||
|
||||
bool public tC = false;
|
||||
uint248 public tD = 1;
|
||||
|
||||
struct UnpackedStruct {
|
||||
uint256 a;
|
||||
uint256 b;
|
||||
}
|
||||
|
||||
mapping(address => bool) public map_bool;
|
||||
|
||||
bytes32 public tE = hex"1337";
|
||||
address public tF = address(1337);
|
||||
int256 public tG = type(int256).min;
|
||||
bool public tH = true;
|
||||
bytes32 private tI = ~bytes32(hex"1337");
|
||||
|
||||
uint256 randomPacking;
|
||||
|
||||
constructor() {
|
||||
basic = UnpackedStruct({a: 1337, b: 1337});
|
||||
|
||||
uint256 two = (1 << 128) | 1;
|
||||
map_packed[msg.sender] = two;
|
||||
map_packed[address(uint160(1337))] = 1 << 128;
|
||||
}
|
||||
|
||||
function read_struct_upper(address who) public view returns (uint256) {
|
||||
return map_packed[who] >> 128;
|
||||
}
|
||||
|
||||
function read_struct_lower(address who) public view returns (uint256) {
|
||||
return map_packed[who] & ((1 << 128) - 1);
|
||||
}
|
||||
|
||||
function hidden() public view returns (bytes32 t) {
|
||||
bytes32 slot = keccak256("my.random.var");
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
t := sload(slot)
|
||||
}
|
||||
}
|
||||
|
||||
function const() public pure returns (bytes32 t) {
|
||||
t = bytes32(hex"1337");
|
||||
}
|
||||
|
||||
function extra_sload() public view returns (bytes32 t) {
|
||||
// trigger read on slot `tE`, and make a staticcall to make sure compiler doesn't optimize this SLOAD away
|
||||
assembly {
|
||||
pop(staticcall(gas(), sload(tE.slot), 0, 0, 0, 0))
|
||||
}
|
||||
t = tI;
|
||||
}
|
||||
|
||||
function setRandomPacking(uint256 val) public {
|
||||
randomPacking = val;
|
||||
}
|
||||
|
||||
function _getMask(uint256 size) internal pure returns (uint256 mask) {
|
||||
assembly {
|
||||
// mask = (1 << size) - 1
|
||||
mask := sub(shl(size, 1), 1)
|
||||
}
|
||||
}
|
||||
|
||||
function setRandomPacking(uint256 val, uint256 size, uint256 offset) public {
|
||||
// Generate mask based on the size of the value
|
||||
uint256 mask = _getMask(size);
|
||||
// Zero out all bits for the word we're about to set
|
||||
uint256 cleanedWord = randomPacking & ~(mask << offset);
|
||||
// Place val in the correct spot of the cleaned word
|
||||
randomPacking = cleanedWord | val << offset;
|
||||
}
|
||||
|
||||
function getRandomPacked(uint256 size, uint256 offset) public view returns (uint256) {
|
||||
// Generate mask based on the size of the value
|
||||
uint256 mask = _getMask(size);
|
||||
// Shift to place the bits in the correct position, and use mask to zero out remaining bits
|
||||
return (randomPacking >> offset) & mask;
|
||||
}
|
||||
|
||||
function getRandomPacked(uint8 shifts, uint8[] memory shiftSizes, uint8 elem) public view returns (uint256) {
|
||||
require(elem < shifts, "!elem");
|
||||
uint256 leftBits;
|
||||
uint256 rightBits;
|
||||
|
||||
for (uint256 i; i < shiftSizes.length; i++) {
|
||||
if (i < elem) {
|
||||
leftBits += shiftSizes[i];
|
||||
} else if (elem != i) {
|
||||
rightBits += shiftSizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// we may have some right bits unaccounted for
|
||||
leftBits += 256 - (leftBits + shiftSizes[elem] + rightBits);
|
||||
|
||||
// clear left bits, then clear right bits and realign
|
||||
return (randomPacking << leftBits) >> (leftBits + rightBits);
|
||||
}
|
||||
}
|
||||
110
lib_forge_std/test/StdStyle.t.sol
Normal file
110
lib_forge_std/test/StdStyle.t.sol
Normal file
@@ -0,0 +1,110 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdStyleTest is Test {
|
||||
function test_StyleColor() public pure {
|
||||
console2.log(StdStyle.red("StdStyle.red String Test"));
|
||||
console2.log(StdStyle.red(uint256(10e18)));
|
||||
console2.log(StdStyle.red(int256(-10e18)));
|
||||
console2.log(StdStyle.red(true));
|
||||
console2.log(StdStyle.red(address(0)));
|
||||
console2.log(StdStyle.redBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.redBytes32("StdStyle.redBytes32"));
|
||||
console2.log(StdStyle.green("StdStyle.green String Test"));
|
||||
console2.log(StdStyle.green(uint256(10e18)));
|
||||
console2.log(StdStyle.green(int256(-10e18)));
|
||||
console2.log(StdStyle.green(true));
|
||||
console2.log(StdStyle.green(address(0)));
|
||||
console2.log(StdStyle.greenBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.greenBytes32("StdStyle.greenBytes32"));
|
||||
console2.log(StdStyle.yellow("StdStyle.yellow String Test"));
|
||||
console2.log(StdStyle.yellow(uint256(10e18)));
|
||||
console2.log(StdStyle.yellow(int256(-10e18)));
|
||||
console2.log(StdStyle.yellow(true));
|
||||
console2.log(StdStyle.yellow(address(0)));
|
||||
console2.log(StdStyle.yellowBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.yellowBytes32("StdStyle.yellowBytes32"));
|
||||
console2.log(StdStyle.blue("StdStyle.blue String Test"));
|
||||
console2.log(StdStyle.blue(uint256(10e18)));
|
||||
console2.log(StdStyle.blue(int256(-10e18)));
|
||||
console2.log(StdStyle.blue(true));
|
||||
console2.log(StdStyle.blue(address(0)));
|
||||
console2.log(StdStyle.blueBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.blueBytes32("StdStyle.blueBytes32"));
|
||||
console2.log(StdStyle.magenta("StdStyle.magenta String Test"));
|
||||
console2.log(StdStyle.magenta(uint256(10e18)));
|
||||
console2.log(StdStyle.magenta(int256(-10e18)));
|
||||
console2.log(StdStyle.magenta(true));
|
||||
console2.log(StdStyle.magenta(address(0)));
|
||||
console2.log(StdStyle.magentaBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.magentaBytes32("StdStyle.magentaBytes32"));
|
||||
console2.log(StdStyle.cyan("StdStyle.cyan String Test"));
|
||||
console2.log(StdStyle.cyan(uint256(10e18)));
|
||||
console2.log(StdStyle.cyan(int256(-10e18)));
|
||||
console2.log(StdStyle.cyan(true));
|
||||
console2.log(StdStyle.cyan(address(0)));
|
||||
console2.log(StdStyle.cyanBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.cyanBytes32("StdStyle.cyanBytes32"));
|
||||
}
|
||||
|
||||
function test_StyleFontWeight() public pure {
|
||||
console2.log(StdStyle.bold("StdStyle.bold String Test"));
|
||||
console2.log(StdStyle.bold(uint256(10e18)));
|
||||
console2.log(StdStyle.bold(int256(-10e18)));
|
||||
console2.log(StdStyle.bold(address(0)));
|
||||
console2.log(StdStyle.bold(true));
|
||||
console2.log(StdStyle.boldBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.boldBytes32("StdStyle.boldBytes32"));
|
||||
console2.log(StdStyle.dim("StdStyle.dim String Test"));
|
||||
console2.log(StdStyle.dim(uint256(10e18)));
|
||||
console2.log(StdStyle.dim(int256(-10e18)));
|
||||
console2.log(StdStyle.dim(address(0)));
|
||||
console2.log(StdStyle.dim(true));
|
||||
console2.log(StdStyle.dimBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.dimBytes32("StdStyle.dimBytes32"));
|
||||
console2.log(StdStyle.italic("StdStyle.italic String Test"));
|
||||
console2.log(StdStyle.italic(uint256(10e18)));
|
||||
console2.log(StdStyle.italic(int256(-10e18)));
|
||||
console2.log(StdStyle.italic(address(0)));
|
||||
console2.log(StdStyle.italic(true));
|
||||
console2.log(StdStyle.italicBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.italicBytes32("StdStyle.italicBytes32"));
|
||||
console2.log(StdStyle.underline("StdStyle.underline String Test"));
|
||||
console2.log(StdStyle.underline(uint256(10e18)));
|
||||
console2.log(StdStyle.underline(int256(-10e18)));
|
||||
console2.log(StdStyle.underline(address(0)));
|
||||
console2.log(StdStyle.underline(true));
|
||||
console2.log(StdStyle.underlineBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.underlineBytes32("StdStyle.underlineBytes32"));
|
||||
console2.log(StdStyle.inverse("StdStyle.inverse String Test"));
|
||||
console2.log(StdStyle.inverse(uint256(10e18)));
|
||||
console2.log(StdStyle.inverse(int256(-10e18)));
|
||||
console2.log(StdStyle.inverse(address(0)));
|
||||
console2.log(StdStyle.inverse(true));
|
||||
console2.log(StdStyle.inverseBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
|
||||
console2.log(StdStyle.inverseBytes32("StdStyle.inverseBytes32"));
|
||||
}
|
||||
|
||||
function test_StyleCombined() public pure {
|
||||
console2.log(StdStyle.red(StdStyle.bold("Red Bold String Test")));
|
||||
console2.log(StdStyle.green(StdStyle.dim(uint256(10e18))));
|
||||
console2.log(StdStyle.yellow(StdStyle.italic(int256(-10e18))));
|
||||
console2.log(StdStyle.blue(StdStyle.underline(address(0))));
|
||||
console2.log(StdStyle.magenta(StdStyle.inverse(true)));
|
||||
}
|
||||
|
||||
function test_StyleCustom() public pure {
|
||||
console2.log(h1("Custom Style 1"));
|
||||
console2.log(h2("Custom Style 2"));
|
||||
}
|
||||
|
||||
function h1(string memory a) private pure returns (string memory) {
|
||||
return StdStyle.cyan(StdStyle.inverse(StdStyle.bold(a)));
|
||||
}
|
||||
|
||||
function h2(string memory a) private pure returns (string memory) {
|
||||
return StdStyle.magenta(StdStyle.bold(StdStyle.underline(a)));
|
||||
}
|
||||
}
|
||||
49
lib_forge_std/test/StdToml.t.sol
Normal file
49
lib_forge_std/test/StdToml.t.sol
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdTomlTest is Test {
|
||||
using stdToml for string;
|
||||
|
||||
string root;
|
||||
string path;
|
||||
|
||||
function setUp() public {
|
||||
root = vm.projectRoot();
|
||||
path = string.concat(root, "/test/fixtures/test.toml");
|
||||
}
|
||||
|
||||
struct SimpleToml {
|
||||
uint256 a;
|
||||
string b;
|
||||
}
|
||||
|
||||
struct NestedToml {
|
||||
uint256 a;
|
||||
string b;
|
||||
SimpleToml c;
|
||||
}
|
||||
|
||||
function test_readToml() public view {
|
||||
string memory json = vm.readFile(path);
|
||||
assertEq(json.readUint(".a"), 123);
|
||||
}
|
||||
|
||||
function test_writeToml() public {
|
||||
string memory json = "json";
|
||||
json.serialize("a", uint256(123));
|
||||
string memory semiFinal = json.serialize("b", string("test"));
|
||||
string memory finalJson = json.serialize("c", semiFinal);
|
||||
finalJson.write(path);
|
||||
|
||||
string memory toml = vm.readFile(path);
|
||||
bytes memory data = toml.parseRaw("$");
|
||||
NestedToml memory decodedData = abi.decode(data, (NestedToml));
|
||||
|
||||
assertEq(decodedData.a, 123);
|
||||
assertEq(decodedData.b, "test");
|
||||
assertEq(decodedData.c.a, 123);
|
||||
assertEq(decodedData.c.b, "test");
|
||||
}
|
||||
}
|
||||
342
lib_forge_std/test/StdUtils.t.sol
Normal file
342
lib_forge_std/test/StdUtils.t.sol
Normal file
@@ -0,0 +1,342 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "../src/Test.sol";
|
||||
|
||||
contract StdUtilsMock is StdUtils {
|
||||
// We deploy a mock version so we can properly test expected reverts.
|
||||
function exposed_getTokenBalances(address token, address[] memory addresses)
|
||||
external
|
||||
returns (uint256[] memory balances)
|
||||
{
|
||||
return getTokenBalances(token, addresses);
|
||||
}
|
||||
|
||||
function exposed_bound(int256 num, int256 min, int256 max) external pure returns (int256) {
|
||||
return bound(num, min, max);
|
||||
}
|
||||
|
||||
function exposed_bound(uint256 num, uint256 min, uint256 max) external pure returns (uint256) {
|
||||
return bound(num, min, max);
|
||||
}
|
||||
|
||||
function exposed_bytesToUint(bytes memory b) external pure returns (uint256) {
|
||||
return bytesToUint(b);
|
||||
}
|
||||
}
|
||||
|
||||
contract StdUtilsTest is Test {
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
BOUND UINT
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function test_Bound() public pure {
|
||||
assertEq(bound(uint256(5), 0, 4), 0);
|
||||
assertEq(bound(uint256(0), 69, 69), 69);
|
||||
assertEq(bound(uint256(0), 68, 69), 68);
|
||||
assertEq(bound(uint256(10), 150, 190), 174);
|
||||
assertEq(bound(uint256(300), 2800, 3200), 3107);
|
||||
assertEq(bound(uint256(9999), 1337, 6666), 4669);
|
||||
}
|
||||
|
||||
function test_Bound_WithinRange() public pure {
|
||||
assertEq(bound(uint256(51), 50, 150), 51);
|
||||
assertEq(bound(uint256(51), 50, 150), bound(bound(uint256(51), 50, 150), 50, 150));
|
||||
assertEq(bound(uint256(149), 50, 150), 149);
|
||||
assertEq(bound(uint256(149), 50, 150), bound(bound(uint256(149), 50, 150), 50, 150));
|
||||
}
|
||||
|
||||
function test_Bound_EdgeCoverage() public pure {
|
||||
assertEq(bound(uint256(0), 50, 150), 50);
|
||||
assertEq(bound(uint256(1), 50, 150), 51);
|
||||
assertEq(bound(uint256(2), 50, 150), 52);
|
||||
assertEq(bound(uint256(3), 50, 150), 53);
|
||||
assertEq(bound(type(uint256).max, 50, 150), 150);
|
||||
assertEq(bound(type(uint256).max - 1, 50, 150), 149);
|
||||
assertEq(bound(type(uint256).max - 2, 50, 150), 148);
|
||||
assertEq(bound(type(uint256).max - 3, 50, 150), 147);
|
||||
}
|
||||
|
||||
function test_Bound_DistributionIsEven(uint256 min, uint256 size) public pure {
|
||||
size = size % 100 + 1;
|
||||
min = bound(min, UINT256_MAX / 2, UINT256_MAX / 2 + size);
|
||||
uint256 max = min + size - 1;
|
||||
uint256 result;
|
||||
|
||||
for (uint256 i = 1; i <= size * 4; ++i) {
|
||||
// x > max
|
||||
result = bound(max + i, min, max);
|
||||
assertEq(result, min + (i - 1) % size);
|
||||
// x < min
|
||||
result = bound(min - i, min, max);
|
||||
assertEq(result, max - (i - 1) % size);
|
||||
}
|
||||
}
|
||||
|
||||
function test_Bound(uint256 num, uint256 min, uint256 max) public pure {
|
||||
if (min > max) (min, max) = (max, min);
|
||||
|
||||
uint256 result = bound(num, min, max);
|
||||
|
||||
assertGe(result, min);
|
||||
assertLe(result, max);
|
||||
assertEq(result, bound(result, min, max));
|
||||
if (num >= min && num <= max) assertEq(result, num);
|
||||
}
|
||||
|
||||
function test_BoundUint256Max() public pure {
|
||||
assertEq(bound(0, type(uint256).max - 1, type(uint256).max), type(uint256).max - 1);
|
||||
assertEq(bound(1, type(uint256).max - 1, type(uint256).max), type(uint256).max);
|
||||
}
|
||||
|
||||
function test_CannotBoundMaxLessThanMin() public {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdUtilsMock stdUtils = new StdUtilsMock();
|
||||
|
||||
vm.expectRevert(bytes("StdUtils bound(uint256,uint256,uint256): Max is less than min."));
|
||||
stdUtils.exposed_bound(uint256(5), 100, 10);
|
||||
}
|
||||
|
||||
function test_CannotBoundMaxLessThanMin(uint256 num, uint256 min, uint256 max) public {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdUtilsMock stdUtils = new StdUtilsMock();
|
||||
|
||||
vm.assume(min > max);
|
||||
vm.expectRevert(bytes("StdUtils bound(uint256,uint256,uint256): Max is less than min."));
|
||||
stdUtils.exposed_bound(num, min, max);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
BOUND INT
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function test_BoundInt() public pure {
|
||||
assertEq(bound(-3, 0, 4), 2);
|
||||
assertEq(bound(0, -69, -69), -69);
|
||||
assertEq(bound(0, -69, -68), -68);
|
||||
assertEq(bound(-10, 150, 190), 154);
|
||||
assertEq(bound(-300, 2800, 3200), 2908);
|
||||
assertEq(bound(9999, -1337, 6666), 1995);
|
||||
}
|
||||
|
||||
function test_BoundInt_WithinRange() public pure {
|
||||
assertEq(bound(51, -50, 150), 51);
|
||||
assertEq(bound(51, -50, 150), bound(bound(51, -50, 150), -50, 150));
|
||||
assertEq(bound(149, -50, 150), 149);
|
||||
assertEq(bound(149, -50, 150), bound(bound(149, -50, 150), -50, 150));
|
||||
}
|
||||
|
||||
function test_BoundInt_EdgeCoverage() public pure {
|
||||
assertEq(bound(type(int256).min, -50, 150), -50);
|
||||
assertEq(bound(type(int256).min + 1, -50, 150), -49);
|
||||
assertEq(bound(type(int256).min + 2, -50, 150), -48);
|
||||
assertEq(bound(type(int256).min + 3, -50, 150), -47);
|
||||
assertEq(bound(type(int256).min, 10, 150), 10);
|
||||
assertEq(bound(type(int256).min + 1, 10, 150), 11);
|
||||
assertEq(bound(type(int256).min + 2, 10, 150), 12);
|
||||
assertEq(bound(type(int256).min + 3, 10, 150), 13);
|
||||
|
||||
assertEq(bound(type(int256).max, -50, 150), 150);
|
||||
assertEq(bound(type(int256).max - 1, -50, 150), 149);
|
||||
assertEq(bound(type(int256).max - 2, -50, 150), 148);
|
||||
assertEq(bound(type(int256).max - 3, -50, 150), 147);
|
||||
assertEq(bound(type(int256).max, -50, -10), -10);
|
||||
assertEq(bound(type(int256).max - 1, -50, -10), -11);
|
||||
assertEq(bound(type(int256).max - 2, -50, -10), -12);
|
||||
assertEq(bound(type(int256).max - 3, -50, -10), -13);
|
||||
}
|
||||
|
||||
function test_BoundInt_DistributionIsEven(int256 min, uint256 size) public pure {
|
||||
size = size % 100 + 1;
|
||||
min = bound(min, -int256(size / 2), int256(size - size / 2));
|
||||
int256 max = min + int256(size) - 1;
|
||||
int256 result;
|
||||
|
||||
for (uint256 i = 1; i <= size * 4; ++i) {
|
||||
// x > max
|
||||
result = bound(max + int256(i), min, max);
|
||||
assertEq(result, min + int256((i - 1) % size));
|
||||
// x < min
|
||||
result = bound(min - int256(i), min, max);
|
||||
assertEq(result, max - int256((i - 1) % size));
|
||||
}
|
||||
}
|
||||
|
||||
function test_BoundInt(int256 num, int256 min, int256 max) public pure {
|
||||
if (min > max) (min, max) = (max, min);
|
||||
|
||||
int256 result = bound(num, min, max);
|
||||
|
||||
assertGe(result, min);
|
||||
assertLe(result, max);
|
||||
assertEq(result, bound(result, min, max));
|
||||
if (num >= min && num <= max) assertEq(result, num);
|
||||
}
|
||||
|
||||
function test_BoundIntInt256Max() public pure {
|
||||
assertEq(bound(0, type(int256).max - 1, type(int256).max), type(int256).max - 1);
|
||||
assertEq(bound(1, type(int256).max - 1, type(int256).max), type(int256).max);
|
||||
}
|
||||
|
||||
function test_BoundIntInt256Min() public pure {
|
||||
assertEq(bound(0, type(int256).min, type(int256).min + 1), type(int256).min);
|
||||
assertEq(bound(1, type(int256).min, type(int256).min + 1), type(int256).min + 1);
|
||||
}
|
||||
|
||||
function test_CannotBoundIntMaxLessThanMin() public {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdUtilsMock stdUtils = new StdUtilsMock();
|
||||
|
||||
vm.expectRevert(bytes("StdUtils bound(int256,int256,int256): Max is less than min."));
|
||||
stdUtils.exposed_bound(-5, 100, 10);
|
||||
}
|
||||
|
||||
function test_CannotBoundIntMaxLessThanMin(int256 num, int256 min, int256 max) public {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdUtilsMock stdUtils = new StdUtilsMock();
|
||||
|
||||
vm.assume(min > max);
|
||||
vm.expectRevert(bytes("StdUtils bound(int256,int256,int256): Max is less than min."));
|
||||
stdUtils.exposed_bound(num, min, max);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
BOUND PRIVATE KEY
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function test_BoundPrivateKey() public pure {
|
||||
assertEq(boundPrivateKey(0), 1);
|
||||
assertEq(boundPrivateKey(1), 1);
|
||||
assertEq(boundPrivateKey(300), 300);
|
||||
assertEq(boundPrivateKey(9999), 9999);
|
||||
assertEq(boundPrivateKey(SECP256K1_ORDER - 1), SECP256K1_ORDER - 1);
|
||||
assertEq(boundPrivateKey(SECP256K1_ORDER), 1);
|
||||
assertEq(boundPrivateKey(SECP256K1_ORDER + 1), 2);
|
||||
assertEq(boundPrivateKey(UINT256_MAX), UINT256_MAX & SECP256K1_ORDER - 1); // x&y is equivalent to x-x%y
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
BYTES TO UINT
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function test_BytesToUint() external pure {
|
||||
bytes memory maxUint = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
|
||||
bytes memory two = hex"02";
|
||||
bytes memory millionEther = hex"d3c21bcecceda1000000";
|
||||
|
||||
assertEq(bytesToUint(maxUint), type(uint256).max);
|
||||
assertEq(bytesToUint(two), 2);
|
||||
assertEq(bytesToUint(millionEther), 1_000_000 ether);
|
||||
}
|
||||
|
||||
function test_CannotConvertGT32Bytes() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdUtilsMock stdUtils = new StdUtilsMock();
|
||||
|
||||
bytes memory thirty3Bytes = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
|
||||
vm.expectRevert("StdUtils bytesToUint(bytes): Bytes length exceeds 32.");
|
||||
stdUtils.exposed_bytesToUint(thirty3Bytes);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
COMPUTE CREATE ADDRESS
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function test_ComputeCreateAddress() external pure {
|
||||
address deployer = 0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9;
|
||||
uint256 nonce = 14;
|
||||
address createAddress = computeCreateAddress(deployer, nonce);
|
||||
assertEq(createAddress, 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
COMPUTE CREATE2 ADDRESS
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
function test_ComputeCreate2Address() external pure {
|
||||
bytes32 salt = bytes32(uint256(31415));
|
||||
bytes32 initcodeHash = keccak256(abi.encode(0x6080));
|
||||
address deployer = 0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9;
|
||||
address create2Address = computeCreate2Address(salt, initcodeHash, deployer);
|
||||
assertEq(create2Address, 0xB147a5d25748fda14b463EB04B111027C290f4d3);
|
||||
}
|
||||
|
||||
function test_ComputeCreate2AddressWithDefaultDeployer() external pure {
|
||||
bytes32 salt = 0xc290c670fde54e5ef686f9132cbc8711e76a98f0333a438a92daa442c71403c0;
|
||||
bytes32 initcodeHash = hashInitCode(hex"6080", "");
|
||||
assertEq(initcodeHash, 0x1a578b7a4b0b5755db6d121b4118d4bc68fe170dca840c59bc922f14175a76b0);
|
||||
address create2Address = computeCreate2Address(salt, initcodeHash);
|
||||
assertEq(create2Address, 0xc0ffEe2198a06235aAbFffe5Db0CacF1717f5Ac6);
|
||||
}
|
||||
}
|
||||
|
||||
contract StdUtilsForkTest is Test {
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
GET TOKEN BALANCES
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
address internal SHIB = 0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE;
|
||||
address internal SHIB_HOLDER_0 = 0x855F5981e831D83e6A4b4EBFCAdAa68D92333170;
|
||||
address internal SHIB_HOLDER_1 = 0x8F509A90c2e47779cA408Fe00d7A72e359229AdA;
|
||||
address internal SHIB_HOLDER_2 = 0x0e3bbc0D04fF62211F71f3e4C45d82ad76224385;
|
||||
|
||||
address internal USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
|
||||
address internal USDC_HOLDER_0 = 0xDa9CE944a37d218c3302F6B82a094844C6ECEb17;
|
||||
address internal USDC_HOLDER_1 = 0x3e67F4721E6d1c41a015f645eFa37BEd854fcf52;
|
||||
|
||||
function setUp() public {
|
||||
// All tests of the `getTokenBalances` method are fork tests using live contracts.
|
||||
vm.createSelectFork({urlOrAlias: "mainnet", blockNumber: 16_428_900});
|
||||
}
|
||||
|
||||
function test_CannotGetTokenBalances_NonTokenContract() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdUtilsMock stdUtils = new StdUtilsMock();
|
||||
|
||||
// The UniswapV2Factory contract has neither a `balanceOf` function nor a fallback function,
|
||||
// so the `balanceOf` call should revert.
|
||||
address token = address(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
|
||||
address[] memory addresses = new address[](1);
|
||||
addresses[0] = USDC_HOLDER_0;
|
||||
|
||||
vm.expectRevert("Multicall3: call failed");
|
||||
stdUtils.exposed_getTokenBalances(token, addresses);
|
||||
}
|
||||
|
||||
function test_CannotGetTokenBalances_EOA() external {
|
||||
// We deploy a mock version so we can properly test the revert.
|
||||
StdUtilsMock stdUtils = new StdUtilsMock();
|
||||
|
||||
address eoa = vm.addr({privateKey: 1});
|
||||
address[] memory addresses = new address[](1);
|
||||
addresses[0] = USDC_HOLDER_0;
|
||||
vm.expectRevert("StdUtils getTokenBalances(address,address[]): Token address is not a contract.");
|
||||
stdUtils.exposed_getTokenBalances(eoa, addresses);
|
||||
}
|
||||
|
||||
function test_GetTokenBalances_Empty() external {
|
||||
address[] memory addresses = new address[](0);
|
||||
uint256[] memory balances = getTokenBalances(USDC, addresses);
|
||||
assertEq(balances.length, 0);
|
||||
}
|
||||
|
||||
function test_GetTokenBalances_USDC() external {
|
||||
address[] memory addresses = new address[](2);
|
||||
addresses[0] = USDC_HOLDER_0;
|
||||
addresses[1] = USDC_HOLDER_1;
|
||||
uint256[] memory balances = getTokenBalances(USDC, addresses);
|
||||
assertEq(balances[0], 159_000_000_000_000);
|
||||
assertEq(balances[1], 131_350_000_000_000);
|
||||
}
|
||||
|
||||
function test_GetTokenBalances_SHIB() external {
|
||||
address[] memory addresses = new address[](3);
|
||||
addresses[0] = SHIB_HOLDER_0;
|
||||
addresses[1] = SHIB_HOLDER_1;
|
||||
addresses[2] = SHIB_HOLDER_2;
|
||||
uint256[] memory balances = getTokenBalances(SHIB, addresses);
|
||||
assertEq(balances[0], 3_323_256_285_484.42e18);
|
||||
assertEq(balances[1], 1_271_702_771_149.99999928e18);
|
||||
assertEq(balances[2], 606_357_106_247e18);
|
||||
}
|
||||
}
|
||||
15
lib_forge_std/test/Vm.t.sol
Normal file
15
lib_forge_std/test/Vm.t.sol
Normal file
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
import {Test} from "../src/Test.sol";
|
||||
import {Vm, VmSafe} from "../src/Vm.sol";
|
||||
|
||||
contract VmTest is Test {
|
||||
// This test ensures that functions are never accidentally removed from a Vm interface, or
|
||||
// inadvertently moved between Vm and VmSafe. This test must be updated each time a function is
|
||||
// added to or removed from Vm or VmSafe.
|
||||
function test_interfaceId() public pure {
|
||||
assertEq(type(VmSafe).interfaceId, bytes4(0xdf5d274d), "VmSafe");
|
||||
assertEq(type(Vm).interfaceId, bytes4(0xb91a22ba), "Vm");
|
||||
}
|
||||
}
|
||||
10
lib_forge_std/test/compilation/CompilationScript.sol
Normal file
10
lib_forge_std/test/compilation/CompilationScript.sol
Normal file
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../../src/Script.sol";
|
||||
|
||||
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
|
||||
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
|
||||
contract CompilationScript is Script {}
|
||||
10
lib_forge_std/test/compilation/CompilationScriptBase.sol
Normal file
10
lib_forge_std/test/compilation/CompilationScriptBase.sol
Normal file
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../../src/Script.sol";
|
||||
|
||||
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
|
||||
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
|
||||
contract CompilationScriptBase is ScriptBase {}
|
||||
10
lib_forge_std/test/compilation/CompilationTest.sol
Normal file
10
lib_forge_std/test/compilation/CompilationTest.sol
Normal file
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../../src/Test.sol";
|
||||
|
||||
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
|
||||
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
|
||||
contract CompilationTest is Test {}
|
||||
10
lib_forge_std/test/compilation/CompilationTestBase.sol
Normal file
10
lib_forge_std/test/compilation/CompilationTestBase.sol
Normal file
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.6.2 <0.9.0;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../../src/Test.sol";
|
||||
|
||||
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
|
||||
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
|
||||
contract CompilationTestBase is TestBase {}
|
||||
187
lib_forge_std/test/fixtures/broadcast.log.json
vendored
Normal file
187
lib_forge_std/test/fixtures/broadcast.log.json
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
{
|
||||
"transactions": [
|
||||
{
|
||||
"hash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f",
|
||||
"type": "CALL",
|
||||
"contractName": "Test",
|
||||
"contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
|
||||
"function": "multiple_arguments(uint256,address,uint256[]):(uint256)",
|
||||
"arguments": ["1", "0000000000000000000000000000000000001337", "[3,4]"],
|
||||
"tx": {
|
||||
"type": "0x02",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
|
||||
"gas": "0x73b9",
|
||||
"value": "0x0",
|
||||
"data": "0x23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004",
|
||||
"nonce": "0x3",
|
||||
"accessList": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298",
|
||||
"type": "CALL",
|
||||
"contractName": "Test",
|
||||
"contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
|
||||
"function": "inc():(uint256)",
|
||||
"arguments": [],
|
||||
"tx": {
|
||||
"type": "0x02",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
|
||||
"gas": "0xdcb2",
|
||||
"value": "0x0",
|
||||
"data": "0x371303c0",
|
||||
"nonce": "0x4",
|
||||
"accessList": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c",
|
||||
"type": "CALL",
|
||||
"contractName": "Test",
|
||||
"contractAddress": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
|
||||
"function": "t(uint256):(uint256)",
|
||||
"arguments": ["1"],
|
||||
"tx": {
|
||||
"type": "0x02",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
|
||||
"gas": "0x8599",
|
||||
"value": "0x0",
|
||||
"data": "0xafe29f710000000000000000000000000000000000000000000000000000000000000001",
|
||||
"nonce": "0x5",
|
||||
"accessList": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"receipts": [
|
||||
{
|
||||
"transactionHash": "0x481dc86e40bba90403c76f8e144aa9ff04c1da2164299d0298573835f0991181",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0xef0730448490304e5403be0fa8f8ce64f118e9adcca60c07a2ae1ab921d748af",
|
||||
"blockNumber": "0x1",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": null,
|
||||
"cumulativeGasUsed": "0x13f3a",
|
||||
"gasUsed": "0x13f3a",
|
||||
"contractAddress": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
|
||||
"logs": [],
|
||||
"status": "0x1",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"effectiveGasPrice": "0xee6b2800"
|
||||
},
|
||||
{
|
||||
"transactionHash": "0x6a187183545b8a9e7f1790e847139379bf5622baff2cb43acf3f5c79470af782",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0xf3acb96a90071640c2a8c067ae4e16aad87e634ea8d8bbbb5b352fba86ba0148",
|
||||
"blockNumber": "0x2",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": null,
|
||||
"cumulativeGasUsed": "0x45d80",
|
||||
"gasUsed": "0x45d80",
|
||||
"contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
|
||||
"logs": [],
|
||||
"status": "0x1",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"effectiveGasPrice": "0xee6b2800"
|
||||
},
|
||||
{
|
||||
"transactionHash": "0x064ad173b4867bdef2fb60060bbdaf01735fbf10414541ea857772974e74ea9d",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0x8373d02109d3ee06a0225f23da4c161c656ccc48fe0fcee931d325508ae73e58",
|
||||
"blockNumber": "0x3",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0x4e59b44847b379578588920ca78fbf26c0b4956c",
|
||||
"cumulativeGasUsed": "0x45feb",
|
||||
"gasUsed": "0x45feb",
|
||||
"contractAddress": null,
|
||||
"logs": [],
|
||||
"status": "0x1",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"effectiveGasPrice": "0xee6b2800"
|
||||
},
|
||||
{
|
||||
"transactionHash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0x16712fae5c0e18f75045f84363fb6b4d9a9fe25e660c4ce286833a533c97f629",
|
||||
"blockNumber": "0x4",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
|
||||
"cumulativeGasUsed": "0x5905",
|
||||
"gasUsed": "0x5905",
|
||||
"contractAddress": null,
|
||||
"logs": [],
|
||||
"status": "0x1",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"effectiveGasPrice": "0xee6b2800"
|
||||
},
|
||||
{
|
||||
"transactionHash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0x156b88c3eb9a1244ba00a1834f3f70de735b39e3e59006dd03af4fe7d5480c11",
|
||||
"blockNumber": "0x5",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
|
||||
"cumulativeGasUsed": "0xa9c4",
|
||||
"gasUsed": "0xa9c4",
|
||||
"contractAddress": null,
|
||||
"logs": [],
|
||||
"status": "0x1",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"effectiveGasPrice": "0xee6b2800"
|
||||
},
|
||||
{
|
||||
"transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb",
|
||||
"blockNumber": "0x6",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
|
||||
"cumulativeGasUsed": "0x66c5",
|
||||
"gasUsed": "0x66c5",
|
||||
"contractAddress": null,
|
||||
"logs": [
|
||||
{
|
||||
"address": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
|
||||
"topics": [
|
||||
"0x0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b"
|
||||
],
|
||||
"data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000",
|
||||
"blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb",
|
||||
"blockNumber": "0x6",
|
||||
"transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c",
|
||||
"transactionIndex": "0x1",
|
||||
"logIndex": "0x0",
|
||||
"transactionLogIndex": "0x0",
|
||||
"removed": false
|
||||
}
|
||||
],
|
||||
"status": "0x1",
|
||||
"logsBloom": "0x00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100",
|
||||
"effectiveGasPrice": "0xee6b2800"
|
||||
},
|
||||
{
|
||||
"transactionHash": "0x11fbb10230c168ca1e36a7e5c69a6dbcd04fd9e64ede39d10a83e36ee8065c16",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0xf1e0ed2eda4e923626ec74621006ed50b3fc27580dc7b4cf68a07ca77420e29c",
|
||||
"blockNumber": "0x7",
|
||||
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
||||
"to": "0x0000000000000000000000000000000000001337",
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"gasUsed": "0x5208",
|
||||
"contractAddress": null,
|
||||
"logs": [],
|
||||
"status": "0x1",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"effectiveGasPrice": "0xee6b2800"
|
||||
}
|
||||
],
|
||||
"libraries": [
|
||||
"src/Broadcast.t.sol:F:0x5fbdb2315678afecb367f032d93f642f64180aa3"
|
||||
],
|
||||
"pending": [],
|
||||
"path": "broadcast/Broadcast.t.sol/31337/run-latest.json",
|
||||
"returns": {},
|
||||
"timestamp": 1655140035
|
||||
}
|
||||
8
lib_forge_std/test/fixtures/test.json
vendored
Normal file
8
lib_forge_std/test/fixtures/test.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"a": 123,
|
||||
"b": "test",
|
||||
"c": {
|
||||
"a": 123,
|
||||
"b": "test"
|
||||
}
|
||||
}
|
||||
6
lib_forge_std/test/fixtures/test.toml
vendored
Normal file
6
lib_forge_std/test/fixtures/test.toml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
a = 123
|
||||
b = "test"
|
||||
|
||||
[c]
|
||||
a = 123
|
||||
b = "test"
|
||||
441
lib_forge_std/test/mocks/MockERC20.t.sol
Normal file
441
lib_forge_std/test/mocks/MockERC20.t.sol
Normal file
@@ -0,0 +1,441 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import {MockERC20} from "../../src/mocks/MockERC20.sol";
|
||||
import {StdCheats} from "../../src/StdCheats.sol";
|
||||
import {Test} from "../../src/Test.sol";
|
||||
|
||||
contract Token_ERC20 is MockERC20 {
|
||||
constructor(string memory name, string memory symbol, uint8 decimals) {
|
||||
initialize(name, symbol, decimals);
|
||||
}
|
||||
|
||||
function mint(address to, uint256 value) public virtual {
|
||||
_mint(to, value);
|
||||
}
|
||||
|
||||
function burn(address from, uint256 value) public virtual {
|
||||
_burn(from, value);
|
||||
}
|
||||
}
|
||||
|
||||
contract MockERC20Test is StdCheats, Test {
|
||||
Token_ERC20 token;
|
||||
|
||||
bytes32 constant PERMIT_TYPEHASH =
|
||||
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
|
||||
|
||||
function setUp() public {
|
||||
token = new Token_ERC20("Token", "TKN", 18);
|
||||
}
|
||||
|
||||
function invariantMetadata() public view {
|
||||
assertEq(token.name(), "Token");
|
||||
assertEq(token.symbol(), "TKN");
|
||||
assertEq(token.decimals(), 18);
|
||||
}
|
||||
|
||||
function testMint() public {
|
||||
token.mint(address(0xBEEF), 1e18);
|
||||
|
||||
assertEq(token.totalSupply(), 1e18);
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1e18);
|
||||
}
|
||||
|
||||
function testBurn() public {
|
||||
token.mint(address(0xBEEF), 1e18);
|
||||
token.burn(address(0xBEEF), 0.9e18);
|
||||
|
||||
assertEq(token.totalSupply(), 1e18 - 0.9e18);
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 0.1e18);
|
||||
}
|
||||
|
||||
function testApprove() public {
|
||||
assertTrue(token.approve(address(0xBEEF), 1e18));
|
||||
|
||||
assertEq(token.allowance(address(this), address(0xBEEF)), 1e18);
|
||||
}
|
||||
|
||||
function testTransfer() public {
|
||||
token.mint(address(this), 1e18);
|
||||
|
||||
assertTrue(token.transfer(address(0xBEEF), 1e18));
|
||||
assertEq(token.totalSupply(), 1e18);
|
||||
|
||||
assertEq(token.balanceOf(address(this)), 0);
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1e18);
|
||||
}
|
||||
|
||||
function testTransferFrom() public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, 1e18);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), 1e18);
|
||||
|
||||
assertTrue(token.transferFrom(from, address(0xBEEF), 1e18));
|
||||
assertEq(token.totalSupply(), 1e18);
|
||||
|
||||
assertEq(token.allowance(from, address(this)), 0);
|
||||
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1e18);
|
||||
}
|
||||
|
||||
function testInfiniteApproveTransferFrom() public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, 1e18);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), type(uint256).max);
|
||||
|
||||
assertTrue(token.transferFrom(from, address(0xBEEF), 1e18));
|
||||
assertEq(token.totalSupply(), 1e18);
|
||||
|
||||
assertEq(token.allowance(from, address(this)), type(uint256).max);
|
||||
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1e18);
|
||||
}
|
||||
|
||||
function testPermit() public {
|
||||
uint256 privateKey = 0xBEEF;
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s);
|
||||
|
||||
assertEq(token.allowance(owner, address(0xCAFE)), 1e18);
|
||||
assertEq(token.nonces(owner), 1);
|
||||
}
|
||||
|
||||
function testFailTransferInsufficientBalance() public {
|
||||
token.mint(address(this), 0.9e18);
|
||||
token.transfer(address(0xBEEF), 1e18);
|
||||
}
|
||||
|
||||
function testFailTransferFromInsufficientAllowance() public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, 1e18);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), 0.9e18);
|
||||
|
||||
token.transferFrom(from, address(0xBEEF), 1e18);
|
||||
}
|
||||
|
||||
function testFailTransferFromInsufficientBalance() public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, 0.9e18);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), 1e18);
|
||||
|
||||
token.transferFrom(from, address(0xBEEF), 1e18);
|
||||
}
|
||||
|
||||
function testFailPermitBadNonce() public {
|
||||
uint256 privateKey = 0xBEEF;
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 1, block.timestamp))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s);
|
||||
}
|
||||
|
||||
function testFailPermitBadDeadline() public {
|
||||
uint256 privateKey = 0xBEEF;
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, address(0xCAFE), 1e18, block.timestamp + 1, v, r, s);
|
||||
}
|
||||
|
||||
function testFailPermitPastDeadline() public {
|
||||
uint256 oldTimestamp = block.timestamp;
|
||||
uint256 privateKey = 0xBEEF;
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, oldTimestamp))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
vm.warp(block.timestamp + 1);
|
||||
token.permit(owner, address(0xCAFE), 1e18, oldTimestamp, v, r, s);
|
||||
}
|
||||
|
||||
function testFailPermitReplay() public {
|
||||
uint256 privateKey = 0xBEEF;
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s);
|
||||
token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s);
|
||||
}
|
||||
|
||||
function testMetadata(string calldata name, string calldata symbol, uint8 decimals) public {
|
||||
Token_ERC20 tkn = new Token_ERC20(name, symbol, decimals);
|
||||
assertEq(tkn.name(), name);
|
||||
assertEq(tkn.symbol(), symbol);
|
||||
assertEq(tkn.decimals(), decimals);
|
||||
}
|
||||
|
||||
function testMint(address from, uint256 amount) public {
|
||||
token.mint(from, amount);
|
||||
|
||||
assertEq(token.totalSupply(), amount);
|
||||
assertEq(token.balanceOf(from), amount);
|
||||
}
|
||||
|
||||
function testBurn(address from, uint256 mintAmount, uint256 burnAmount) public {
|
||||
burnAmount = bound(burnAmount, 0, mintAmount);
|
||||
|
||||
token.mint(from, mintAmount);
|
||||
token.burn(from, burnAmount);
|
||||
|
||||
assertEq(token.totalSupply(), mintAmount - burnAmount);
|
||||
assertEq(token.balanceOf(from), mintAmount - burnAmount);
|
||||
}
|
||||
|
||||
function testApprove(address to, uint256 amount) public {
|
||||
assertTrue(token.approve(to, amount));
|
||||
|
||||
assertEq(token.allowance(address(this), to), amount);
|
||||
}
|
||||
|
||||
function testTransfer(address from, uint256 amount) public {
|
||||
token.mint(address(this), amount);
|
||||
|
||||
assertTrue(token.transfer(from, amount));
|
||||
assertEq(token.totalSupply(), amount);
|
||||
|
||||
if (address(this) == from) {
|
||||
assertEq(token.balanceOf(address(this)), amount);
|
||||
} else {
|
||||
assertEq(token.balanceOf(address(this)), 0);
|
||||
assertEq(token.balanceOf(from), amount);
|
||||
}
|
||||
}
|
||||
|
||||
function testTransferFrom(address to, uint256 approval, uint256 amount) public {
|
||||
amount = bound(amount, 0, approval);
|
||||
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, amount);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), approval);
|
||||
|
||||
assertTrue(token.transferFrom(from, to, amount));
|
||||
assertEq(token.totalSupply(), amount);
|
||||
|
||||
uint256 app = from == address(this) || approval == type(uint256).max ? approval : approval - amount;
|
||||
assertEq(token.allowance(from, address(this)), app);
|
||||
|
||||
if (from == to) {
|
||||
assertEq(token.balanceOf(from), amount);
|
||||
} else {
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
assertEq(token.balanceOf(to), amount);
|
||||
}
|
||||
}
|
||||
|
||||
function testPermit(uint248 privKey, address to, uint256 amount, uint256 deadline) public {
|
||||
uint256 privateKey = privKey;
|
||||
if (deadline < block.timestamp) deadline = block.timestamp;
|
||||
if (privateKey == 0) privateKey = 1;
|
||||
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, to, amount, deadline, v, r, s);
|
||||
|
||||
assertEq(token.allowance(owner, to), amount);
|
||||
assertEq(token.nonces(owner), 1);
|
||||
}
|
||||
|
||||
function testFailBurnInsufficientBalance(address to, uint256 mintAmount, uint256 burnAmount) public {
|
||||
burnAmount = bound(burnAmount, mintAmount + 1, type(uint256).max);
|
||||
|
||||
token.mint(to, mintAmount);
|
||||
token.burn(to, burnAmount);
|
||||
}
|
||||
|
||||
function testFailTransferInsufficientBalance(address to, uint256 mintAmount, uint256 sendAmount) public {
|
||||
sendAmount = bound(sendAmount, mintAmount + 1, type(uint256).max);
|
||||
|
||||
token.mint(address(this), mintAmount);
|
||||
token.transfer(to, sendAmount);
|
||||
}
|
||||
|
||||
function testFailTransferFromInsufficientAllowance(address to, uint256 approval, uint256 amount) public {
|
||||
amount = bound(amount, approval + 1, type(uint256).max);
|
||||
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, amount);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), approval);
|
||||
|
||||
token.transferFrom(from, to, amount);
|
||||
}
|
||||
|
||||
function testFailTransferFromInsufficientBalance(address to, uint256 mintAmount, uint256 sendAmount) public {
|
||||
sendAmount = bound(sendAmount, mintAmount + 1, type(uint256).max);
|
||||
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, mintAmount);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), sendAmount);
|
||||
|
||||
token.transferFrom(from, to, sendAmount);
|
||||
}
|
||||
|
||||
function testFailPermitBadNonce(uint256 privateKey, address to, uint256 amount, uint256 deadline, uint256 nonce)
|
||||
public
|
||||
{
|
||||
if (deadline < block.timestamp) deadline = block.timestamp;
|
||||
if (privateKey == 0) privateKey = 1;
|
||||
if (nonce == 0) nonce = 1;
|
||||
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, nonce, deadline))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, to, amount, deadline, v, r, s);
|
||||
}
|
||||
|
||||
function testFailPermitBadDeadline(uint256 privateKey, address to, uint256 amount, uint256 deadline) public {
|
||||
if (deadline < block.timestamp) deadline = block.timestamp;
|
||||
if (privateKey == 0) privateKey = 1;
|
||||
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, to, amount, deadline + 1, v, r, s);
|
||||
}
|
||||
|
||||
function testFailPermitPastDeadline(uint256 privateKey, address to, uint256 amount, uint256 deadline) public {
|
||||
deadline = bound(deadline, 0, block.timestamp - 1);
|
||||
if (privateKey == 0) privateKey = 1;
|
||||
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, to, amount, deadline, v, r, s);
|
||||
}
|
||||
|
||||
function testFailPermitReplay(uint256 privateKey, address to, uint256 amount, uint256 deadline) public {
|
||||
if (deadline < block.timestamp) deadline = block.timestamp;
|
||||
if (privateKey == 0) privateKey = 1;
|
||||
|
||||
address owner = vm.addr(privateKey);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
|
||||
privateKey,
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19\x01",
|
||||
token.DOMAIN_SEPARATOR(),
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
token.permit(owner, to, amount, deadline, v, r, s);
|
||||
token.permit(owner, to, amount, deadline, v, r, s);
|
||||
}
|
||||
}
|
||||
721
lib_forge_std/test/mocks/MockERC721.t.sol
Normal file
721
lib_forge_std/test/mocks/MockERC721.t.sol
Normal file
@@ -0,0 +1,721 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import {MockERC721, IERC721TokenReceiver} from "../../src/mocks/MockERC721.sol";
|
||||
import {StdCheats} from "../../src/StdCheats.sol";
|
||||
import {Test} from "../../src/Test.sol";
|
||||
|
||||
contract ERC721Recipient is IERC721TokenReceiver {
|
||||
address public operator;
|
||||
address public from;
|
||||
uint256 public id;
|
||||
bytes public data;
|
||||
|
||||
function onERC721Received(address _operator, address _from, uint256 _id, bytes calldata _data)
|
||||
public
|
||||
virtual
|
||||
override
|
||||
returns (bytes4)
|
||||
{
|
||||
operator = _operator;
|
||||
from = _from;
|
||||
id = _id;
|
||||
data = _data;
|
||||
|
||||
return IERC721TokenReceiver.onERC721Received.selector;
|
||||
}
|
||||
}
|
||||
|
||||
contract RevertingERC721Recipient is IERC721TokenReceiver {
|
||||
function onERC721Received(address, address, uint256, bytes calldata) public virtual override returns (bytes4) {
|
||||
revert(string(abi.encodePacked(IERC721TokenReceiver.onERC721Received.selector)));
|
||||
}
|
||||
}
|
||||
|
||||
contract WrongReturnDataERC721Recipient is IERC721TokenReceiver {
|
||||
function onERC721Received(address, address, uint256, bytes calldata) public virtual override returns (bytes4) {
|
||||
return 0xCAFEBEEF;
|
||||
}
|
||||
}
|
||||
|
||||
contract NonERC721Recipient {}
|
||||
|
||||
contract Token_ERC721 is MockERC721 {
|
||||
constructor(string memory _name, string memory _symbol) {
|
||||
initialize(_name, _symbol);
|
||||
}
|
||||
|
||||
function tokenURI(uint256) public pure virtual override returns (string memory) {}
|
||||
|
||||
function mint(address to, uint256 tokenId) public virtual {
|
||||
_mint(to, tokenId);
|
||||
}
|
||||
|
||||
function burn(uint256 tokenId) public virtual {
|
||||
_burn(tokenId);
|
||||
}
|
||||
|
||||
function safeMint(address to, uint256 tokenId) public virtual {
|
||||
_safeMint(to, tokenId);
|
||||
}
|
||||
|
||||
function safeMint(address to, uint256 tokenId, bytes memory data) public virtual {
|
||||
_safeMint(to, tokenId, data);
|
||||
}
|
||||
}
|
||||
|
||||
contract MockERC721Test is StdCheats, Test {
|
||||
Token_ERC721 token;
|
||||
|
||||
function setUp() public {
|
||||
token = new Token_ERC721("Token", "TKN");
|
||||
}
|
||||
|
||||
function invariantMetadata() public view {
|
||||
assertEq(token.name(), "Token");
|
||||
assertEq(token.symbol(), "TKN");
|
||||
}
|
||||
|
||||
function testMint() public {
|
||||
token.mint(address(0xBEEF), 1337);
|
||||
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1);
|
||||
assertEq(token.ownerOf(1337), address(0xBEEF));
|
||||
}
|
||||
|
||||
function testBurn() public {
|
||||
token.mint(address(0xBEEF), 1337);
|
||||
token.burn(1337);
|
||||
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 0);
|
||||
|
||||
vm.expectRevert("NOT_MINTED");
|
||||
token.ownerOf(1337);
|
||||
}
|
||||
|
||||
function testApprove() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.approve(address(0xBEEF), 1337);
|
||||
|
||||
assertEq(token.getApproved(1337), address(0xBEEF));
|
||||
}
|
||||
|
||||
function testApproveBurn() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.approve(address(0xBEEF), 1337);
|
||||
|
||||
token.burn(1337);
|
||||
|
||||
assertEq(token.balanceOf(address(this)), 0);
|
||||
assertEq(token.getApproved(1337), address(0));
|
||||
|
||||
vm.expectRevert("NOT_MINTED");
|
||||
token.ownerOf(1337);
|
||||
}
|
||||
|
||||
function testApproveAll() public {
|
||||
token.setApprovalForAll(address(0xBEEF), true);
|
||||
|
||||
assertTrue(token.isApprovedForAll(address(this), address(0xBEEF)));
|
||||
}
|
||||
|
||||
function testTransferFrom() public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, 1337);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), 1337);
|
||||
|
||||
token.transferFrom(from, address(0xBEEF), 1337);
|
||||
|
||||
assertEq(token.getApproved(1337), address(0));
|
||||
assertEq(token.ownerOf(1337), address(0xBEEF));
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
}
|
||||
|
||||
function testTransferFromSelf() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.transferFrom(address(this), address(0xBEEF), 1337);
|
||||
|
||||
assertEq(token.getApproved(1337), address(0));
|
||||
assertEq(token.ownerOf(1337), address(0xBEEF));
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1);
|
||||
assertEq(token.balanceOf(address(this)), 0);
|
||||
}
|
||||
|
||||
function testTransferFromApproveAll() public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, 1337);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.transferFrom(from, address(0xBEEF), 1337);
|
||||
|
||||
assertEq(token.getApproved(1337), address(0));
|
||||
assertEq(token.ownerOf(1337), address(0xBEEF));
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
}
|
||||
|
||||
function testSafeTransferFromToEOA() public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
token.mint(from, 1337);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.safeTransferFrom(from, address(0xBEEF), 1337);
|
||||
|
||||
assertEq(token.getApproved(1337), address(0));
|
||||
assertEq(token.ownerOf(1337), address(0xBEEF));
|
||||
assertEq(token.balanceOf(address(0xBEEF)), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
}
|
||||
|
||||
function testSafeTransferFromToERC721Recipient() public {
|
||||
address from = address(0xABCD);
|
||||
ERC721Recipient recipient = new ERC721Recipient();
|
||||
|
||||
token.mint(from, 1337);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.safeTransferFrom(from, address(recipient), 1337);
|
||||
|
||||
assertEq(token.getApproved(1337), address(0));
|
||||
assertEq(token.ownerOf(1337), address(recipient));
|
||||
assertEq(token.balanceOf(address(recipient)), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
|
||||
assertEq(recipient.operator(), address(this));
|
||||
assertEq(recipient.from(), from);
|
||||
assertEq(recipient.id(), 1337);
|
||||
assertEq(recipient.data(), "");
|
||||
}
|
||||
|
||||
function testSafeTransferFromToERC721RecipientWithData() public {
|
||||
address from = address(0xABCD);
|
||||
ERC721Recipient recipient = new ERC721Recipient();
|
||||
|
||||
token.mint(from, 1337);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.safeTransferFrom(from, address(recipient), 1337, "testing 123");
|
||||
|
||||
assertEq(token.getApproved(1337), address(0));
|
||||
assertEq(token.ownerOf(1337), address(recipient));
|
||||
assertEq(token.balanceOf(address(recipient)), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
|
||||
assertEq(recipient.operator(), address(this));
|
||||
assertEq(recipient.from(), from);
|
||||
assertEq(recipient.id(), 1337);
|
||||
assertEq(recipient.data(), "testing 123");
|
||||
}
|
||||
|
||||
function testSafeMintToEOA() public {
|
||||
token.safeMint(address(0xBEEF), 1337);
|
||||
|
||||
assertEq(token.ownerOf(1337), address(address(0xBEEF)));
|
||||
assertEq(token.balanceOf(address(address(0xBEEF))), 1);
|
||||
}
|
||||
|
||||
function testSafeMintToERC721Recipient() public {
|
||||
ERC721Recipient to = new ERC721Recipient();
|
||||
|
||||
token.safeMint(address(to), 1337);
|
||||
|
||||
assertEq(token.ownerOf(1337), address(to));
|
||||
assertEq(token.balanceOf(address(to)), 1);
|
||||
|
||||
assertEq(to.operator(), address(this));
|
||||
assertEq(to.from(), address(0));
|
||||
assertEq(to.id(), 1337);
|
||||
assertEq(to.data(), "");
|
||||
}
|
||||
|
||||
function testSafeMintToERC721RecipientWithData() public {
|
||||
ERC721Recipient to = new ERC721Recipient();
|
||||
|
||||
token.safeMint(address(to), 1337, "testing 123");
|
||||
|
||||
assertEq(token.ownerOf(1337), address(to));
|
||||
assertEq(token.balanceOf(address(to)), 1);
|
||||
|
||||
assertEq(to.operator(), address(this));
|
||||
assertEq(to.from(), address(0));
|
||||
assertEq(to.id(), 1337);
|
||||
assertEq(to.data(), "testing 123");
|
||||
}
|
||||
|
||||
function testFailMintToZero() public {
|
||||
token.mint(address(0), 1337);
|
||||
}
|
||||
|
||||
function testFailDoubleMint() public {
|
||||
token.mint(address(0xBEEF), 1337);
|
||||
token.mint(address(0xBEEF), 1337);
|
||||
}
|
||||
|
||||
function testFailBurnUnMinted() public {
|
||||
token.burn(1337);
|
||||
}
|
||||
|
||||
function testFailDoubleBurn() public {
|
||||
token.mint(address(0xBEEF), 1337);
|
||||
|
||||
token.burn(1337);
|
||||
token.burn(1337);
|
||||
}
|
||||
|
||||
function testFailApproveUnMinted() public {
|
||||
token.approve(address(0xBEEF), 1337);
|
||||
}
|
||||
|
||||
function testFailApproveUnAuthorized() public {
|
||||
token.mint(address(0xCAFE), 1337);
|
||||
|
||||
token.approve(address(0xBEEF), 1337);
|
||||
}
|
||||
|
||||
function testFailTransferFromUnOwned() public {
|
||||
token.transferFrom(address(0xFEED), address(0xBEEF), 1337);
|
||||
}
|
||||
|
||||
function testFailTransferFromWrongFrom() public {
|
||||
token.mint(address(0xCAFE), 1337);
|
||||
|
||||
token.transferFrom(address(0xFEED), address(0xBEEF), 1337);
|
||||
}
|
||||
|
||||
function testFailTransferFromToZero() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.transferFrom(address(this), address(0), 1337);
|
||||
}
|
||||
|
||||
function testFailTransferFromNotOwner() public {
|
||||
token.mint(address(0xFEED), 1337);
|
||||
|
||||
token.transferFrom(address(0xFEED), address(0xBEEF), 1337);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToNonERC721Recipient() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new NonERC721Recipient()), 1337);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToNonERC721RecipientWithData() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new NonERC721Recipient()), 1337, "testing 123");
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToRevertingERC721Recipient() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), 1337);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToRevertingERC721RecipientWithData() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), 1337, "testing 123");
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToERC721RecipientWithWrongReturnData() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), 1337);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData() public {
|
||||
token.mint(address(this), 1337);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), 1337, "testing 123");
|
||||
}
|
||||
|
||||
function testFailSafeMintToNonERC721Recipient() public {
|
||||
token.safeMint(address(new NonERC721Recipient()), 1337);
|
||||
}
|
||||
|
||||
function testFailSafeMintToNonERC721RecipientWithData() public {
|
||||
token.safeMint(address(new NonERC721Recipient()), 1337, "testing 123");
|
||||
}
|
||||
|
||||
function testFailSafeMintToRevertingERC721Recipient() public {
|
||||
token.safeMint(address(new RevertingERC721Recipient()), 1337);
|
||||
}
|
||||
|
||||
function testFailSafeMintToRevertingERC721RecipientWithData() public {
|
||||
token.safeMint(address(new RevertingERC721Recipient()), 1337, "testing 123");
|
||||
}
|
||||
|
||||
function testFailSafeMintToERC721RecipientWithWrongReturnData() public {
|
||||
token.safeMint(address(new WrongReturnDataERC721Recipient()), 1337);
|
||||
}
|
||||
|
||||
function testFailSafeMintToERC721RecipientWithWrongReturnDataWithData() public {
|
||||
token.safeMint(address(new WrongReturnDataERC721Recipient()), 1337, "testing 123");
|
||||
}
|
||||
|
||||
function testFailBalanceOfZeroAddress() public view {
|
||||
token.balanceOf(address(0));
|
||||
}
|
||||
|
||||
function testFailOwnerOfUnminted() public view {
|
||||
token.ownerOf(1337);
|
||||
}
|
||||
|
||||
function testMetadata(string memory name, string memory symbol) public {
|
||||
MockERC721 tkn = new Token_ERC721(name, symbol);
|
||||
|
||||
assertEq(tkn.name(), name);
|
||||
assertEq(tkn.symbol(), symbol);
|
||||
}
|
||||
|
||||
function testMint(address to, uint256 id) public {
|
||||
if (to == address(0)) to = address(0xBEEF);
|
||||
|
||||
token.mint(to, id);
|
||||
|
||||
assertEq(token.balanceOf(to), 1);
|
||||
assertEq(token.ownerOf(id), to);
|
||||
}
|
||||
|
||||
function testBurn(address to, uint256 id) public {
|
||||
if (to == address(0)) to = address(0xBEEF);
|
||||
|
||||
token.mint(to, id);
|
||||
token.burn(id);
|
||||
|
||||
assertEq(token.balanceOf(to), 0);
|
||||
|
||||
vm.expectRevert("NOT_MINTED");
|
||||
token.ownerOf(id);
|
||||
}
|
||||
|
||||
function testApprove(address to, uint256 id) public {
|
||||
if (to == address(0)) to = address(0xBEEF);
|
||||
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.approve(to, id);
|
||||
|
||||
assertEq(token.getApproved(id), to);
|
||||
}
|
||||
|
||||
function testApproveBurn(address to, uint256 id) public {
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.approve(address(to), id);
|
||||
|
||||
token.burn(id);
|
||||
|
||||
assertEq(token.balanceOf(address(this)), 0);
|
||||
assertEq(token.getApproved(id), address(0));
|
||||
|
||||
vm.expectRevert("NOT_MINTED");
|
||||
token.ownerOf(id);
|
||||
}
|
||||
|
||||
function testApproveAll(address to, bool approved) public {
|
||||
token.setApprovalForAll(to, approved);
|
||||
|
||||
assertEq(token.isApprovedForAll(address(this), to), approved);
|
||||
}
|
||||
|
||||
function testTransferFrom(uint256 id, address to) public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
if (to == address(0) || to == from) to = address(0xBEEF);
|
||||
|
||||
token.mint(from, id);
|
||||
|
||||
vm.prank(from);
|
||||
token.approve(address(this), id);
|
||||
|
||||
token.transferFrom(from, to, id);
|
||||
|
||||
assertEq(token.getApproved(id), address(0));
|
||||
assertEq(token.ownerOf(id), to);
|
||||
assertEq(token.balanceOf(to), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
}
|
||||
|
||||
function testTransferFromSelf(uint256 id, address to) public {
|
||||
if (to == address(0) || to == address(this)) to = address(0xBEEF);
|
||||
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.transferFrom(address(this), to, id);
|
||||
|
||||
assertEq(token.getApproved(id), address(0));
|
||||
assertEq(token.ownerOf(id), to);
|
||||
assertEq(token.balanceOf(to), 1);
|
||||
assertEq(token.balanceOf(address(this)), 0);
|
||||
}
|
||||
|
||||
function testTransferFromApproveAll(uint256 id, address to) public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
if (to == address(0) || to == from) to = address(0xBEEF);
|
||||
|
||||
token.mint(from, id);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.transferFrom(from, to, id);
|
||||
|
||||
assertEq(token.getApproved(id), address(0));
|
||||
assertEq(token.ownerOf(id), to);
|
||||
assertEq(token.balanceOf(to), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
}
|
||||
|
||||
function testSafeTransferFromToEOA(uint256 id, address to) public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
if (to == address(0) || to == from) to = address(0xBEEF);
|
||||
|
||||
if (uint256(uint160(to)) <= 18 || to.code.length > 0) return;
|
||||
|
||||
token.mint(from, id);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.safeTransferFrom(from, to, id);
|
||||
|
||||
assertEq(token.getApproved(id), address(0));
|
||||
assertEq(token.ownerOf(id), to);
|
||||
assertEq(token.balanceOf(to), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
}
|
||||
|
||||
function testSafeTransferFromToERC721Recipient(uint256 id) public {
|
||||
address from = address(0xABCD);
|
||||
|
||||
ERC721Recipient recipient = new ERC721Recipient();
|
||||
|
||||
token.mint(from, id);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.safeTransferFrom(from, address(recipient), id);
|
||||
|
||||
assertEq(token.getApproved(id), address(0));
|
||||
assertEq(token.ownerOf(id), address(recipient));
|
||||
assertEq(token.balanceOf(address(recipient)), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
|
||||
assertEq(recipient.operator(), address(this));
|
||||
assertEq(recipient.from(), from);
|
||||
assertEq(recipient.id(), id);
|
||||
assertEq(recipient.data(), "");
|
||||
}
|
||||
|
||||
function testSafeTransferFromToERC721RecipientWithData(uint256 id, bytes calldata data) public {
|
||||
address from = address(0xABCD);
|
||||
ERC721Recipient recipient = new ERC721Recipient();
|
||||
|
||||
token.mint(from, id);
|
||||
|
||||
vm.prank(from);
|
||||
token.setApprovalForAll(address(this), true);
|
||||
|
||||
token.safeTransferFrom(from, address(recipient), id, data);
|
||||
|
||||
assertEq(token.getApproved(id), address(0));
|
||||
assertEq(token.ownerOf(id), address(recipient));
|
||||
assertEq(token.balanceOf(address(recipient)), 1);
|
||||
assertEq(token.balanceOf(from), 0);
|
||||
|
||||
assertEq(recipient.operator(), address(this));
|
||||
assertEq(recipient.from(), from);
|
||||
assertEq(recipient.id(), id);
|
||||
assertEq(recipient.data(), data);
|
||||
}
|
||||
|
||||
function testSafeMintToEOA(uint256 id, address to) public {
|
||||
if (to == address(0)) to = address(0xBEEF);
|
||||
|
||||
if (uint256(uint160(to)) <= 18 || to.code.length > 0) return;
|
||||
|
||||
token.safeMint(to, id);
|
||||
|
||||
assertEq(token.ownerOf(id), address(to));
|
||||
assertEq(token.balanceOf(address(to)), 1);
|
||||
}
|
||||
|
||||
function testSafeMintToERC721Recipient(uint256 id) public {
|
||||
ERC721Recipient to = new ERC721Recipient();
|
||||
|
||||
token.safeMint(address(to), id);
|
||||
|
||||
assertEq(token.ownerOf(id), address(to));
|
||||
assertEq(token.balanceOf(address(to)), 1);
|
||||
|
||||
assertEq(to.operator(), address(this));
|
||||
assertEq(to.from(), address(0));
|
||||
assertEq(to.id(), id);
|
||||
assertEq(to.data(), "");
|
||||
}
|
||||
|
||||
function testSafeMintToERC721RecipientWithData(uint256 id, bytes calldata data) public {
|
||||
ERC721Recipient to = new ERC721Recipient();
|
||||
|
||||
token.safeMint(address(to), id, data);
|
||||
|
||||
assertEq(token.ownerOf(id), address(to));
|
||||
assertEq(token.balanceOf(address(to)), 1);
|
||||
|
||||
assertEq(to.operator(), address(this));
|
||||
assertEq(to.from(), address(0));
|
||||
assertEq(to.id(), id);
|
||||
assertEq(to.data(), data);
|
||||
}
|
||||
|
||||
function testFailMintToZero(uint256 id) public {
|
||||
token.mint(address(0), id);
|
||||
}
|
||||
|
||||
function testFailDoubleMint(uint256 id, address to) public {
|
||||
if (to == address(0)) to = address(0xBEEF);
|
||||
|
||||
token.mint(to, id);
|
||||
token.mint(to, id);
|
||||
}
|
||||
|
||||
function testFailBurnUnMinted(uint256 id) public {
|
||||
token.burn(id);
|
||||
}
|
||||
|
||||
function testFailDoubleBurn(uint256 id, address to) public {
|
||||
if (to == address(0)) to = address(0xBEEF);
|
||||
|
||||
token.mint(to, id);
|
||||
|
||||
token.burn(id);
|
||||
token.burn(id);
|
||||
}
|
||||
|
||||
function testFailApproveUnMinted(uint256 id, address to) public {
|
||||
token.approve(to, id);
|
||||
}
|
||||
|
||||
function testFailApproveUnAuthorized(address owner, uint256 id, address to) public {
|
||||
if (owner == address(0) || owner == address(this)) owner = address(0xBEEF);
|
||||
|
||||
token.mint(owner, id);
|
||||
|
||||
token.approve(to, id);
|
||||
}
|
||||
|
||||
function testFailTransferFromUnOwned(address from, address to, uint256 id) public {
|
||||
token.transferFrom(from, to, id);
|
||||
}
|
||||
|
||||
function testFailTransferFromWrongFrom(address owner, address from, address to, uint256 id) public {
|
||||
if (owner == address(0)) to = address(0xBEEF);
|
||||
if (from == owner) revert();
|
||||
|
||||
token.mint(owner, id);
|
||||
|
||||
token.transferFrom(from, to, id);
|
||||
}
|
||||
|
||||
function testFailTransferFromToZero(uint256 id) public {
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.transferFrom(address(this), address(0), id);
|
||||
}
|
||||
|
||||
function testFailTransferFromNotOwner(address from, address to, uint256 id) public {
|
||||
if (from == address(this)) from = address(0xBEEF);
|
||||
|
||||
token.mint(from, id);
|
||||
|
||||
token.transferFrom(from, to, id);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToNonERC721Recipient(uint256 id) public {
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new NonERC721Recipient()), id);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToNonERC721RecipientWithData(uint256 id, bytes calldata data) public {
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new NonERC721Recipient()), id, data);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToRevertingERC721Recipient(uint256 id) public {
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), id);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToRevertingERC721RecipientWithData(uint256 id, bytes calldata data) public {
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), id, data);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToERC721RecipientWithWrongReturnData(uint256 id) public {
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), id);
|
||||
}
|
||||
|
||||
function testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData(uint256 id, bytes calldata data)
|
||||
public
|
||||
{
|
||||
token.mint(address(this), id);
|
||||
|
||||
token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), id, data);
|
||||
}
|
||||
|
||||
function testFailSafeMintToNonERC721Recipient(uint256 id) public {
|
||||
token.safeMint(address(new NonERC721Recipient()), id);
|
||||
}
|
||||
|
||||
function testFailSafeMintToNonERC721RecipientWithData(uint256 id, bytes calldata data) public {
|
||||
token.safeMint(address(new NonERC721Recipient()), id, data);
|
||||
}
|
||||
|
||||
function testFailSafeMintToRevertingERC721Recipient(uint256 id) public {
|
||||
token.safeMint(address(new RevertingERC721Recipient()), id);
|
||||
}
|
||||
|
||||
function testFailSafeMintToRevertingERC721RecipientWithData(uint256 id, bytes calldata data) public {
|
||||
token.safeMint(address(new RevertingERC721Recipient()), id, data);
|
||||
}
|
||||
|
||||
function testFailSafeMintToERC721RecipientWithWrongReturnData(uint256 id) public {
|
||||
token.safeMint(address(new WrongReturnDataERC721Recipient()), id);
|
||||
}
|
||||
|
||||
function testFailSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256 id, bytes calldata data) public {
|
||||
token.safeMint(address(new WrongReturnDataERC721Recipient()), id, data);
|
||||
}
|
||||
|
||||
function testFailOwnerOfUnminted(uint256 id) public view {
|
||||
token.ownerOf(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user