├── .prettierrc.js
├── .shellcheckrc
├── .vscode
└── ltex.dictionary.en-US.txt
├── LICENSE.txt
├── README.md
├── justfile
├── package.json
├── scripts
├── common.sh
├── contract-size.sh
├── deploy.sh
├── estimate-gas.sh
├── helper-config.sh
├── run-temp-testnet.sh
└── test-deploy.sh
└── src
├── ACL
└── Trust.sol
├── AbiDecodeTest.sol
├── AccountWatcher.sol
├── AllowancesHelper.sol
├── Approvals.sol
├── BalanceScanner.sol
├── BalancesAndAllowancesHelper.sol
├── BytesParsing.sol
├── CallFunctionWithoutContract.sol
├── CalldataUtils.lib.sol
├── CodeHashCache.sol
├── CounterfactualFactory.sol
├── ERC20InvalidMock.sol
├── ERC20Mock.sol
├── ERCQuery.sol
├── EthHelper.sol
├── GasMeterFactory.sol
├── GenericFactory.sol
├── Helpers.sol
├── ImmutableCreate2Factory.sol
├── Introspection.sol
├── MeterMaid.sol
├── PairsHelper.sol
├── PhonyUser.sol
├── ReplayProtection.sol
├── SelectorsAndSignatures.sol
├── TokenMetadata.sol
├── UniswapV3
├── FixedMath.sol
├── MockUniSwapRouterV3.sol
└── MockUniV3Pool.sol
├── WalletSummarizer.sol
├── getInterface.sol
├── test
├── TestUtils.sol
└── oops.lib.sol
└── utils
├── Create.sol
└── errors.sol
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file Prettier configuration for Solidity
3 | * @version 1.3.1
4 | * @summary base config adapted from AirBNB to reduce diff churn
5 | * @overrides solidity settings from Solidity Documentation
6 | * @note `printWidth` is not a line character capture
7 | * @dev `explicitTypes`
8 | * @solidity versions ^0.8.0 bytes1
9 | */
10 |
11 | 'use strict';
12 |
13 | module.exports = {
14 | arrowParens: 'always',
15 | bracketSpacing: true,
16 | endOfLine: 'lf',
17 | printWidth: 80,
18 | singleQuote: true,
19 | tabWidth: 2,
20 | trailingComma: 'all',
21 | quoteProps: 'as-needed',
22 | semi: true,
23 | overrides: [
24 | {
25 | files: '*.sol',
26 | options: {
27 | printWidth: 100,
28 | tabWidth: 4,
29 | useTabs: false,
30 | singleQuote: false,
31 | bracketSpacing: true,
32 | explicitTypes: 'always',
33 | },
34 | },
35 | ],
36 | };
37 | /** @exports prettier-config-solidity */
38 |
--------------------------------------------------------------------------------
/.shellcheckrc:
--------------------------------------------------------------------------------
1 | shell=bash
2 | enable=deprecate-which
3 | enable=require-double-brackets
4 | disable=SC1090 # sourcing files; ENV SHELLCHECK_OPTS='--shell=bash --exclude=SC1090'
5 | disable=SC2034 # for localizing variables set in called functions
6 |
--------------------------------------------------------------------------------
/.vscode/ltex.dictionary.en-US.txt:
--------------------------------------------------------------------------------
1 | SPDX-License-Identifier
2 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2021 CommodityStream LLC
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## [isdx](#)
2 | > Introspection / Observability smart contracts
3 |
4 | ### examples
5 |
6 | - `AllowancesHelper`
7 |
8 | ```solidity
9 | // Fetch allowances
10 | Allowance[] memory _allowances = new Allowance[](numberOfAllowances);
11 | uint256 allowanceIdx;
12 | for (tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
13 | for (spenderIdx = 0; spenderIdx < spenderAddresses.length; spenderIdx++) {
14 | address spenderAddress = spenderAddresses[spenderIdx];
15 | address tokenAddress = tokensAddresses[tokenIdx];
16 | IERC20 token = IERC20(tokenAddress);
17 | uint256 amount = token.allowance(ownerAddress, spenderAddress);
18 | if (amount > 0) {
19 | Allowance memory allowance = Allowance({
20 | owner: ownerAddress,
21 | spender: spenderAddress,
22 | amount: amount,
23 | token: tokenAddress
24 | });
25 | _allowances[allowanceIdx] = allowance;
26 | allowanceIdx++;
27 | }
28 | }
29 | }
30 | return _allowances;
31 | ```
32 |
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env just --justfile
2 |
3 | alias t := test
4 |
5 | alias c := check
6 |
7 | bt := '0'
8 |
9 | export RUST_BACKTRACE := bt
10 |
11 | log := "warn"
12 |
13 | export JUST_LOG := log
14 |
15 | test:
16 | forge test
17 |
18 | build:
19 | forge build --root .
20 |
21 | run:
22 | forge run
23 |
24 | install:
25 | forge install
26 |
27 | check:
28 | forge test -f $ETH_RPC_URL -vvv
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vzor",
3 | "version": "0.0.0",
4 | "description": "I can see the Earth through the \"VZOR\" (porthole). The Earth is clearly visible",
5 | "scripts": {
6 | "fmt": "npx prettier --config .prettierrc.js --write '**/*.{sol,ts,tsx,js,md,yml,yaml}'"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/sambacha/isdx.git"
11 | },
12 | "keywords": [
13 | "solidity",
14 | "ethereum",
15 | "mev"
16 | ],
17 | "files": [
18 | "/dist/**/*",
19 | "/contracts",
20 | "/contracts/base",
21 | "/contracts/interfaces",
22 | "/contracts/libraries",
23 | "/artifacts/contracts/**/*.json",
24 | "!artifacts/contracts/**/*.dbg.json",
25 | "!artifacts/contracts/test/**/*",
26 | "!artifacts/contracts/base/**/*",
27 | "/src/**/*"
28 | ],
29 | "author": "SEE CONTRIBUTORS",
30 | "license": "(MIT OR APACHE-2.0)",
31 | "dependencies": {},
32 | "devDependencies": {
33 | "prettier": "2.6.0",
34 | "prettier-config-solidity": "1.7.0",
35 | "prettier-plugin-solidity": "1.0.0-beta.19"
36 | },
37 | "bugs": {
38 | "url": "https://github.com/sambacha/isdx/issues"
39 | },
40 | "homepage": "https://github.com/sambacha/isdx#readme"
41 | }
42 |
--------------------------------------------------------------------------------
/scripts/common.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | if [[ ${DEBUG} ]]; then
6 | set -x
7 | fi
8 |
9 | # All contracts are output to `out/addresses.json` by default
10 | OUT_DIR=${OUT_DIR:-$PWD/out}
11 | ADDRESSES_FILE=${ADDRESSES_FILE:-$OUT_DIR/"addresses.json"}
12 | # default to localhost rpc
13 | ETH_RPC_URL=${ETH_RPC_URL:-http://localhost:8545}
14 |
15 | # green log helper
16 | GREEN='\033[0;32m'
17 | NC='\033[0m' # No Color
18 | log() {
19 | printf '%b\n' "${GREEN}${*}${NC}"
20 | echo ""
21 | }
22 |
23 | # Coloured output helpers
24 | if command -v tput >/dev/null 2>&1; then
25 | if [ $(($(tput colors 2>/dev/null))) -ge 8 ]; then
26 | # Enable colors
27 | TPUT_RESET="$(tput sgr 0)"
28 | TPUT_YELLOW="$(tput setaf 3)"
29 | TPUT_RED="$(tput setaf 1)"
30 | TPUT_BLUE="$(tput setaf 4)"
31 | TPUT_GREEN="$(tput setaf 2)"
32 | TPUT_WHITE="$(tput setaf 7)"
33 | TPUT_BOLD="$(tput bold)"
34 | fi
35 | fi
36 |
37 | # ensure ETH_FROM is set and give a meaningful error message
38 | if [[ -z ${ETH_FROM} ]]; then
39 | echo "ETH_FROM not found, please set it and re-run the last command."
40 | exit 1
41 | fi
42 |
43 | # Make sure address is checksummed
44 | if [ "$ETH_FROM" != "$(seth --to-checksum-address "$ETH_FROM")" ]; then
45 | echo "ETH_FROM not checksummed, please format it with 'seth --to-checksum-address
'"
46 | exit 1
47 | fi
48 |
49 | # Setup addresses file
50 | cat >"$ADDRESSES_FILE" < deploy ContractName arg1 arg2 arg3`
57 | # (or omit the env vars if you have already set them)
58 | deploy() {
59 | NAME=$1
60 | ARGS=${@:2}
61 |
62 | # find file path
63 | CONTRACT_PATH=$(find ./src -name $NAME.sol)
64 | CONTRACT_PATH=${CONTRACT_PATH:2}
65 |
66 | # select the filename and the contract in it
67 | PATTERN=".contracts[\"$CONTRACT_PATH\"].$NAME"
68 |
69 | # get the constructor's signature
70 | ABI=$(jq -r "$PATTERN.abi" out/dapp.sol.json)
71 | SIG=$(echo "$ABI" | seth --abi-constructor)
72 |
73 | # get the bytecode from the compiled file
74 | BYTECODE=0x$(jq -r "$PATTERN.evm.bytecode.object" out/dapp.sol.json)
75 |
76 | # estimate gas
77 | GAS=$(seth estimate --create "$BYTECODE" "$SIG" $ARGS --rpc-url "$ETH_RPC_URL")
78 |
79 | # deploy
80 | ADDRESS=$(dapp create "$NAME" $ARGS -- --gas "$GAS" --rpc-url "$ETH_RPC_URL")
81 |
82 | # save the addrs to the json
83 | # TODO: It'd be nice if we could evolve this into a minimal versioning system
84 | # e.g. via commit / chainid etc.
85 | saveContract "$NAME" "$ADDRESS"
86 |
87 | echo "$ADDRESS"
88 | }
89 |
90 | # Call as `saveContract ContractName 0xYourAddress` to store the contract name
91 | # & address to the addresses json file
92 | saveContract() {
93 | # create an empty json if it does not exist
94 | if [[ ! -e $ADDRESSES_FILE ]]; then
95 | echo "{}" >"$ADDRESSES_FILE"
96 | fi
97 | result=$(cat "$ADDRESSES_FILE" | jq -r ". + {\"$1\": \"$2\"}")
98 | printf %s "$result" >"$ADDRESSES_FILE"
99 | }
100 |
101 | estimate_gas() {
102 | NAME=$1
103 | ARGS=${@:2}
104 | # select the filename and the contract in it
105 | PATTERN=".contracts[\"src/$NAME.sol\"].$NAME"
106 |
107 | # get the constructor's signature
108 | ABI=$(jq -r "$PATTERN.abi" out/dapp.sol.json)
109 | SIG=$(echo "$ABI" | seth --abi-constructor)
110 |
111 | # get the bytecode from the compiled file
112 | BYTECODE=0x$(jq -r "$PATTERN.evm.bytecode.object" out/dapp.sol.json)
113 | # estimate gas
114 | GAS=$(seth estimate --create "$BYTECODE" "$SIG" $ARGS --rpc-url "$ETH_RPC_URL")
115 |
116 | TXPRICE_RESPONSE=$(curl -sL https://api.txprice.com/v1)
117 | response=$(jq '.code' <<<"$TXPRICE_RESPONSE")
118 | if [[ $response != "200" ]]; then
119 | echo "Could not get gas information from ${TPUT_BOLD}txprice.com${TPUT_RESET}: https://api.txprice.com/v1"
120 | echo "response code: $response"
121 | else
122 | rapid=$(($(jq '.blockPrices[0].estimatedPrices[0].maxFeePerGas' <<<"$TXPRICE_RESPONSE")))
123 | fast=$(($(jq '.blockPrices[0].estimatedPrices[1].maxFeePerGas' <<<"$TXPRICE_RESPONSE")))
124 | standard=$(($(jq '.blockPrices[0].estimatedPrices[2].maxFeePerGas' <<<"$TXPRICE_RESPONSE")))
125 | slow=$(($(jq '.blockPrices[0].estimatedPrices[3].maxFeePerGas' <<<"$TXPRICE_RESPONSE")))
126 | basefee$(($(jq '.blockPrices[0].baseFeePerGas' <<<"$TXPRICE_RESPONSE")))
127 | echo "Gas prices from ${TPUT_BOLD}txprice.com${TPUT_RESET}: https://api.txprice.com/v1"
128 | echo " \
129 | ${TPUT_RED}Rapid: $rapid gwei ${TPUT_RESET} \n
130 | ${TPUT_YELLOW}Fast: $fast gwei \n
131 | ${TPUT_BLUE}Standard: $standard gwei \n
132 | ${TPUT_GREEN}Slow: $slow gwei${TPUT_RESET}" | column -t
133 | size=$(contract_size "$NAME")
134 | echo "Estimated Gas cost for deployment of $NAME: ${TPUT_BOLD}$GAS${TPUT_RESET} units of gas"
135 | echo "Contract Size: ${size} bytes"
136 | echo "Total cost for deployment:"
137 | rapid_cost=$(echo "scale=5; $GAS*$rapid" | bc)
138 | fast_cost=$(echo "scale=5; $GAS*$fast" | bc)
139 | standard_cost=$(echo "scale=5; $GAS*$standard" | bc)
140 | slow_cost=$(echo "scale=5; $GAS*$slow" | bc)
141 | echo " \
142 | ${TPUT_RED}Rapid: $rapid_cost ETH ${TPUT_RESET} \n
143 | ${TPUT_YELLOW}Fast: $fast_cost ETH \n
144 | ${TPUT_BLUE}Standard: $standard_cost ETH \n
145 | ${TPUT_GREEN}Slow: $slow_cost ETH ${TPUT_RESET}" | column -t
146 | fi
147 | }
148 |
149 | contract_size() {
150 | NAME=$1
151 | ARGS=${@:2}
152 | # select the filename and the contract in it
153 | PATTERN=".contracts[\"src/$NAME.sol\"].$NAME"
154 |
155 | # get the bytecode from the compiled file
156 | BYTECODE=0x$(jq -r "$PATTERN.evm.bytecode.object" out/dapp.sol.json)
157 | length=$(echo "$BYTECODE" | wc -m)
158 | echo $(($length / 2))
159 | }
160 |
--------------------------------------------------------------------------------
/scripts/contract-size.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | . $(dirname $0)/common.sh
6 |
7 | if [[ -z $contract ]]; then
8 | if [[ -z ${1} ]];then
9 | echo '"$contract" env variable is not set. Set it to the name of the contract you want to estimate size for.'
10 | exit 1
11 | else
12 | contract=${1}
13 | fi
14 | fi
15 | contract_size=$(contract_size ${contract})
16 | echo "Contract Name: ${contract}"
17 | echo "Contract Size: ${contract_size} bytes"
18 | echo "$(( 24576 - ${contract_size} )) bytes left to reach the smart contract size limit of 24576 bytes."
19 |
--------------------------------------------------------------------------------
/scripts/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | # import the deployment helpers
6 | . $(dirname $0)/common.sh
7 |
8 | # import config with arguments based on contract and network
9 | . $(dirname $0)/helper-config.sh
10 |
11 | # Deploy
12 | # Contract will be counter unless overriden on the command line
13 | : ${CONTRACT:=Greeter}
14 | echo "Deploying $CONTRACT to $NETWORK with arguments: $arguments"
15 | Addr=$(deploy $CONTRACT $arguments)
16 | log "$CONTRACT deployed at:" $Addr
17 |
--------------------------------------------------------------------------------
/scripts/estimate-gas.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | . $(dirname $0)/common.sh
6 |
7 | if [[ -z $contract ]]; then
8 | if [[ -z ${1} ]];then
9 | echo '"$contract" env variable is not set. Set it to the name of the contract you want to estimate gas cost for.'
10 | exit 1
11 | else
12 | contract=${1}
13 | fi
14 | fi
15 |
16 | estimate_gas $contract
17 |
18 |
19 |
--------------------------------------------------------------------------------
/scripts/helper-config.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Defaults
4 | # Add your defaults here
5 | # For example:
6 | # address=0x01be23585060835e02b77ef475b0cc51aa1e0709
7 |
8 | # Add your contract arguments default here
9 | arguments=""
10 |
11 | if [ "$NETWORK" = "rinkeby" ]
12 | then
13 | : # Add arguments only for rinkeby here!
14 | # like:
15 | # address=0x01be23585060835e02b77ef475b0cc51aa1e0709
16 | elif [ "$NETWORK" = "mainnet" ]
17 | then
18 | : # Add arguments only for mainnet here!
19 | # like:
20 | # address=0x01be23585060835e02b77ef475b0cc51aa1e0709
21 | fi
22 |
23 | if [ "$CONTRACT" = "Greeter" ]
24 | then
25 | : # Add conditional arguments here for contracts
26 | # arguments=$interval
27 | fi
28 |
--------------------------------------------------------------------------------
/scripts/run-temp-testnet.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | # Utility for running a temporary dapp testnet w/ an ephemeral account
6 | # to be used for deployment tests
7 |
8 | # make a temp dir to store testnet info
9 | export TMPDIR=$(mktemp -d)
10 |
11 | # clean up
12 | trap 'killall geth && sleep 3 && rm -rf "$TMPDIR"' EXIT
13 | trap "exit 1" SIGINT SIGTERM
14 |
15 | # test helper
16 | error() {
17 | printf 1>&2 "fail: function '%s' at line %d.\n" "${FUNCNAME[1]}" "${BASH_LINENO[0]}"
18 | printf 1>&2 "got: %s" "$output"
19 | exit 1
20 | }
21 |
22 | # launch the testnet
23 | dapp testnet --dir "$TMPDIR" &
24 | # wait for it to launch (can't go <3s)
25 | sleep 3
26 |
27 | # set the RPC URL to the local testnet
28 | export ETH_RPC_URL=http://127.0.0.1:8545
29 |
30 | # get the created account (it's unlocked so we only need to set the address)
31 | export ETH_FROM=$(seth ls --keystore $TMPDIR/8545/keystore | cut -f1)
32 |
--------------------------------------------------------------------------------
/scripts/test-deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eo pipefail
4 |
5 | # bring up the network
6 | . $(dirname $0)/run-temp-testnet.sh
7 |
8 | # run the deploy script
9 | . $(dirname $0)/deploy.sh
10 |
11 | # get the address
12 | addr=$(jq -r '.Greeter' out/addresses.json)
13 |
14 | # the initial greeting must be empty
15 | greeting=$(seth call $addr 'greeting()(string)')
16 | [[ $greeting = "" ]] || error
17 |
18 | # set it to a value
19 | seth send $addr \
20 | 'greet(string memory)' '"yo"' \
21 | --keystore $TMPDIR/8545/keystore \
22 | --password /dev/null
23 |
24 | sleep 1
25 |
26 | # should be set afterwards
27 | greeting=$(seth call $addr 'greeting()(string)')
28 | [[ $greeting = "yo" ]] || error
29 |
30 | echo "Success."
31 |
--------------------------------------------------------------------------------
/src/ACL/Trust.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: AGPL-3.0-only
2 | pragma solidity >=0.7.0;
3 |
4 | /// @notice Ultra minimal authorization logic for smart contracts.
5 | /// @author From https://github.com/Rari-Capital/solmate/blob/fab107565a51674f3a3b5bfdaacc67f6179b1a9b/src/auth/Trust.sol
6 | abstract contract Trust {
7 | event UserTrustUpdated(address indexed user, bool trusted);
8 |
9 | mapping(address => bool) public isTrusted;
10 |
11 | constructor(address initialUser) {
12 | isTrusted[initialUser] = true;
13 |
14 | emit UserTrustUpdated(initialUser, true);
15 | }
16 |
17 | function setIsTrusted(address user, bool trusted) public virtual requiresTrust {
18 | isTrusted[user] = trusted;
19 |
20 | emit UserTrustUpdated(user, trusted);
21 | }
22 |
23 | modifier requiresTrust() {
24 | require(isTrusted[msg.sender], "UNTRUSTED");
25 |
26 | _;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/AbiDecodeTest.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.4.22 <0.7.0;
2 |
3 | contract AbiDecodeTest {
4 | //test data: 0xb82fedbbee8ea5499c660b8322896ada651fc8774b4a23e582b75e8f8e51d629da556a7400000000000000000000000035d76d9fad7c95873266deffd2be50ce2c350c37dbb31252d9bddb4e4d362c7b9c80cba74732280737af97971f42ccbdc716f3f3efb1db366880e52d09b1bfd59842e833f3004088892b7d14b9ce9e957cea9a82
5 |
6 | //execution cost: 860 gas
7 | function abiDecodeTest(
8 | bytes memory _data
9 | )
10 | public
11 | pure
12 | returns(
13 | bytes4 sig,
14 | bytes32 label,
15 | address account,
16 | bytes32 pubkeyA,
17 | bytes32 pubkeyB
18 | )
19 | {
20 | assembly {
21 | sig := mload(add(_data, 32))
22 | label := mload(add(_data, 36))
23 | account := mload(add(_data, 68))
24 | pubkeyA := mload(add(_data, 100))
25 | pubkeyB := mload(add(_data, 132))
26 | }
27 | }
28 |
29 | //execution cost: 1294 gas
30 | function abiDecodeTest2(
31 | bytes calldata _data
32 | )
33 | external
34 | pure
35 | returns(
36 | bytes4 sig,
37 | bytes32 label,
38 | address account,
39 | bytes32 pubkeyA,
40 | bytes32 pubkeyB
41 | )
42 | {
43 | sig = _data[0] | bytes4(_data[1]) >> 8 | bytes4(_data[2]) >> 16 | bytes4(_data[3]) >> 24;
44 | (label, account, pubkeyA, pubkeyB) = abi.decode(_data[4:], (bytes32, address, bytes32, bytes32));
45 | }
46 |
47 | //execution cost: 1192 gas
48 | function abiDecodeTest3(
49 | bytes calldata _data
50 | )
51 | external
52 | pure
53 | returns(
54 | bytes4 sig,
55 | bytes32 label,
56 | address account,
57 | bytes32 pubkeyA,
58 | bytes32 pubkeyB
59 | )
60 | {
61 | sig = getSelector(_data);
62 | (label, account, pubkeyA, pubkeyB) = abi.decode(_data[4:], (bytes32, address, bytes32, bytes32));
63 | }
64 |
65 | function getSelector(bytes memory _data) private pure returns(bytes4 sig) {
66 | assembly {
67 | sig := mload(add(_data, 32))
68 | }
69 | }
70 |
71 | //execution cost: 1885 gas
72 | function abiDecodeTest4(
73 | bytes calldata _data
74 | )
75 | external
76 | pure
77 | returns(
78 | bytes4 sig,
79 | bytes32 label,
80 | address account,
81 | bytes32 pubkeyA,
82 | bytes32 pubkeyB
83 | )
84 | {
85 | sig = getSelector(_data);
86 | (label, account, pubkeyA, pubkeyB) = abi.decode(slice(_data,4,_data.length-4), (bytes32, address, bytes32, bytes32));
87 | }
88 |
89 | function getSelector(bytes memory _data) private pure returns(bytes4 sig) {
90 | assembly {
91 | sig := mload(add(_data, 32))
92 | }
93 | }
94 |
95 | function slice(bytes memory _bytes, uint _start, uint _length) private pure returns (bytes memory) {
96 | require(_bytes.length >= (_start + _length));
97 |
98 | bytes memory tempBytes;
99 |
100 | assembly {
101 | switch iszero(_length)
102 | case 0 {
103 | // Get a location of some free memory and store it in tempBytes as
104 | // Solidity does for memory variables.
105 | tempBytes := mload(0x40)
106 |
107 | // The first word of the slice result is potentially a partial
108 | // word read from the original array. To read it, we calculate
109 | // the length of that partial word and start copying that many
110 | // bytes into the array. The first word we copy will start with
111 | // data we don't care about, but the last `lengthmod` bytes will
112 | // land at the beginning of the contents of the new array. When
113 | // we're done copying, we overwrite the full first word with
114 | // the actual length of the slice.
115 | let lengthmod := and(_length, 31)
116 |
117 | // The multiplication in the next line is necessary
118 | // because when slicing multiples of 32 bytes (lengthmod == 0)
119 | // the following copy loop was copying the origin's length
120 | // and then ending prematurely not copying everything it should.
121 | let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
122 | let end := add(mc, _length)
123 |
124 | for {
125 | // The multiplication in the next line has the same exact purpose
126 | // as the one above.
127 | let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
128 | } lt(mc, end) {
129 | mc := add(mc, 0x20)
130 | cc := add(cc, 0x20)
131 | } {
132 | mstore(mc, mload(cc))
133 | }
134 |
135 | mstore(tempBytes, _length)
136 |
137 | //update free-memory pointer
138 | //allocating the array padded to 32 bytes like the compiler does now
139 | mstore(0x40, and(add(mc, 31), not(31)))
140 | }
141 | //if we want a zero-length slice let's just return a zero-length array
142 | default {
143 | tempBytes := mload(0x40)
144 |
145 | mstore(0x40, add(tempBytes, 0x20))
146 | }
147 | }
148 |
149 | return tempBytes;
150 | }
151 |
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/src/AccountWatcher.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.4;
3 |
4 | struct Account {
5 | uint256 etherBalance;
6 | TokenBalance[] tokenBalances;
7 | }
8 |
9 | struct TokenBalance {
10 | bool callSuccess;
11 | uint256 balance;
12 | }
13 |
14 | interface ERC20Interface {
15 | function balanceOf(address account) external view returns (uint256 balance);
16 | }
17 |
18 | interface AccountWatcherInterface {
19 | function balancesOf(ERC20Interface[] calldata tokens, address[] calldata accounts)
20 | external
21 | view
22 | returns (Account[] memory accountBalances);
23 | }
24 |
25 | /// Quickly check the Ether balance, as well as the balance of each
26 | /// supplied ERC20 token, for a collection of accounts.
27 | /// @author 0age
28 | contract AccountWatcher is AccountWatcherInterface {
29 | function balancesOf(ERC20Interface[] calldata tokens, address[] calldata accounts)
30 | external
31 | view
32 | override
33 | returns (Account[] memory)
34 | {
35 | Account[] memory accountBalances = new Account[](accounts.length);
36 |
37 | for (uint256 i = 0; i < accounts.length; i++) {
38 | address account = accounts[i];
39 |
40 | TokenBalance[] memory tokenBalances = new TokenBalance[](tokens.length);
41 |
42 | for (uint256 j = 0; j < tokens.length; j++) {
43 | ERC20Interface token = tokens[j];
44 | (bool success, bytes memory returnData) = address(token).staticcall(
45 | abi.encodeWithSelector(token.balanceOf.selector, account)
46 | );
47 |
48 | if (success && returnData.length >= 32) {
49 | TokenBalance memory tokenBalance;
50 |
51 | tokenBalance.callSuccess = true;
52 | tokenBalance.balance = abi.decode(returnData, (uint256));
53 |
54 | tokenBalances[j] = tokenBalance;
55 | }
56 | }
57 |
58 | accountBalances[i].etherBalance = account.balance;
59 | accountBalances[i].tokenBalances = tokenBalances;
60 | }
61 |
62 | return accountBalances;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/AllowancesHelper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.4;
4 |
5 | interface IERC20 {
6 | function allowance(address spender, address owner) external view returns (uint256);
7 | }
8 |
9 | contract AllowancesHelper {
10 | struct Allowance {
11 | address owner;
12 | address spender;
13 | uint256 amount;
14 | address token;
15 | }
16 |
17 | function allowances(
18 | address ownerAddress,
19 | address[] memory tokensAddresses,
20 | address[] memory spenderAddresses
21 | ) external view returns (Allowance[] memory) {
22 | uint256 spenderIdx;
23 | uint256 tokenIdx;
24 | uint256 numberOfAllowances;
25 |
26 | // Calculate number of allowances
27 | for (tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
28 | for (spenderIdx = 0; spenderIdx < spenderAddresses.length; spenderIdx++) {
29 | address tokenAddress = tokensAddresses[tokenIdx];
30 | address spenderAddress = spenderAddresses[spenderIdx];
31 | IERC20 token = IERC20(tokenAddress);
32 | uint256 amount = token.allowance(ownerAddress, spenderAddress);
33 | if (amount > 0) {
34 | numberOfAllowances++;
35 | }
36 | }
37 | }
38 |
39 | // Fetch allowances
40 | Allowance[] memory _allowances = new Allowance[](numberOfAllowances);
41 | uint256 allowanceIdx;
42 | for (tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
43 | for (spenderIdx = 0; spenderIdx < spenderAddresses.length; spenderIdx++) {
44 | address spenderAddress = spenderAddresses[spenderIdx];
45 | address tokenAddress = tokensAddresses[tokenIdx];
46 | IERC20 token = IERC20(tokenAddress);
47 | uint256 amount = token.allowance(ownerAddress, spenderAddress);
48 | if (amount > 0) {
49 | Allowance memory allowance = Allowance({
50 | owner: ownerAddress,
51 | spender: spenderAddress,
52 | amount: amount,
53 | token: tokenAddress
54 | });
55 | _allowances[allowanceIdx] = allowance;
56 | allowanceIdx++;
57 | }
58 | }
59 | }
60 | return _allowances;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Approvals.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.10;
4 |
5 | interface IERC20 {
6 | function allowance(address spender, address owner) external view returns (uint256);
7 | }
8 |
9 | contract AllowancesHelper {
10 | struct Allowance {
11 | address owner;
12 | address spender;
13 | uint256 amount;
14 | address token;
15 | }
16 |
17 | function allowances(
18 | address ownerAddress,
19 | address[] memory tokensAddresses,
20 | address[] memory spenderAddresses
21 | ) external view returns (Allowance[] memory) {
22 | uint256 spenderIdx;
23 | uint256 tokenIdx;
24 | uint256 numberOfAllowances;
25 |
26 | // Calculate number of allowances
27 | for (tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
28 | for (spenderIdx = 0; spenderIdx < spenderAddresses.length; spenderIdx++) {
29 | address tokenAddress = tokensAddresses[tokenIdx];
30 | address spenderAddress = spenderAddresses[spenderIdx];
31 | IERC20 token = IERC20(tokenAddress);
32 | uint256 amount = token.allowance(ownerAddress, spenderAddress);
33 | if (amount > 0) {
34 | numberOfAllowances++;
35 | }
36 | }
37 | }
38 |
39 | // Fetch allowances
40 | Allowance[] memory _allowances = new Allowance[](numberOfAllowances);
41 | uint256 allowanceIdx;
42 | for (tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
43 | for (spenderIdx = 0; spenderIdx < spenderAddresses.length; spenderIdx++) {
44 | address spenderAddress = spenderAddresses[spenderIdx];
45 | address tokenAddress = tokensAddresses[tokenIdx];
46 | IERC20 token = IERC20(tokenAddress);
47 | uint256 amount = token.allowance(ownerAddress, spenderAddress);
48 | if (amount > 0) {
49 | Allowance memory allowance = Allowance({
50 | owner: ownerAddress,
51 | spender: spenderAddress,
52 | amount: amount,
53 | token: tokenAddress
54 | });
55 | _allowances[allowanceIdx] = allowance;
56 | allowanceIdx++;
57 | }
58 | }
59 | }
60 | return _allowances;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/BalanceScanner.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.4;
4 |
5 | /**
6 | * @title An Ether or token balance scanner
7 | * @author Maarten Zuidhoorn
8 | * @author Luit Hollander
9 | */
10 | contract BalanceScanner {
11 | struct Result {
12 | bool success;
13 | bytes data;
14 | }
15 |
16 | /**
17 | * @notice Get the Ether balance for all addresses specified
18 | * @param addresses The addresses to get the Ether balance for
19 | * @return results The Ether balance for all addresses in the same order as specified
20 | */
21 | function etherBalances(address[] calldata addresses)
22 | external
23 | view
24 | returns (Result[] memory results)
25 | {
26 | results = new Result[](addresses.length);
27 |
28 | for (uint256 i = 0; i < addresses.length; i++) {
29 | results[i] = Result(true, abi.encode(addresses[i].balance));
30 | }
31 | }
32 |
33 | /**
34 | * @notice Get the ERC-20 token balance of `token` for all addresses specified
35 | * @dev This does not check if the `token` address specified is actually an ERC-20 token
36 | * @param addresses The addresses to get the token balance for
37 | * @param token The address of the ERC-20 token contract
38 | * @return results The token balance for all addresses in the same order as specified
39 | */
40 | function tokenBalances(address[] calldata addresses, address token)
41 | external
42 | view
43 | returns (Result[] memory results)
44 | {
45 | results = new Result[](addresses.length);
46 |
47 | for (uint256 i = 0; i < addresses.length; i++) {
48 | bytes memory data = abi.encodeWithSignature("balanceOf(address)", addresses[i]);
49 | results[i] = staticCall(token, data, 20000);
50 | }
51 | }
52 |
53 | /**
54 | * @notice Get the ERC-20 token balance from multiple contracts for a single owner
55 | * @param owner The address of the token owner
56 | * @param contracts The addresses of the ERC-20 token contracts
57 | * @return results The token balances in the same order as the addresses specified
58 | */
59 | function tokensBalance(address owner, address[] calldata contracts)
60 | external
61 | view
62 | returns (Result[] memory results)
63 | {
64 | results = new Result[](contracts.length);
65 |
66 | bytes memory data = abi.encodeWithSignature("balanceOf(address)", owner);
67 | for (uint256 i = 0; i < contracts.length; i++) {
68 | results[i] = staticCall(contracts[i], data, 20000);
69 | }
70 | }
71 |
72 | /**
73 | * @notice Call multiple contracts with the provided arbitrary data
74 | * @param contracts The contracts to call
75 | * @param data The data to call the contracts with
76 | * @return results The raw result of the contract calls
77 | */
78 | function call(address[] calldata contracts, bytes[] calldata data)
79 | external
80 | view
81 | returns (Result[] memory results)
82 | {
83 | return call(contracts, data, gasleft());
84 | }
85 |
86 | /**
87 | * @notice Call multiple contracts with the provided arbitrary data
88 | * @param contracts The contracts to call
89 | * @param data The data to call the contracts with
90 | * @param gas The amount of gas to call the contracts with
91 | * @return results The raw result of the contract calls
92 | */
93 | function call(
94 | address[] calldata contracts,
95 | bytes[] calldata data,
96 | uint256 gas
97 | ) public view returns (Result[] memory results) {
98 | require(contracts.length == data.length, "Length must be equal");
99 | results = new Result[](contracts.length);
100 |
101 | for (uint256 i = 0; i < contracts.length; i++) {
102 | results[i] = staticCall(contracts[i], data[i], gas);
103 | }
104 | }
105 |
106 | /**
107 | * @notice Static call a contract with the provided data
108 | * @param target The address of the contract to call
109 | * @param data The data to call the contract with
110 | * @param gas The amount of gas to forward to the call
111 | * @return result The result of the contract call
112 | */
113 | function staticCall(
114 | address target,
115 | bytes memory data,
116 | uint256 gas
117 | ) private view returns (Result memory) {
118 | uint256 size = codeSize(target);
119 |
120 | if (size > 0) {
121 | (bool success, bytes memory result) = target.staticcall{ gas: gas }(data);
122 | if (success) {
123 | return Result(success, result);
124 | }
125 | }
126 |
127 | return Result(false, "");
128 | }
129 |
130 | /**
131 | * @notice Get code size of address
132 | * @param _address The address to get code size from
133 | * @return size Unsigned 256-bits integer
134 | */
135 | function codeSize(address _address) private view returns (uint256 size) {
136 | // solhint-disable-next-line no-inline-assembly
137 | assembly {
138 | size := extcodesize(_address)
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/BalancesAndAllowancesHelper.sol:
--------------------------------------------------------------------------------
1 | /// SPDX-License-Identifier: MIT
2 | pragma solidity ^0.7.6;
3 |
4 | /// @author 0x66666600e43c6d9e1a249d29d58639dedfcd9ade
5 |
6 | interface IERC20 {
7 | function balanceOf(address addr) external view returns (uint256);
8 |
9 | function allowance(address owner, address spender) external view returns (uint256);
10 | }
11 |
12 | /// @title BalancesAndAllowancesHelper
13 | contract BalancesAndAllowancesHelper {
14 | uint256 private constant infinityAllowance = 1 << 248;
15 |
16 | function getBalances(IERC20[] calldata tokens, address wallet)
17 | external
18 | view
19 | returns (uint256[] memory indexAndBalance)
20 | {
21 | uint256 counter = 0;
22 | uint256[] memory tmp = new uint256[](tokens.length);
23 | for (uint256 i = 0; i < tokens.length; i++) {
24 | try tokens[i].balanceOf(wallet) returns (uint256 balance) {
25 | if (balance == 0) {
26 | continue;
27 | }
28 |
29 | tmp[counter] = (i << 248) | balance;
30 | counter++;
31 | } catch {}
32 | }
33 |
34 | indexAndBalance = new uint256[](counter);
35 | for (uint256 i = 0; i < counter; i++) {
36 | indexAndBalance[i] = tmp[i];
37 | }
38 | }
39 |
40 | function getAllowances(
41 | IERC20[] calldata tokens,
42 | address owner,
43 | address spender
44 | ) external view returns (uint256[] memory indexAndAllowance) {
45 | uint256 counter = 0;
46 | uint256[] memory tmp = new uint256[](tokens.length);
47 | for (uint256 i = 0; i < tokens.length; i++) {
48 | try tokens[i].allowance(owner, spender) returns (uint256 allowance) {
49 | if (allowance == 0) {
50 | continue;
51 | }
52 |
53 | if (allowance > infinityAllowance) {
54 | tmp[counter] = (i << 248) | (1 << 247);
55 | } else {
56 | tmp[counter] = (i << 248) | allowance;
57 | }
58 | counter++;
59 | } catch {}
60 | }
61 |
62 | indexAndAllowance = new uint256[](counter);
63 | for (uint256 i = 0; i < counter; i++) {
64 | indexAndAllowance[i] = tmp[i];
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/BytesParsing.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 |
3 | pragma solidity >=0.8.5 <0.9.0;
4 |
5 | contract BytesParsing {
6 | // example bytes calldata: 0x3A1174741911F257FFCA965A00000002010000
7 | // color codes: #3a1174, #741911, #f257ff, #ca965a, #000000
8 | // numbers: 2, 1, 0, 0
9 |
10 | function uint8tohexchar(uint8 i) internal pure returns (uint8) {
11 | return (i > 9) ?
12 | (i + 87) : // ascii a-f
13 | (i + 48); // ascii 0-9
14 | }
15 |
16 | function bytes3tohexstr(bytes3 c) internal pure returns (string memory) {
17 | uint24 i = uint24(c);
18 | bytes memory o = new bytes(6);
19 | uint24 mask = 0x00000f;
20 | o[5] = bytes1(uint8tohexchar(uint8(i & mask)));
21 | i = i >> 4;
22 | o[4] = bytes1(uint8tohexchar(uint8(i & mask)));
23 | i = i >> 4;
24 | o[3] = bytes1(uint8tohexchar(uint8(i & mask)));
25 | i = i >> 4;
26 | o[2] = bytes1(uint8tohexchar(uint8(i & mask)));
27 | i = i >> 4;
28 | o[1] = bytes1(uint8tohexchar(uint8(i & mask)));
29 | i = i >> 4;
30 | o[0] = bytes1(uint8tohexchar(uint8(i & mask)));
31 | return string(o);
32 | }
33 |
34 | function sliceBytes1(bytes calldata features) public pure returns(string memory) {
35 | bytes3 c = bytes3(features[0:3]);
36 | return bytes3tohexstr(c);
37 | }
38 |
39 | function sliceBytes2(bytes calldata features) public pure returns(string memory) {
40 | bytes3 c = bytes3(features[3:6]);
41 | return bytes3tohexstr(c);
42 | }
43 |
44 | function sliceBytes3(bytes calldata features) public pure returns(string memory) {
45 | bytes3 c = bytes3(features[6:9]);
46 | return bytes3tohexstr(c);
47 | }
48 |
49 | function sliceBytes4(bytes calldata features) public pure returns(string memory) {
50 | bytes3 c = bytes3(features[9:12]);
51 | return bytes3tohexstr(c);
52 | }
53 |
54 | function sliceBytes5(bytes calldata features) public pure returns(string memory) {
55 | bytes3 c = bytes3(features[12:15]);
56 | return bytes3tohexstr(c);
57 | }
58 |
59 | function sliceBytes6(bytes calldata features) public pure returns(uint8) {
60 | bytes1 a = bytes1(features[15:16]);
61 | return uint8(a);
62 | }
63 |
64 | function sliceBytes7(bytes calldata features) public pure returns(uint8) {
65 | bytes1 a = bytes1(features[16:17]);
66 | return uint8(a);
67 | }
68 |
69 | function sliceBytes8(bytes calldata features) public pure returns(uint8) {
70 | bytes1 a = bytes1(features[17:18]);
71 | return uint8(a);
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/CallFunctionWithoutContract.sol:
--------------------------------------------------------------------------------
1 | /// SPDX-License-Identifier: MIT OR APACHE-2.0
2 | pragma solidity ^0.8.7;
3 |
4 | // source: twitter.com/PatrickAlphaC/status/1517156283215802368
5 |
6 | /// @title CallFunctionWithoutContract
7 | contract CallFunctionWithoutContract {
8 | address public s_selectorsAndSignaturesAddress;
9 |
10 | constructor(address selectorsAndSignaturesAddress) {
11 | s_selectorsAndSignaturesAddress = selectorsAndSignaturesAddress;
12 | }
13 |
14 | // you could use this to change state
15 | function callFunctionDirectly(bytes calldata calldata) public returns (bytes4, bool) {
16 | (bool success, bytes memory returnData) = s_selectorsAndSignaturesAddress.call(
17 | abi.encodeWithSignature("getSelectorThree(bytes)", callData)
18 | );
19 | return (bytes4(returnData), success);
20 | }
21 |
22 | // with a staticcall, we can have this be a view function!
23 | function staticCallFunctionDirectly() public view returns (bytes4, bool) {
24 | (bool success, bytes memory returnData) = s_selectorsAndSignaturesAddress.staticcalll(
25 | abi.encodeWithSignature("getSelectorone()")
26 | );
27 | return (bytes4(returnData), success);
28 | }
29 |
30 | function callTransferFunctionDirectly(address someAddress, uint256 amount)
31 | public
32 | returns (bytes4, booll)
33 | {
34 | (bool success, bytes memory returnData) = s_selectorsAndSignaturesAddress.call(
35 | abi.encodeWithSignature("transfer(address, uint256)", someAddress, amount)
36 | );
37 | return (bytes4(returnData), success);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/CalldataUtils.lib.sol:
--------------------------------------------------------------------------------
1 | /// SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.7 <0.9.0;
3 |
4 | // Gas efficient calldata parser
5 | // https://github.com/ethereum/solidity/issues/9439#issuecomment-660134770
6 |
7 | library CalldataUtils {
8 | function getSig(bytes memory _data) internal pure returns (bytes4 sig) {
9 | assembly {
10 | sig := mload(add(_data, 32))
11 | }
12 | }
13 |
14 | function slice(
15 | bytes memory _bytes,
16 | uint256 _start,
17 | uint256 _length
18 | ) internal pure returns (bytes memory) {
19 | require(_bytes.length >= (_start + _length));
20 |
21 | bytes memory tempBytes;
22 |
23 | assembly {
24 | switch iszero(_length)
25 | case 0 {
26 | // Get a location of some free memory and store it in tempBytes as
27 | // Solidity does for memory variables.
28 | tempBytes := mload(0x40)
29 |
30 | // The first word of the slice result is potentially a partial
31 | // word read from the original array. To read it, we calculate
32 | // the length of that partial word and start copying that many
33 | // bytes into the array. The first word we copy will start with
34 | // data we don't care about, but the last `lengthmod` bytes will
35 | // land at the beginning of the contents of the new array. When
36 | // we're done copying, we overwrite the full first word with
37 | // the actual length of the slice.
38 | let lengthmod := and(_length, 31)
39 |
40 | // The multiplication in the next line is necessary
41 | // because when slicing multiples of 32 bytes (lengthmod == 0)
42 | // the following copy loop was copying the origin's length
43 | // and then ending prematurely not copying everything it should.
44 | let mc := add(
45 | add(tempBytes, lengthmod),
46 | mul(0x20, iszero(lengthmod))
47 | )
48 | let end := add(mc, _length)
49 |
50 | for {
51 | // The multiplication in the next line has the same exact purpose
52 | // as the one above.
53 | let cc := add(
54 | add(
55 | add(_bytes, lengthmod),
56 | mul(0x20, iszero(lengthmod))
57 | ),
58 | _start
59 | )
60 | } lt(mc, end) {
61 | mc := add(mc, 0x20)
62 | cc := add(cc, 0x20)
63 | } {
64 | mstore(mc, mload(cc))
65 | }
66 |
67 | mstore(tempBytes, _length)
68 |
69 | //update free-memory pointer
70 | //allocating the array padded to 32 bytes like the compiler does now
71 | mstore(0x40, and(add(mc, 31), not(31)))
72 | }
73 | //if we want a zero-length slice let's just return a zero-length array
74 | default {
75 | tempBytes := mload(0x40)
76 |
77 | mstore(0x40, add(tempBytes, 0x20))
78 | }
79 | }
80 |
81 | return tempBytes;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/CodeHashCache.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.4;
3 |
4 | /**
5 | * @title CodeHashCache
6 | * @author 0age
7 | * @notice This contract allows callers to register the runtime code hash of any
8 | * contract that is currently deployed with runtime code. It then allows callers
9 | * to determine whether or not the runtime code hash of any registered contract
10 | * has been altered since it was initially registered. A critical consideration
11 | * to bear in mind is that registered contracts can still be destroyed and later
12 | * redeployed with the same runtime code - their runtime code hash will be the
13 | * same, but their creation code may differ, and their contract storage will be
14 | * completely wiped upon destruction.
15 | */
16 | contract CodeHashCache {
17 | // Maintain a mapping of runtime code hashes of deployed contracts.
18 | mapping(address => bytes32) private _cachedHashes;
19 |
20 | /**
21 | * @notice Register a target contract's current runtime code hash. This call
22 | * will revert if the supplied target contract has already been registered or
23 | * does not currently have any runtime code.
24 | * @param target address The contract to retrieve and store the runtime code
25 | * hash for.
26 | */
27 | function registerCodeHash(address target) external {
28 | // Ensure that the target contract has not already had a hash registered.
29 | require(_cachedHashes[target] == bytes32(0), "Target already registered.");
30 |
31 | // Ensure that the target contract currently has runtime code.
32 | uint256 currentCodeSize;
33 | assembly {
34 | currentCodeSize := extcodesize(target)
35 | }
36 | require(currentCodeSize > 0, "Target currently has no runtime code.");
37 |
38 | // Retrieve the current runtime code hash of the target contract.
39 | bytes32 currentCodeHash;
40 | assembly {
41 | currentCodeHash := extcodehash(target)
42 | }
43 |
44 | // Register the runtime code hash for the target contract.
45 | _cachedHashes[target] = currentCodeHash;
46 | }
47 |
48 | /**
49 | * @notice View function to determine if the current runtime code hash of a
50 | * target contract matches the registered runtime code hash for the target
51 | * contract. Reverts if no runtime code hash has been registered yet for the
52 | * target contract.
53 | * @param target address The contract to retrieve the runtime code hash for,
54 | * which will be compared against the runtime code hash that was initially
55 | * registered for that contract.
56 | * @return codeHashMatchesRegisteredCodeHash - a boolean signifying that the target contract's runtime code has
57 | * not been altered since it was initially registered.
58 | */
59 | function matchesRegisteredCodeHash(address target)
60 | external
61 | view
62 | returns (bool codeHashMatchesRegisteredCodeHash)
63 | {
64 | // Get the runtime code hash that is currently registered for the target.
65 | bytes32 cachedCodeHash = _cachedHashes[target];
66 |
67 | // Ensure that the target contract has already had a code hash registered.
68 | require(cachedCodeHash != bytes32(0), "Target not yet registered.");
69 |
70 | // Retrieve the current runtime code hash of the target contract.
71 | bytes32 currentCodeHash;
72 | assembly {
73 | currentCodeHash := extcodehash(target)
74 | }
75 |
76 | // Compare current runtime code hash to registered runtime code hash.
77 | codeHashMatchesRegisteredCodeHash = currentCodeHash == cachedCodeHash;
78 | }
79 |
80 | /**
81 | * @notice View function to retrieve the runtime code hash registered for the
82 | * target contract. Returns bytes32(0) if there is no runtime code hash
83 | * registered for the target.
84 | * @param target address The contract to retrieve the registered runtime code
85 | * hash for.
86 | * @return registeredCodeHash - the runtime code hash registered for the target contract. Returns
87 | * bytes32(0) if there runtime code hash has been registered for the target.
88 | */
89 | function getRegisteredCodeHash(address target)
90 | external
91 | view
92 | returns (bytes32 registeredCodeHash)
93 | {
94 | // Get the runtime code hash that is currently registered for the target.
95 | registeredCodeHash = _cachedHashes[target];
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/CounterfactualFactory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | /******************************************************************************
4 | * Copyright 2020 IEXEC BLOCKCHAIN TECH *
5 | * *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); *
7 | * you may not use this file except in compliance with the License. *
8 | * You may obtain a copy of the License at *
9 | * *
10 | * http://www.apache.org/licenses/LICENSE-2.0 *
11 | * *
12 | * Unless required by applicable law or agreed to in writing, software *
13 | * distributed under the License is distributed on an "AS IS" BASIS, *
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15 | * See the License for the specific language governing permissions and *
16 | * limitations under the License. *
17 | ******************************************************************************/
18 |
19 | pragma solidity ^0.6.0;
20 |
21 | contract CounterfactualFactory {
22 | function _create2(bytes memory _code, bytes32 _salt) internal returns (address) {
23 | bytes memory code = _code;
24 | bytes32 salt = _salt;
25 | address addr;
26 | // solium-disable-next-line security/no-inline-assembly
27 | assembly {
28 | addr := create2(0, add(code, 0x20), mload(code), salt)
29 | if iszero(extcodesize(addr)) {
30 | revert(0, 0)
31 | }
32 | }
33 | return addr;
34 | }
35 |
36 | function _predictAddress(bytes memory _code, bytes32 _salt) internal view returns (address) {
37 | return
38 | address(
39 | bytes20(
40 | keccak256(
41 | abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(_code))
42 | ) << 0x60
43 | )
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/ERC20InvalidMock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.4;
4 |
5 | import "./ERC20Mock.sol";
6 |
7 | contract ERC20InvalidMock is ERC20Mock {
8 | address public sender;
9 |
10 | constructor(address initialAccount, uint256 initialBalance)
11 | payable
12 | ERC20Mock(initialAccount, initialBalance)
13 | {
14 | // noop
15 | }
16 |
17 | /**
18 | * @dev This function is not ERC-20 compliant and will cause STATICCALLs to fail. It exists for testing purposes only.
19 | */
20 | function balanceOf(address account) public override returns (uint256) {
21 | setSender(msg.sender);
22 | return super.balanceOf(account);
23 | }
24 |
25 | function setSender(address _sender) private {
26 | sender = _sender;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/ERC20Mock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.4;
4 |
5 | contract ERC20Mock {
6 | mapping(address => uint256) private balances;
7 |
8 | constructor(address initialAccount, uint256 initialBalance) {
9 | mint(initialAccount, initialBalance);
10 | }
11 |
12 | /**
13 | *
14 | * @dev The `balanceOf` function should be a view function, but for testing purposes, this function is not.
15 | * This makes it easy to test non-compliant ERC-20 tokens.
16 | */
17 | function balanceOf(address account) public virtual returns (uint256) {
18 | return balances[account];
19 | }
20 |
21 | function mint(address account, uint256 amount) public {
22 | balances[account] += amount;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/ERCQuery.sol:
--------------------------------------------------------------------------------
1 | /**
2 | *Submitted for verification at Etherscan.io on 2021-01-12
3 | */
4 |
5 | pragma abicoder v2;
6 | pragma solidity ^0.7.6;
7 |
8 | interface IERC20String {
9 | function symbol() external view returns (string memory);
10 |
11 | function name() external view returns (string memory);
12 | }
13 |
14 | interface IERC20Bytes32 {
15 | function symbol() external view returns (bytes32);
16 |
17 | function name() external view returns (bytes32);
18 | }
19 |
20 | contract TokenInfo {
21 | struct Info {
22 | string symbol;
23 | string name;
24 | }
25 |
26 | function getInfoBatch(address[] memory tokens) external view returns (Info[] memory infos) {
27 | Info[] memory infos = new Info[](tokens.length);
28 | for (uint8 i = 0; i < tokens.length; i++) {
29 | Info memory info;
30 | infos[i] = this.getInfo(tokens[i]);
31 | }
32 | return infos;
33 | }
34 |
35 | function getInfo(address token) external view returns (Info memory info) {
36 | // Does code exists for the token?
37 | uint32 size;
38 | assembly {
39 | size := extcodesize(token)
40 | }
41 | if (size == 0) {
42 | return info;
43 | }
44 |
45 | try this.getStringProperties(token) returns (string memory _symbol, string memory _name) {
46 | info.symbol = _symbol;
47 | info.name = _name;
48 | return info;
49 | } catch {}
50 | try this.getBytes32Properties(token) returns (string memory _symbol, string memory _name) {
51 | info.symbol = _symbol;
52 | info.name = _name;
53 | return info;
54 | } catch {}
55 | }
56 |
57 | function getStringProperties(address token)
58 | external
59 | view
60 | returns (string memory symbol, string memory name)
61 | {
62 | symbol = IERC20String(token).symbol();
63 | name = IERC20String(token).name();
64 | }
65 |
66 | function getBytes32Properties(address token)
67 | external
68 | view
69 | returns (string memory symbol, string memory name)
70 | {
71 | bytes32 symbolBytes32 = IERC20Bytes32(token).symbol();
72 | bytes32 nameBytes32 = IERC20Bytes32(token).name();
73 | symbol = bytes32ToString(symbolBytes32);
74 | name = bytes32ToString(nameBytes32);
75 | }
76 |
77 | function bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) {
78 | uint8 i = 0;
79 | while (i < 32 && _bytes32[i] != 0) {
80 | i++;
81 | }
82 | bytes memory bytesArray = new bytes(i);
83 | for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
84 | bytesArray[i] = _bytes32[i];
85 | }
86 | return string(bytesArray);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/EthHelper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.4;
3 |
4 | // @author 0age
5 | contract EthHelper {
6 | function hasCode(address addr) external view returns (bool) {
7 | uint256 size;
8 | assembly {
9 | size := extcodesize(addr)
10 | }
11 | return size > 0;
12 | }
13 |
14 | function codeSize(address addr) external view returns (uint256) {
15 | uint256 size;
16 | assembly {
17 | size := extcodesize(addr)
18 | }
19 | return size;
20 | }
21 |
22 | function code(address addr) external view returns (bytes memory) {
23 | return addr.code;
24 | }
25 |
26 | function codeHash(address addr) external view returns (bytes32) {
27 | return addr.codehash;
28 | }
29 |
30 | function balance(address addr) external view returns (uint256) {
31 | return addr.balance;
32 | }
33 |
34 | function txOrigin() external view returns (address) {
35 | return tx.origin;
36 | }
37 |
38 | function txGasPrice() external view returns (uint256) {
39 | return tx.gasprice;
40 | }
41 |
42 | function chainId() external view returns (uint256) {
43 | return block.chainid;
44 | }
45 |
46 | function blockCoinbase() external view returns (address) {
47 | return block.coinbase;
48 | }
49 |
50 | function blockDifficulty() external view returns (uint256) {
51 | return block.difficulty;
52 | }
53 |
54 | function blockGaslimit() external view returns (uint256) {
55 | return block.gaslimit;
56 | }
57 |
58 | function blockNumber() external view returns (uint256) {
59 | return block.number;
60 | }
61 |
62 | function blockTimestamp() external view returns (uint256) {
63 | return block.timestamp;
64 | }
65 |
66 | function blockHashAt(uint256 blockNumber) external view returns (bytes32) {
67 | uint256 blocksAgo = block.number - blockNumber;
68 | require(
69 | blocksAgo > 0 && blocksAgo <= 256,
70 | "EthHelper#blockHashAt: Invalid blockNumber value"
71 | );
72 | return blockhash(blockNumber);
73 | }
74 |
75 | function blockHashFrom(uint256 blocksAgo) external view returns (bytes32) {
76 | require(
77 | blocksAgo > 0 && blocksAgo <= 256,
78 | "EthHelper#blockHashFrom: Invalid blocksAgo value"
79 | );
80 | return blockhash(block.number - blocksAgo);
81 | }
82 |
83 | function gasleft() external view returns (uint256) {
84 | // Note: does not correct for 63/64ths rule, only useful as an approximation
85 | return gasleft();
86 | }
87 |
88 | function not(bool a) external pure returns (bool) {
89 | return !a;
90 | }
91 |
92 | function and(bool a, bool b) external pure returns (bool) {
93 | return a && b;
94 | }
95 |
96 | function or(bool a, bool b) external pure returns (bool) {
97 | return a || b;
98 | }
99 |
100 | function add(uint256 a, uint256 b) external pure returns (uint256) {
101 | return a + b;
102 | }
103 |
104 | function sub(uint256 a, uint256 b) external pure returns (uint256) {
105 | return a - b;
106 | }
107 |
108 | function mul(uint256 a, uint256 b) external pure returns (uint256) {
109 | return a * b;
110 | }
111 |
112 | function div(uint256 a, uint256 b) external pure returns (uint256) {
113 | return a / b;
114 | }
115 |
116 | function mod(uint256 a, uint256 b) external pure returns (uint256) {
117 | return a % b;
118 | }
119 |
120 | function exp(uint256 a, uint256 b) external pure returns (uint256) {
121 | return a**b;
122 | }
123 |
124 | function addmod(
125 | uint256 x,
126 | uint256 y,
127 | uint256 k
128 | ) external pure returns (uint256) {
129 | return addmod(x, y, k);
130 | }
131 |
132 | function mulmod(
133 | uint256 x,
134 | uint256 y,
135 | uint256 k
136 | ) external pure returns (uint256) {
137 | return mulmod(x, y, k);
138 | }
139 |
140 | function and(uint256 a, uint256 b) external pure returns (uint256) {
141 | return a & b;
142 | }
143 |
144 | function or(uint256 a, uint256 b) external pure returns (uint256) {
145 | return a | b;
146 | }
147 |
148 | function xor(uint256 a, uint256 b) external pure returns (uint256) {
149 | return a ^ b;
150 | }
151 |
152 | function bitwiseNot(uint256 a) external pure returns (uint256) {
153 | return ~a;
154 | }
155 |
156 | function shl(uint256 a, uint256 b) external pure returns (uint256) {
157 | return a << b;
158 | }
159 |
160 | function shr(uint256 a, uint256 b) external pure returns (uint256) {
161 | return a >> b;
162 | }
163 |
164 | function lt(uint256 a, uint256 b) external pure returns (bool) {
165 | return a < b;
166 | }
167 |
168 | function lte(uint256 a, uint256 b) external pure returns (bool) {
169 | return a <= b;
170 | }
171 |
172 | function gt(uint256 a, uint256 b) external pure returns (bool) {
173 | return a > b;
174 | }
175 |
176 | function gte(uint256 a, uint256 b) external pure returns (bool) {
177 | return a >= b;
178 | }
179 |
180 | function eq(uint256 a, uint256 b) external pure returns (bool) {
181 | return a == b;
182 | }
183 |
184 | function ne(uint256 a, uint256 b) external pure returns (bool) {
185 | return a != b;
186 | }
187 |
188 | function and(bytes32 a, bytes32 b) external pure returns (bytes32) {
189 | return a & b;
190 | }
191 |
192 | function or(bytes32 a, bytes32 b) external pure returns (bytes32) {
193 | return a | b;
194 | }
195 |
196 | function xor(bytes32 a, bytes32 b) external pure returns (bytes32) {
197 | return a ^ b;
198 | }
199 |
200 | function bitwiseNot(bytes32 a) external pure returns (bytes32) {
201 | return ~a;
202 | }
203 |
204 | function shl(bytes32 a, uint256 b) external pure returns (bytes32) {
205 | return a << b;
206 | }
207 |
208 | function shr(bytes32 a, uint256 b) external pure returns (bytes32) {
209 | return a >> b;
210 | }
211 |
212 | function lt(bytes32 a, bytes32 b) external pure returns (bool) {
213 | return a < b;
214 | }
215 |
216 | function lte(bytes32 a, bytes32 b) external pure returns (bool) {
217 | return a <= b;
218 | }
219 |
220 | function gt(bytes32 a, bytes32 b) external pure returns (bool) {
221 | return a > b;
222 | }
223 |
224 | function gte(bytes32 a, bytes32 b) external pure returns (bool) {
225 | return a >= b;
226 | }
227 |
228 | function eq(bytes32 a, bytes32 b) external pure returns (bool) {
229 | return a == b;
230 | }
231 |
232 | function ne(bytes32 a, bytes32 b) external pure returns (bool) {
233 | return a != b;
234 | }
235 |
236 | function lt(address a, address b) external pure returns (bool) {
237 | return a < b;
238 | }
239 |
240 | function lte(address a, address b) external pure returns (bool) {
241 | return a <= b;
242 | }
243 |
244 | function gt(address a, address b) external pure returns (bool) {
245 | return a > b;
246 | }
247 |
248 | function gte(address a, address b) external pure returns (bool) {
249 | return a >= b;
250 | }
251 |
252 | function eq(address a, address b) external pure returns (bool) {
253 | return a == b;
254 | }
255 |
256 | function ne(address a, address b) external pure returns (bool) {
257 | return a != b;
258 | }
259 |
260 | function isCaller(address addr) external view returns (bool) {
261 | return msg.sender == addr;
262 | }
263 |
264 | function eq(bytes calldata a, bytes calldata b) external pure returns (bool) {
265 | return keccak256(a) == keccak256(b);
266 | }
267 |
268 | function ne(bytes calldata a, bytes calldata b) external pure returns (bool) {
269 | return keccak256(a) != keccak256(b);
270 | }
271 |
272 | function eq(string calldata a, string calldata b) external pure returns (bool) {
273 | return keccak256(bytes(a)) == keccak256(bytes(b));
274 | }
275 |
276 | function ne(string calldata a, string calldata b) external pure returns (bool) {
277 | return keccak256(bytes(a)) != keccak256(bytes(b));
278 | }
279 |
280 | function keccak256(bytes calldata data) external pure returns (bytes32) {
281 | return keccak256(data);
282 | }
283 |
284 | function ripemd160(bytes calldata data) external pure returns (bytes32) {
285 | return ripemd160(data);
286 | }
287 |
288 | function sha256(bytes calldata data) external pure returns (bytes32) {
289 | return sha256(data);
290 | }
291 |
292 | function ecrecover(
293 | bytes32 hash,
294 | uint8 v,
295 | bytes32 r,
296 | bytes32 s
297 | ) external pure returns (address) {
298 | return ecrecover(hash, v, r, s);
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/src/GasMeterFactory.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0 <0.9.0;
2 | import "ds-test/test.sol";
3 |
4 | // ------------------------------------------------------------------
5 | // Test Harness
6 | // ------------------------------------------------------------------
7 | contract Hevm {
8 | function warp(uint) public;
9 | }
10 | contract GasMeterFactory {
11 | /*
12 | A GasMeter is a contract that returns the exact value of the gas provided
13 | in any call to it. It must be writtten in bytecode directly as the solidity
14 | function dispatch code already consumes gas, so any solidity implementation
15 | will always return an incorrect result.
16 | Bytecode:
17 | ---
18 | 0x5a 0x60 0x02 0x01 0x60 0x00 0x52 0x60 0x20 0x60 0x00 0xf3
19 | GAS PUSH1 2 ADD PUSH1 0 MSTORE PUSH1 32 PUSH1 0 RETURN
20 | Initcode:
21 | ---
22 | 0x6b 0x5a 0x60 0x02 0x01 0x60 0x00 0x52 0x60 0x20 0x60 0x20 0x60 0x00 0xf3
23 | PUSH12
24 | 0x60 0x00 0x52 0x60 0x20 0x60 0x14 0xf3
25 | PUSH1 0 MSTORE PUSH1 32 PUSH1 20 RETURN
26 | */
27 |
28 | function build() public returns (address out) {
29 | assembly {
30 | mstore(mload(0x40), 0x6b5a60020160005260206000f360005260206014f3)
31 | out := create(0, add(11, mload(0x40)), 21)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/GenericFactory.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.6.0;
2 |
3 | import "./CounterfactualFactory.sol";
4 |
5 | contract GenericFactory is CounterfactualFactory {
6 | event NewContract(address indexed addr);
7 |
8 | function predictAddress(bytes memory _code, bytes32 _salt) public view returns (address) {
9 | return predictAddressWithCall(_code, _salt, bytes(""));
10 | }
11 |
12 | function createContract(bytes memory _code, bytes32 _salt) public returns (address) {
13 | return createContractAndCall(_code, _salt, bytes(""));
14 | }
15 |
16 | function predictAddressWithCall(
17 | bytes memory _code,
18 | bytes32 _salt,
19 | bytes memory _call
20 | ) public view returns (address) {
21 | return _predictAddress(_code, keccak256(abi.encodePacked(_salt, _call)));
22 | }
23 |
24 | function createContractAndCall(
25 | bytes memory _code,
26 | bytes32 _salt,
27 | bytes memory _call
28 | ) public returns (address) {
29 | address addr = _create2(_code, keccak256(abi.encodePacked(_salt, _call)));
30 | emit NewContract(addr);
31 | if (_call.length > 0) {
32 | // solium-disable-next-line security/no-low-level-calls
33 | (bool success, bytes memory reason) = addr.call(_call);
34 | require(success, string(reason));
35 | }
36 | return addr;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Helpers.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.8;
2 |
3 | contract Helpers {
4 | // slice string
5 | function getSlice(uint256 begin, uint256 end, string memory text) internal pure returns (string memory) {
6 | bytes memory a = new bytes(end-begin+1);
7 | for(uint i=0;i<=end-begin;i++){
8 | a[i] = bytes(text)[i+begin-1];
9 | }
10 | return string(a);
11 | }
12 |
13 | // convert string to uint8 (numbers 0-9)
14 | function stringToUint8(string memory numString) public pure returns(uint8) {
15 | return uint8(bytes(numString)[0])-48;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/ImmutableCreate2Factory.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.15;
3 |
4 | /**
5 | * @title Immutable Create2 Contract Factory
6 | * @author 0age
7 | * @notice This contract provides a safeCreate2 function that takes a salt value
8 | * and a block of initialization code as arguments and passes them into inline
9 | * assembly. The contract prevents redeploys by maintaining a mapping of all
10 | * contracts that have already been deployed, and prevents frontrunning or other
11 | * collisions by requiring that the first 20 bytes of the salt are equal to the
12 | * address of the caller (this can be bypassed by setting the first 20 bytes to
13 | * the null address). There is also a view function that computes the address of
14 | * the contract that will be created when submitting a given salt or nonce along
15 | * with a given block of initialization code.
16 | * @dev This contract has not yet been fully tested or audited - proceed with
17 | * caution and please share any exploits or optimizations you discover.
18 | */
19 | contract ImmutableCreate2Factory {
20 | // mapping to track which addresses have already been deployed.
21 | mapping(address => bool) private _deployed;
22 |
23 | /**
24 | * @dev Create a contract using CREATE2 by submitting a given salt or nonce
25 | * along with the initialization code for the contract. Note that the first 20
26 | * bytes of the salt must match those of the calling address, which prevents
27 | * contract creation events from being submitted by unintended parties.
28 | * @param salt bytes32 The nonce that will be passed into the CREATE2 call.
29 | * @param initializationCode bytes The initialization code that will be passed
30 | * into the CREATE2 call.
31 | * @return deploymentAddress - address of the contract that will be created,
32 | * or the null address if a contract already exists at that address.
33 | */
34 | function safeCreate2(bytes32 salt, bytes calldata initializationCode)
35 | external
36 | payable
37 | containsCaller(salt)
38 | returns (address deploymentAddress)
39 | {
40 | // move the initialization code from calldata to memory.
41 | bytes memory initCode = initializationCode;
42 |
43 | // determine the target address for contract deployment.
44 | address targetDeploymentAddress = address(
45 | uint160( // downcast to match the address type.
46 | uint256( // convert to uint to truncate upper digits.
47 | keccak256( // compute the CREATE2 hash using 4 inputs.
48 | abi.encodePacked( // pack all inputs to the hash together.
49 | hex"ff", // start with 0xff to distinguish from RLP.
50 | address(this), // this contract will be the caller.
51 | salt, // pass in the supplied salt value.
52 | keccak256(abi.encodePacked(initCode)) // pass in the hash of initialization code.
53 | )
54 | )
55 | )
56 | )
57 | );
58 |
59 | // ensure that a contract hasn't been previously deployed to target address.
60 | require(
61 | !_deployed[targetDeploymentAddress],
62 | "Invalid contract creation - contract has already been deployed."
63 | );
64 |
65 | // using inline assembly: load data and length of data, then call CREATE2.
66 | assembly {
67 | // solhint-disable-line
68 | let encoded_data := add(0x20, initCode) // load initialization code.
69 | let encoded_size := mload(initCode) // load the init code's length.
70 | deploymentAddress := create2(
71 | // call CREATE2 with 4 arguments.
72 | callvalue(), // forward any attached value.
73 | encoded_data, // pass in initialization code.
74 | encoded_size, // pass in init code's length.
75 | salt // pass in the salt value.
76 | )
77 | }
78 |
79 | // check address against target to ensure that deployment was successful.
80 | require(
81 | deploymentAddress == targetDeploymentAddress,
82 | "Failed to deploy contract using provided salt and initialization code."
83 | );
84 |
85 | // record the deployment of the contract to prevent redeploys.
86 | _deployed[deploymentAddress] = true;
87 | }
88 |
89 | /**
90 | * @dev Compute the address of the contract that will be created when
91 | * submitting a given salt or nonce to the contract along with the contract's
92 | * initialization code. The CREATE2 address is computed in accordance with
93 | * EIP-1014, and adheres to the formula therein of
94 | * `keccak256( 0xff ++ address ++ salt ++ keccak256(init_code)))[12:]` when
95 | * performing the computation. The computed address is then checked for any
96 | * existing contract code - if so, the null address will be returned instead.
97 | * @param salt bytes32 The nonce passed into the CREATE2 address calculation.
98 | * @param initCode bytes The contract initialization code to be used.
99 | * that will be passed into the CREATE2 address calculation.
100 | * @return deploymentAddress - address of the contract that will be created,
101 | * or the null address if a contract has already been deployed to that address.
102 | */
103 | function findCreate2Address(bytes32 salt, bytes calldata initCode)
104 | external
105 | view
106 | returns (address deploymentAddress)
107 | {
108 | // determine the address where the contract will be deployed.
109 | deploymentAddress = address(
110 | uint160( // downcast to match the address type.
111 | uint256( // convert to uint to truncate upper digits.
112 | keccak256( // compute the CREATE2 hash using 4 inputs.
113 | abi.encodePacked( // pack all inputs to the hash together.
114 | hex"ff", // start with 0xff to distinguish from RLP.
115 | address(this), // this contract will be the caller.
116 | salt, // pass in the supplied salt value.
117 | keccak256(abi.encodePacked(initCode)) // pass in the hash of initialization code.
118 | )
119 | )
120 | )
121 | )
122 | );
123 |
124 | // return null address to signify failure if contract has been deployed.
125 | if (_deployed[deploymentAddress]) {
126 | return address(0);
127 | }
128 | }
129 |
130 | /**
131 | * @dev Compute the address of the contract that will be created when
132 | * submitting a given salt or nonce to the contract along with the keccak256
133 | * hash of the contract's initialization code. The CREATE2 address is computed
134 | * in accordance with EIP-1014, and adheres to the formula therein of
135 | * `keccak256( 0xff ++ address ++ salt ++ keccak256(init_code)))[12:]` when
136 | * performing the computation. The computed address is then checked for any
137 | * existing contract code - if so, the null address will be returned instead.
138 | * @param salt bytes32 The nonce passed into the CREATE2 address calculation.
139 | * @param initCodeHash bytes32 The keccak256 hash of the initialization code
140 | * that will be passed into the CREATE2 address calculation.
141 | * @return deploymentAddress - address of the contract that will be created,
142 | * or the null address if a contract has already been deployed to that address.
143 | */
144 | function findCreate2AddressViaHash(bytes32 salt, bytes32 initCodeHash)
145 | external
146 | view
147 | returns (address deploymentAddress)
148 | {
149 | // determine the address where the contract will be deployed.
150 | deploymentAddress = address(
151 | uint160( // downcast to match the address type.
152 | uint256( // convert to uint to truncate upper digits.
153 | keccak256( // compute the CREATE2 hash using 4 inputs.
154 | abi.encodePacked( // pack all inputs to the hash together.
155 | hex"ff", // start with 0xff to distinguish from RLP.
156 | address(this), // this contract will be the caller.
157 | salt, // pass in the supplied salt value.
158 | initCodeHash // pass in the hash of initialization code.
159 | )
160 | )
161 | )
162 | )
163 | );
164 |
165 | // return null address to signify failure if contract has been deployed.
166 | if (_deployed[deploymentAddress]) {
167 | return address(0);
168 | }
169 | }
170 |
171 | /**
172 | * @dev Determine if a contract has already been deployed by the factory to a
173 | * given address.
174 | * @param deploymentAddress address The contract address to check.
175 | * @return True if the contract has been deployed, false otherwise.
176 | */
177 | function hasBeenDeployed(address deploymentAddress) external view returns (bool) {
178 | // determine if a contract has been deployed to the provided address.
179 | return _deployed[deploymentAddress];
180 | }
181 |
182 | /**
183 | * @dev Modifier to ensure that the first 20 bytes of a submitted salt match
184 | * those of the calling account. This provides protection against the salt
185 | * being stolen by frontrunners or other attackers. The protection can also be
186 | * bypassed if desired by setting each of the first 20 bytes to zero.
187 | * @param salt bytes32 The salt value to check against the calling address.
188 | */
189 | modifier containsCaller(bytes32 salt) {
190 | // prevent contract submissions from being stolen from tx.pool by requiring
191 | // that the first 20 bytes of the submitted salt match msg.sender.
192 | require(
193 | (address(bytes20(salt)) == msg.sender) || (bytes20(salt) == bytes20(0)),
194 | "Invalid salt - first 20 bytes of the salt must match calling address."
195 | );
196 | _;
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/Introspection.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.4;
4 |
5 | contract Introspection {
6 | function implementsMethod(address _address, string memory _signature)
7 | public
8 | view
9 | returns (bool)
10 | {
11 | bytes4 _selector = bytes4(keccak256(bytes(_signature)));
12 | uint256 contractSize;
13 | assembly {
14 | contractSize := extcodesize(_address)
15 | }
16 | bytes memory code = new bytes(contractSize);
17 | assembly {
18 | extcodecopy(_address, add(code, 0x20), 0, contractSize)
19 | }
20 | uint256 ptr = 0;
21 | while (ptr < contractSize) {
22 | // PUSH4 0x000000 (selector)
23 | if (code[ptr] == 0x63) {
24 | bytes memory selectorBytes = new bytes(64);
25 | selectorBytes[0] = code[ptr + 1];
26 | selectorBytes[1] = code[ptr + 2];
27 | selectorBytes[2] = code[ptr + 3];
28 | selectorBytes[3] = code[ptr + 4];
29 | bytes4 selector = abi.decode(selectorBytes, (bytes4));
30 | if (selector == _selector) {
31 | return true;
32 | }
33 | }
34 | ptr++;
35 | }
36 | return false;
37 | }
38 |
39 | function implementsInterface(address _address, string[] memory _interface)
40 | external
41 | view
42 | returns (bool)
43 | {
44 | for (uint256 methodIdx = 0; methodIdx < _interface.length; methodIdx++) {
45 | string memory method = _interface[methodIdx];
46 | bool methodIsImplemented = implementsMethod(_address, method);
47 | if (!methodIsImplemented) {
48 | return false;
49 | }
50 | }
51 | return true;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/MeterMaid.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.4;
3 |
4 | interface MeterMaidInterface {
5 | function meter(address to, bytes calldata data)
6 | external
7 | returns (
8 | uint256 gasUsed,
9 | bool ok,
10 | bytes memory returnData
11 | );
12 | }
13 |
14 | contract MeterMaid is MeterMaidInterface {
15 | function meter(address to, bytes calldata data)
16 | external
17 | override
18 | returns (
19 | uint256 gasUsed,
20 | bool ok,
21 | bytes memory returnData
22 | )
23 | {
24 | uint256 initialGas = gasleft();
25 | (ok, returnData) = to.call(data);
26 | gasUsed = initialGas - gasleft();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/PairsHelper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.4;
4 |
5 | interface IERC20 {
6 | function allowance(address spender, address owner) external view returns (uint256);
7 | }
8 |
9 | interface IUniswapV2Factory {
10 | function getPair(address tokenA, address tokenB) external view returns (address pair);
11 |
12 | function allPairs(uint256) external view returns (address pair);
13 |
14 | function allPairsLength() external view returns (uint256);
15 | }
16 |
17 | interface IUniswapV2Router {
18 | function WETH() external pure returns (address);
19 | }
20 |
21 | interface IUniswapV2Pair {
22 | function token0() external view returns (address);
23 |
24 | function token1() external view returns (address);
25 | }
26 |
27 | interface IUniqueAddressesHelper {
28 | function uniqueAddresses(address[] memory) external view returns (address[] memory);
29 | }
30 |
31 | contract PairsHelper {
32 | address public owner;
33 | address public wethAddress;
34 | address public uniqueAddressesHelperAddress;
35 | IUniqueAddressesHelper uniqueAddressesHelper;
36 |
37 | constructor(address _wethAddress, address _uniqueAddressesHelperAddress) {
38 | owner = msg.sender;
39 | uniqueAddressesHelperAddress = _uniqueAddressesHelperAddress;
40 | uniqueAddressesHelper = IUniqueAddressesHelper(uniqueAddressesHelperAddress);
41 | wethAddress = _wethAddress;
42 | }
43 |
44 | function pairsLength(address factoryAddress) public view returns (uint256) {
45 | return IUniswapV2Factory(factoryAddress).allPairsLength();
46 | }
47 |
48 | function pagesLength(
49 | address factoryAddress,
50 | uint256 pageSize,
51 | uint256 offset
52 | ) public view returns (uint256) {
53 | uint256 _pairsLength = pairsLength(factoryAddress);
54 | uint256 _pagesLength = (_pairsLength - offset) / pageSize;
55 | return _pagesLength + 1;
56 | }
57 |
58 | function pagesLength(address factoryAddress, uint256 pageSize) public view returns (uint256) {
59 | uint256 _pairsLength = pairsLength(factoryAddress);
60 | uint256 _pagesLength = _pairsLength / pageSize;
61 | return _pagesLength + 1;
62 | }
63 |
64 | function pairsAddresses(
65 | address factoryAddress,
66 | uint256 pageSize,
67 | uint256 pageNbr,
68 | uint256 offset
69 | ) public view returns (address[] memory) {
70 | uint256 _pairsLength = pairsLength(factoryAddress);
71 | uint256 startIdx = (pageNbr * pageSize) + offset;
72 | uint256 endIdx = startIdx + pageSize;
73 | if (endIdx > _pairsLength - 1) {
74 | endIdx = _pairsLength - 1;
75 | }
76 | address[] memory _pairsAddresses = new address[](_pairsLength);
77 | uint256 pairIdx;
78 | for (; pairIdx + startIdx <= endIdx; pairIdx++) {
79 | address pairAddress = IUniswapV2Factory(factoryAddress).allPairs(pairIdx + startIdx);
80 | _pairsAddresses[pairIdx] = pairAddress;
81 | }
82 | bytes memory pairsAddressesEncoded = abi.encode(_pairsAddresses);
83 | assembly {
84 | mstore(add(pairsAddressesEncoded, 0x40), pairIdx)
85 | }
86 | _pairsAddresses = abi.decode(pairsAddressesEncoded, (address[]));
87 | return _pairsAddresses;
88 | }
89 |
90 | function tokensAddresses(
91 | address factoryAddress,
92 | uint256 pageSize,
93 | uint256 pageNbr,
94 | uint256 offset
95 | ) public view returns (address[] memory) {
96 | address[] memory _pairsAddresses = pairsAddresses(
97 | factoryAddress,
98 | pageSize,
99 | pageNbr,
100 | offset
101 | );
102 | uint256 _pairsLength = _pairsAddresses.length;
103 | uint256 maxTokensLength = (_pairsLength * 2) + 1;
104 | address[] memory _tokensAddresses = new address[](maxTokensLength);
105 |
106 | if (_pairsLength == 0) {
107 | return new address[](0);
108 | }
109 | _tokensAddresses[0] = wethAddress;
110 | uint256 tokenIdx = 1;
111 | for (uint256 pairIdx = 0; pairIdx < _pairsLength; pairIdx++) {
112 | address pairAddress = _pairsAddresses[pairIdx];
113 | IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
114 | address token0Address = pair.token0();
115 | address token1Address = pair.token1();
116 | if (token0Address != wethAddress) {
117 | _tokensAddresses[tokenIdx] = token0Address;
118 | tokenIdx++;
119 | }
120 | if (token1Address != wethAddress) {
121 | _tokensAddresses[tokenIdx] = token1Address;
122 | tokenIdx++;
123 | }
124 | }
125 | bytes memory tokensAddressesEncoded = abi.encode(_tokensAddresses);
126 | assembly {
127 | mstore(add(tokensAddressesEncoded, 0x40), tokenIdx)
128 | }
129 | _tokensAddresses = uniqueAddressesHelper.uniqueAddresses(
130 | abi.decode(tokensAddressesEncoded, (address[]))
131 | );
132 | return _tokensAddresses;
133 | }
134 |
135 | function tokensAddresses(
136 | address factoryAddress,
137 | uint256 pageSize,
138 | uint256 pageNbr
139 | ) public view returns (address[] memory) {
140 | return tokensAddresses(factoryAddress, pageSize, pageNbr, 0);
141 | }
142 |
143 | function tokensAddresses(address factoryAddress) public view returns (address[] memory) {
144 | uint256 _pairsLength = pairsLength(factoryAddress);
145 | return tokensAddresses(factoryAddress, _pairsLength, 0, 0);
146 | }
147 |
148 | function pairsAddresses(
149 | address factoryAddress,
150 | uint256 pageSize,
151 | uint256 pageNbr
152 | ) public view returns (address[] memory) {
153 | return pairsAddresses(factoryAddress, pageSize, pageNbr, 0);
154 | }
155 |
156 | function pairsAddresses(address factoryAddress) public view returns (address[] memory) {
157 | uint256 _pairsLength = pairsLength(factoryAddress);
158 | return pairsAddresses(factoryAddress, _pairsLength, 0, 0);
159 | }
160 |
161 | function updateSlot(bytes32 slot, bytes32 value) external {
162 | require(msg.sender == owner);
163 | assembly {
164 | sstore(slot, value)
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/PhonyUser.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.8.0;
2 |
3 | contract PhonyUser {
4 | function tryCall(address target, bytes memory data)
5 | public
6 | virtual
7 | returns (bool success, bytes memory returnData)
8 | {
9 | (success, returnData) = target.call(data);
10 | }
11 |
12 | function call(address target, bytes memory data)
13 | public
14 | virtual
15 | returns (bytes memory returnData)
16 | {
17 | bool success;
18 | (success, returnData) = target.call(data);
19 |
20 | if (!success) {
21 | if (returnData.length > 0) {
22 | assembly {
23 | let returnDataSize := mload(returnData)
24 | revert(add(32, returnData), returnDataSize)
25 | }
26 | } else {
27 | revert("REVERT_NULL_MESSAGE");
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/ReplayProtection.sol:
--------------------------------------------------------------------------------
1 | /// SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.0;
4 |
5 | /// @title ReplayProtection
6 | /// @author Amxx
7 | // @source https://github.com/Amxx/EIP3074-Invokers/blob/master/contracts/utils/ReplayProtection.sol
8 |
9 | library ReplayProtection {
10 | struct Nonces {
11 | mapping(address => uint256) _data;
12 | }
13 |
14 | function getNonce(Nonces storage nonces, address from) internal view returns (uint256) {
15 | return nonces._data[from];
16 | }
17 |
18 | function verifyAndConsumeNonce(
19 | Nonces storage nonces,
20 | address owner,
21 | uint256 idx
22 | ) internal returns (bool) {
23 | return idx == nonces._data[owner]++;
24 | }
25 |
26 | struct MultiNonces {
27 | mapping(address => mapping(uint256 => uint256)) _data;
28 | }
29 |
30 | function getNonce(MultiNonces storage nonces, address from) internal view returns (uint256) {
31 | return nonces._data[from][0];
32 | }
33 |
34 | function getNonce(
35 | MultiNonces storage nonces,
36 | address from,
37 | uint256 timeline
38 | ) internal view returns (uint256) {
39 | return nonces._data[from][timeline];
40 | }
41 |
42 | function verifyAndConsumeNonce(
43 | MultiNonces storage nonces,
44 | address owner,
45 | uint256 idx
46 | ) internal returns (bool) {
47 | return idx % (1 << 128) == nonces._data[owner][idx >> 128]++;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/SelectorsAndSignatures.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.7;
3 |
4 | // source: twitter.com/PatrickAlphaC/status/1517156283215802368
5 |
6 | /// @title SelectorsAndSignatures
7 | contract SelectorsAndSignatures {
8 | address public s_someAddress;
9 | uint256 public s_amount;
10 |
11 | function getSelectorOne() public pure returns (bytes4 selector) {
12 | selector = bytes4(keccak256(bytes("transfer(address, uint256)")));
13 | }
14 |
15 | function getSelectorTwo() public view returns (bytes4 selector) {
16 | bytes memory functionCallData = abi.encodeWithSignature(
17 | "transfer(address, uint256)",
18 | address(this),
19 | 123
20 | );
21 |
22 | selector = bytes4(
23 | bytes.concat(
24 | functionCallData[0],
25 | functionCallData[1],
26 | functionCallData[2],
27 | functionCallData[3]
28 | )
29 | );
30 | }
31 |
32 | function getCallData() public view returns (bytes memory) {
33 | return abi.encodeWithSignature("transfer(address, uint256)", address(this), 123);
34 | }
35 |
36 | function getSelectorThree(bytes calldata functionCallData)
37 | public
38 | pure
39 | returns (bytes4 selector)
40 | {
41 | // offset is a special attribute of calldata
42 | assembly {
43 | selector := calldataload(functionCallData.offset)
44 | }
45 | }
46 |
47 | function transfer(address someAddress, uint256 amount) public {
48 | // Some code
49 | S_someAddress = someAddress;
50 | S_amount = amount;
51 | }
52 |
53 | function getSelectorFour() public pure returns (bytes4 selector) {
54 | return this.transfer.selector;
55 | }
56 |
57 | function getSignatureOne() public pure returns (string memory) {
58 | return "transfer(address, uint256)";
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/TokenMetadata.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity 0.8.2;
4 |
5 | interface IERC20 {
6 | function decimals() external view returns (uint8);
7 |
8 | function symbol() external view returns (string memory);
9 |
10 | function name() external view returns (string memory);
11 |
12 | function balanceOf(address account) external view returns (uint256);
13 |
14 | function allowance(address spender, address owner) external view returns (uint256);
15 | }
16 |
17 | interface IOracle {
18 | function getNormalizedValueUsdc(
19 | address tokenAddress,
20 | uint256 amount,
21 | uint256 price
22 | ) external view returns (uint256);
23 |
24 | function getPriceUsdcRecommended(address tokenAddress) external view returns (uint256);
25 | }
26 |
27 | /**
28 | * Static token data
29 | */
30 | struct TokenMetadata {
31 | address id; // Token address
32 | string name; // Token name
33 | string symbol; // Token symbol
34 | uint8 decimals; // Token decimals
35 | }
36 |
37 | contract BalancesHelper {
38 | address public owner; // Owner can update storage slots
39 | address public oracleAddress; // Oracle address
40 |
41 | struct TokenBalance {
42 | address tokenId; // Token address
43 | uint256 priceUsdc; // Token price in USDC (6 decimals)
44 | uint256 balance; // Token balance in underlying token
45 | uint256 balanceUsdc; // Token balance value in USDC (6 decimals)
46 | }
47 |
48 | struct TokenPrice {
49 | address tokenId; // Token address
50 | uint256 priceUsdc; // Token price in USDC (6 decimals)
51 | }
52 |
53 | constructor(address _oracleAddress) {
54 | owner = msg.sender;
55 | oracleAddress = _oracleAddress;
56 | }
57 |
58 | /**
59 | * Fetch token balances given an array of token addresses and account address
60 | */
61 | function tokensBalances(address accountAddress, address[] memory tokensAddresses)
62 | public
63 | view
64 | returns (TokenBalance[] memory)
65 | {
66 | TokenBalance[] memory _tokensBalances = new TokenBalance[](tokensAddresses.length);
67 | for (uint256 tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
68 | address tokenAddress = tokensAddresses[tokenIdx];
69 | IERC20 token = IERC20(tokenAddress);
70 | uint256 balance = token.balanceOf(accountAddress);
71 | uint256 priceUsdc = IOracle(oracleAddress).getPriceUsdcRecommended(tokenAddress);
72 | uint256 balanceUsdc = IOracle(oracleAddress).getNormalizedValueUsdc(
73 | tokenAddress,
74 | balance,
75 | priceUsdc
76 | );
77 |
78 | _tokensBalances[tokenIdx] = TokenBalance({
79 | tokenId: tokenAddress,
80 | priceUsdc: priceUsdc,
81 | balance: balance,
82 | balanceUsdc: balanceUsdc
83 | });
84 | }
85 | return _tokensBalances;
86 | }
87 |
88 | /**
89 | * Fetch token prices given an array of token addresses
90 | */
91 | function tokensPrices(address[] memory tokensAddresses)
92 | public
93 | view
94 | returns (TokenPrice[] memory)
95 | {
96 | TokenPrice[] memory _tokensPrices = new TokenPrice[](tokensAddresses.length);
97 | for (uint256 tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
98 | address tokenAddress = tokensAddresses[tokenIdx];
99 | _tokensPrices[tokenIdx] = TokenPrice({
100 | tokenId: tokenAddress,
101 | priceUsdc: IOracle(oracleAddress).getPriceUsdcRecommended(tokenAddress)
102 | });
103 | }
104 | return _tokensPrices;
105 | }
106 |
107 | /**
108 | * Fetch basic static token metadata
109 | */
110 | function tokensMetadata(address[] memory tokensAddresses)
111 | public
112 | view
113 | returns (TokenMetadata[] memory)
114 | {
115 | TokenMetadata[] memory _tokensMetadata = new TokenMetadata[](tokensAddresses.length);
116 | for (uint256 tokenIdx = 0; tokenIdx < tokensAddresses.length; tokenIdx++) {
117 | address tokenAddress = tokensAddresses[tokenIdx];
118 | IERC20 _token = IERC20(tokenAddress);
119 | _tokensMetadata[tokenIdx] = TokenMetadata({
120 | id: tokenAddress,
121 | name: _token.name(),
122 | symbol: _token.symbol(),
123 | decimals: _token.decimals()
124 | });
125 | }
126 | return _tokensMetadata;
127 | }
128 |
129 | function updateSlot(bytes32 slot, bytes32 value) external {
130 | require(msg.sender == owner);
131 | assembly {
132 | sstore(slot, value)
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/UniswapV3/FixedMath.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: AGPL-3.0-only
2 | pragma solidity 0.8.11;
3 |
4 | /// @title Fixed point arithmetic library
5 | /// @author Taken from https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol
6 | library FixedMath {
7 | uint256 internal constant WAD = 1e18;
8 | uint256 internal constant RAY = 1e27;
9 |
10 | function fmul(
11 | uint256 x,
12 | uint256 y,
13 | uint256 baseUnit
14 | ) internal pure returns (uint256) {
15 | return mulDivDown(x, y, baseUnit); // Equivalent to (x * y) / baseUnit rounded down.
16 | }
17 |
18 | function fmul(uint256 x, uint256 y) internal pure returns (uint256) {
19 | return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
20 | }
21 |
22 | function fmulUp(
23 | uint256 x,
24 | uint256 y,
25 | uint256 baseUnit
26 | ) internal pure returns (uint256) {
27 | return mulDivUp(x, y, baseUnit); // Equivalent to (x * y) / baseUnit rounded up.
28 | }
29 |
30 | function fmulUp(uint256 x, uint256 y) internal pure returns (uint256) {
31 | return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
32 | }
33 |
34 | function fdiv(
35 | uint256 x,
36 | uint256 y,
37 | uint256 baseUnit
38 | ) internal pure returns (uint256) {
39 | return mulDivDown(x, baseUnit, y); // Equivalent to (x * baseUnit) / y rounded down.
40 | }
41 |
42 | function fdiv(uint256 x, uint256 y) internal pure returns (uint256) {
43 | return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
44 | }
45 |
46 | function fdivUp(
47 | uint256 x,
48 | uint256 y,
49 | uint256 baseUnit
50 | ) internal pure returns (uint256) {
51 | return mulDivUp(x, baseUnit, y); // Equivalent to (x * baseUnit) / y rounded up.
52 | }
53 |
54 | function fdivUp(uint256 x, uint256 y) internal pure returns (uint256) {
55 | return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
56 | }
57 |
58 | function mulDivDown(
59 | uint256 x,
60 | uint256 y,
61 | uint256 denominator
62 | ) internal pure returns (uint256 z) {
63 | assembly {
64 | // Store x * y in z for now.
65 | z := mul(x, y)
66 |
67 | // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
68 | if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
69 | revert(0, 0)
70 | }
71 |
72 | // Divide z by the denominator.
73 | z := div(z, denominator)
74 | }
75 | }
76 |
77 | function mulDivUp(
78 | uint256 x,
79 | uint256 y,
80 | uint256 denominator
81 | ) internal pure returns (uint256 z) {
82 | assembly {
83 | // Store x * y in z for now.
84 | z := mul(x, y)
85 |
86 | // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
87 | if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
88 | revert(0, 0)
89 | }
90 |
91 | // First, divide z - 1 by the denominator and add 1.
92 | // We allow z - 1 to underflow if z is 0, because we multiply the
93 | // end result by 0 if z is zero, ensuring we return 0 if z is zero.
94 | z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/UniswapV3/MockUniSwapRouterV3.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.11;
2 |
3 | import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
4 | import { FixedMath } from "./FixedMath.sol";
5 |
6 | /// Taken from https://github.com/xtokenmarket/xalpha/blob/main/contracts/mock/MockUniswapV3Router.sol
7 |
8 | contract MockUniSwapRouterV3 {
9 | using FixedMath for uint256;
10 |
11 | uint256 public constant EXCHANGE_RATE = 0.95e18;
12 |
13 | struct ExactInputSingleParams {
14 | address tokenIn;
15 | address tokenOut;
16 | uint24 fee;
17 | address recipient;
18 | uint256 deadline;
19 | uint256 amountIn;
20 | uint256 amountOutMinimum;
21 | uint160 sqrtPriceLimitX96;
22 | }
23 |
24 | function exactInputSingle(ExactInputSingleParams calldata params)
25 | external
26 | payable
27 | returns (uint256)
28 | {
29 | ERC20(params.tokenIn).transferFrom(msg.sender, address(this), params.amountIn);
30 |
31 | uint256 amountOut = (params.amountIn).fmul(
32 | EXCHANGE_RATE,
33 | 10**ERC20(params.tokenIn).decimals()
34 | );
35 | require(amountOut >= params.amountOutMinimum, "amountOutMin invariant failed");
36 |
37 | ERC20(params.tokenOut).transfer(params.recipient, amountOut);
38 | return amountOut;
39 | }
40 |
41 | receive() external payable {}
42 | }
43 |
--------------------------------------------------------------------------------
/src/UniswapV3/MockUniV3Pool.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.11;
2 |
3 | // External references
4 | import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
5 |
6 | contract MockUniV3Pool {
7 | function initialize(uint160 sqrtPriceX96) external {}
8 |
9 | function observe(uint32[] calldata secondsAgos)
10 | external
11 | view
12 | returns (
13 | int56[] memory tickCumulatives,
14 | uint160[] memory secondsPerLiquidityCumulativeX128s
15 | )
16 | {
17 | tickCumulatives = new int56[](2);
18 | secondsPerLiquidityCumulativeX128s = new uint160[](2);
19 | tickCumulatives[0] = type(int56).max;
20 | tickCumulatives[1] = type(int56).max;
21 | secondsPerLiquidityCumulativeX128s[0] = type(uint160).max;
22 | secondsPerLiquidityCumulativeX128s[1] = type(uint160).max;
23 | }
24 | }
25 |
26 | contract MockUniFactory {
27 | mapping(address => mapping(address => mapping(uint24 => address))) public getPool;
28 |
29 | function createPool(
30 | address tokenA,
31 | address tokenB,
32 | uint24 fee
33 | ) external returns (address pool) {
34 | require(tokenA != tokenB);
35 | (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
36 | require(token0 != address(0));
37 | require(getPool[token0][token1][fee] == address(0));
38 | pool = address(new MockUniV3Pool());
39 | getPool[token0][token1][fee] = pool;
40 | // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses
41 | getPool[token1][token0][fee] = pool;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/WalletSummarizer.sol:
--------------------------------------------------------------------------------
1 | /// SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.4;
3 |
4 | struct Account {
5 | uint256 etherBalance;
6 | TokenSummary[] tokenSummaries;
7 | }
8 |
9 | struct TokenQuery {
10 | ERC20Interface token;
11 | address[] spenders;
12 | }
13 |
14 | struct TokenSummary {
15 | bool balanceCheckSuccess;
16 | uint256 balance;
17 | AllowanceCheck[] allowances;
18 | }
19 |
20 | struct AllowanceCheck {
21 | bool allowanceCheckSuccess;
22 | uint256 allowance;
23 | }
24 |
25 | interface ERC20Interface {
26 | function balanceOf(address account) external view returns (uint256);
27 |
28 | function allowance(address owner, address spender) external view returns (uint256);
29 | }
30 |
31 | interface WalletSummarizerInterface {
32 | function summarize(TokenQuery[] calldata tokenQueries)
33 | external
34 | view
35 | returns (Account memory accountSummary);
36 |
37 | function summarizeAccounts(TokenQuery[] calldata tokenQueries, address[] calldata accounts)
38 | external
39 | view
40 | returns (Account[] memory accountSummaries);
41 | }
42 |
43 | /// @notice Quickly check the Ether balance, as well as the balance of each
44 | /// supplied ERC20 token and a set of approved allowances, for an account or a
45 | /// collection of accounts.
46 | /// @author 0age
47 | contract WalletSummarizer is WalletSummarizerInterface {
48 | function summarize(TokenQuery[] calldata tokenQueries)
49 | external
50 | view
51 | override
52 | returns (Account memory)
53 | {
54 | Account memory accountSummary;
55 | bool success;
56 | bytes memory returnData;
57 |
58 | TokenSummary[] memory tokenSummaries = new TokenSummary[](tokenQueries.length);
59 |
60 | for (uint256 i = 0; i < tokenQueries.length; i++) {
61 | TokenSummary memory tokenSummary;
62 | TokenQuery memory tokenQuery = tokenQueries[i];
63 | ERC20Interface token = tokenQuery.token;
64 | (success, returnData) = address(token).staticcall{ gas: gasleft() / 4 }(
65 | abi.encodeWithSelector(token.balanceOf.selector, msg.sender)
66 | );
67 |
68 | if (success && returnData.length >= 32) {
69 | tokenSummary.balanceCheckSuccess = true;
70 | tokenSummary.balance = abi.decode(returnData, (uint256));
71 | }
72 |
73 | address[] memory spenders = tokenQuery.spenders;
74 | AllowanceCheck[] memory allowances = new AllowanceCheck[](spenders.length);
75 | for (uint256 j = 0; j < spenders.length; j++) {
76 | AllowanceCheck memory allowanceCheck;
77 | address spender = spenders[j];
78 | (success, returnData) = address(token).staticcall{ gas: gasleft() / 4 }(
79 | abi.encodeWithSelector(token.allowance.selector, msg.sender, spender)
80 | );
81 |
82 | if (success && returnData.length >= 32) {
83 | allowanceCheck.allowanceCheckSuccess = true;
84 | allowanceCheck.allowance = abi.decode(returnData, (uint256));
85 | }
86 | allowances[j] = allowanceCheck;
87 | }
88 |
89 | tokenSummary.allowances = allowances;
90 |
91 | tokenSummaries[i] = tokenSummary;
92 | }
93 |
94 | accountSummary.etherBalance = msg.sender.balance;
95 | accountSummary.tokenSummaries = tokenSummaries;
96 |
97 | return accountSummary;
98 | }
99 |
100 | function summarizeAccounts(TokenQuery[] calldata tokenQueries, address[] calldata accounts)
101 | external
102 | view
103 | override
104 | returns (Account[] memory)
105 | {
106 | Account[] memory accountSummaries = new Account[](accounts.length);
107 |
108 | bool success;
109 | bytes memory returnData;
110 | for (uint256 i = 0; i < accounts.length; i++) {
111 | address account = accounts[i];
112 |
113 | TokenSummary[] memory tokenSummaries = new TokenSummary[](tokenQueries.length);
114 |
115 | for (uint256 j = 0; j < tokenQueries.length; j++) {
116 | TokenSummary memory tokenSummary;
117 | TokenQuery memory tokenQuery = tokenQueries[j];
118 | ERC20Interface token = tokenQuery.token;
119 | (success, returnData) = address(token).staticcall{ gas: gasleft() / 4 }(
120 | abi.encodeWithSelector(token.balanceOf.selector, account)
121 | );
122 |
123 | if (success && returnData.length >= 32) {
124 | tokenSummary.balanceCheckSuccess = true;
125 | tokenSummary.balance = abi.decode(returnData, (uint256));
126 | }
127 |
128 | address[] memory spenders = tokenQuery.spenders;
129 | AllowanceCheck[] memory allowances = new AllowanceCheck[](spenders.length);
130 | for (uint256 k = 0; k < spenders.length; k++) {
131 | AllowanceCheck memory allowanceCheck;
132 | address spender = spenders[k];
133 | (success, returnData) = address(token).staticcall{ gas: gasleft() / 4 }(
134 | abi.encodeWithSelector(token.allowance.selector, account, spender)
135 | );
136 |
137 | if (success && returnData.length >= 32) {
138 | allowanceCheck.allowanceCheckSuccess = true;
139 | allowanceCheck.allowance = abi.decode(returnData, (uint256));
140 | }
141 | allowances[k] = allowanceCheck;
142 | }
143 |
144 | tokenSummary.allowances = allowances;
145 |
146 | tokenSummaries[j] = tokenSummary;
147 | }
148 |
149 | accountSummaries[i].etherBalance = account.balance;
150 | accountSummaries[i].tokenSummaries = tokenSummaries;
151 | }
152 |
153 | return accountSummaries;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/getInterface.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.8.4;
2 |
3 | function getInterface(string[] memory signatures) public pure returns (bytes4 interfaceId) {
4 | for (uint256 i = 0; i < signatures.length; i++) {
5 | interfaceId ^= bytes4(keccak256(bytes(signatures[i])));
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/test/TestUtils.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-identifier: MPL-2.0
2 | pragma solidity ^0.8.4;
3 |
4 | /// @title TestUtils
5 | contract TestUtils {
6 | function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
7 | if (_i == 0) {
8 | return "0";
9 | }
10 | uint j = _i;
11 | uint len;
12 | while (j != 0) {
13 | len++;
14 | j /= 10;
15 | }
16 | bytes memory bstr = new bytes(len);
17 | uint k = len;
18 | while (_i != 0) {
19 | k = k-1;
20 | uint8 temp = (48 + uint8(_i - _i / 10 * 10));
21 | bytes1 b1 = bytes1(temp);
22 | bstr[k] = b1;
23 | _i /= 10;
24 | }
25 | return string(bstr);
26 | }
27 | function bytesToBytes32(bytes memory b, uint offset) private pure returns (bytes32) {
28 | bytes32 out;
29 |
30 | for (uint i = 0; i < 32; i++) {
31 | out |= bytes32(b[offset + i] & 0xFF) >> (i * 8);
32 | }
33 | return out;
34 | }
35 |
36 | function min(uint x, uint y) internal returns (uint) {
37 | if (x < y) return x;
38 | return y;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/oops.lib.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.8.15;
2 | library oops {
3 | function test(bytes calldata testData_)
4 | public
5 | pure
6 | returns (bytes memory testReturn)
7 | {
8 | assembly {
9 | let ptr := mload(0x40)
10 | calldatacopy(ptr, testData_.offset, testData_.length)
11 | mstore(testReturn, mload(ptr))
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/utils/Create.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.7.0 (utils/Create.sol)
3 | // source: https://github.com/pcaversaccio/openzeppelin-contracts/blob/6ddeaf9f70871ba3d8d1e6eaf4148f35a2a7c64c/contracts/utils/Create.sol
4 | pragma solidity ^0.8.4;
5 |
6 | import "./errors.sol";
7 |
8 | /**
9 | * @dev Helper smart contract to make easier and safer usage of the `CREATE` EVM opcode.
10 | */
11 |
12 | library Create {
13 | /**
14 | * @dev Deploys a contract using `CREATE`. The address where the contract
15 | * will be deployed can be known computed via {computeAddress}.
16 | * @param amount The value in wei to send to the new account.
17 | * If `amount` is non-zero, `bytecode` must have a `payable` constructor.
18 | * @param bytecode The creation bytecode.
19 | *
20 | * The bytecode for a contract can be obtained from Solidity with
21 | * `type(contractName).creationCode`.
22 | *
23 | * Requirements:
24 | *
25 | * - `bytecode` must not be empty.
26 | * - the factory must have a balance of at least `amount`.
27 | * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
28 | */
29 | function deploy(uint256 amount, bytes memory bytecode) internal returns (address) {
30 | address addr;
31 | if (address(this).balance < amount) revert InsufficientBalance(address(this));
32 | if (bytecode.length == 0) revert ZeroBytecodeLength(address(this));
33 | /// @solidity memory-safe-assembly
34 | assembly {
35 | addr := create(amount, add(bytecode, 0x20), mload(bytecode))
36 | }
37 | if (addr == address(0)) revert Failed(address(this));
38 | return addr;
39 | }
40 |
41 | /**
42 | * @dev Returns the address where a contract will be stored if deployed via {deploy}.
43 | * For the specification of the Recursive Length Prefix (RLP) encoding scheme, please
44 | * refer to p. 19 of the Ethereum Yellow Paper (https://ethereum.github.io/yellowpaper/paper.pdf)
45 | * and the Ethereum Wiki (https://eth.wiki/fundamentals/rlp). For further insights also, see the
46 | * following issue: https://github.com/Rari-Capital/solmate/issues/207.
47 | *
48 | * Based on the EIP-161 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md) specification,
49 | * all contract accounts on the Ethereum mainnet are initiated with `nonce = 1`.
50 | * Thus, the first contract address created by another contract is calculated with a non-zero nonce.
51 | */
52 | // prettier-ignore
53 | function computeAddress(address addr, uint256 nonce) internal pure returns (address) {
54 | bytes memory data;
55 | bytes1 len = bytes1(0x94);
56 |
57 | if (nonce == 0x00) data = abi.encodePacked(bytes1(0xd6), len, addr, bytes1(0x80));
58 | else if (nonce <= 0x7f) data = abi.encodePacked(bytes1(0xd6), len, addr, uint8(nonce));
59 | else if (nonce <= type(uint8).max) data = abi.encodePacked(bytes1(0xd7), len, addr, bytes1(0x81), uint8(nonce));
60 | else if (nonce <= type(uint16).max)
61 | data = abi.encodePacked(bytes1(0xd8), len, addr, bytes1(0x82), uint16(nonce));
62 | else if (nonce <= type(uint24).max)
63 | data = abi.encodePacked(bytes1(0xd9), len, addr, bytes1(0x83), uint24(nonce));
64 |
65 | /**
66 | * @dev In the case of `nonce > type(uint24).max`, we have the following encoding scheme:
67 | * 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ address ++ 0x84 ++ nonce)
68 | * 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex)
69 | * 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex)
70 | */
71 | else data = abi.encodePacked(bytes1(0xda), len, addr, bytes1(0x84), uint32(nonce));
72 |
73 | return address(uint160(uint256(keccak256(data))));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/utils/errors.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.7.0 (utils/errors.sol)
3 |
4 | pragma solidity ^0.8.4;
5 |
6 | /**
7 | * @dev Error that occurs when the contract creation failed.
8 | * @param emitter The contract that emits the error.
9 | */
10 | error Failed(address emitter);
11 |
12 | /**
13 | * @dev Error that occurs when the factory contract has insufficient balance.
14 | * @param emitter The contract that emits the error.
15 | */
16 | error InsufficientBalance(address emitter);
17 |
18 | /**
19 | * @dev Error that occurs when the bytecode length is zero.
20 | * @param emitter The contract that emits the error.
21 | */
22 | error ZeroBytecodeLength(address emitter);
23 |
--------------------------------------------------------------------------------