pcaversaccio / snekmate Goto Github PK
View Code? Open in Web Editor NEWState-of-the-art, highly opinionated, hyper-optimised, and secure πVyper smart contract building blocks.
License: GNU Affero General Public License v3.0
State-of-the-art, highly opinionated, hyper-optimised, and secure πVyper smart contract building blocks.
License: GNU Affero General Public License v3.0
In order to ensure no compilation failures if development frameworks use the userdoc
and/or devdoc
CLI options, we should implement a proper CI test for those cases (after the Vyper 0.3.8
release since).
vyper -f userdoc,devdoc .\src\auth\AccessControl.vy
I'm working on some signature verification code in Vyper and consistently failing to recover the correct signer address when using ECDSA.vy's recover_sig
function.
I have deployed ECDSA.vy
to goerli at 0xAdf31a3b9B9DD0486199A1029BEb8bA4Cc86e5E4
Here are the inputs I am using:
cast keccak 1234
outputs 0x387a8233c96e1fc0ad5e284353276177af2186e7afa85296f106336e376669f7
I now want to sign this hash, and use ECDSA.vy to recover the address used to sign.
I am using wagmi.sh's components to generate a signature on my frontend. I input the above hash into the frontend widget, then sign, and the frontend widget recovers my signature as expected after I confirm and sign with my wallet (screenshot attached). You can access this same widget here
I then take the signature provided to me by the widget and call:
In [6]: e.recover_sig("0x387a8233c96e1fc0ad5e284353276177af2186e7afa85296f106336e376669f7", "0x042f669e124b91b5c975d3bc329618373ee0bc666c6412891ff6d85e7e0cf7ae4f9630450253ac55784fc534c655ff2e3743e04df2a5035728ecede196a379311c")
Out[6]: '0xbA3De837Da49408AD799d6B7ffD40a013B7214FB'
I was able to run forge test --match-contract ECDSA
successfully, and the tests pass, but this failing example here is so minimal that I have no idea what else to try.
see ECDSA.vy
0.3.7+commit.6020b8b
No response
The README
sequence should look like this:
src
ββ auth
β ββ Ownable β "Owner-Based Access Control Functions"
β ββ Ownable2Step β "2-Step Ownership Transfer Functions"
β ββ AccessControl β "Multi-Role-Based Access Control Functions"
ββ extensions
β ββ ERC4626 β "ERC-4626 Tokenised Vault Implementation (TBD)"
ββ tokens
β ββ ERC20 β "Modern and Gas-Efficient ERC-20 + EIP-2612 Implementation"
β ββ ERC721 β "Modern and Gas-Efficient ERC-721 + EIP-4494 Implementation"
β ββ ERC1155 β "Modern and Gas-Efficient ERC-1155 Implementation (TBD)"
ββ utils
ββ Base64 β "Base64 Encoding and Decoding Functions"
ββ BatchDistributor β "Batch Sending Both Native and ERC-20 Tokens"
ββ CreateAddress β "`CREATE` EVM Opcode Utility Function for Address Calculation"
ββ Create2Address β "`CREATE2` EVM Opcode Utility Functions for Address Calculations"
ββ ECDSA β "Elliptic Curve Digital Signature Algorithm (ECDSA) Functions"
ββ SignatureChecker β "ECDSA and EIP-1271 Signature Verification Function"
ββ EIP712DomainSeparator β "EIP-712 Domain Separator"
ββ MerkleProofVerification β "Merkle Tree Proof Verification Functions"
ββ Multicall β "Multicall Functions"
Add two new functions to the Math
contract:
wad_ln
: Calculates the natural logarithm of a signed integer with a precision of 1e18.wad_exp
: Calculates the natural exponential function of a signed integer with a precision of 1e18.In the new Vyper version 0.3.8
, the ternary operator has been implemented (see here):
@external
def hello(a: uint256, b: uint256) -> uint256:
return a if a < b else b
We should assess the full code base, where a ternary operator could be beneficial.
Modern and gas-efficient ERC-1155 implementation.
As of Vyper version 0.3.8
, getters for public constant
s and immutable
s are added to the self
namespace: vyperlang/vyper#3334. Thus, we can fix the current workarounds in ERC20
, ERC721
, and ERC4626
.
I open this issue to track whether we should implement that zero transferFrom
calls should be reverted. The implementation would look like this:
@internal
def _spend_allowance(owner: address, spender: address, amount: uint256):
current_allowance: uint256 = self.allowance[owner][spender]
if (current_allowance != max_value(uint256)):
assert current_allowance >= max(amount, 1), "ERC20: insufficient allowance"
self._approve(owner, spender, unsafe_sub(current_allowance, amount))
The reason why we should at least discuss it is that zero transferFrom
calls can be invoked by anyone and allow for a certain type of phishing, see e.g. here.
Since the stability of external mocks is not guaranteed (see e.g. here OpenZeppelin/openzeppelin-contracts#3666), we should remove this dependency and use internal mocks for testing.
Implement a signature verification helper that can be used instead of ECDSA.recover_sig
to seamlessly support both ECDSA signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like Argent and Gnosis Safe.
There will be a new Vyper version 0.3.10
released in around 2 weeks. We will need to upgrade all π snekmate contracts since the new version will entail various performance improvements and security patches (see, e.g., here).
Current release candidates:
Released via https://github.com/vyperlang/vyper/releases/tag/v0.3.10.
In the current SignatureChecker
, there is support to check if a signature is valid with the is_valid_signature_now(..)
function that checks the following:
hash
and signature
recover an address that is the same as the signer
, it returns true.is_valid_signature(..)
on the signer
assuming it's a contract.What would be a great addition to the library is to have another internal function that checks if a signature is valid with ERC1271 only. So calling is_valid_signature(..)
directly on the signer implementing ERC1271.
This function will be useful for functions that want to interact only with wallets such as argent, gnosis, etc .. and check the signature only via ERC1271.
Then this function can be re-used in is_valid_signature_now(..)
.
Developers can interact with other smart contract wallets by calling is_valid_signature(..)
function directly. Still, they need to handle the return value, so the new function can be a wrapper named is_valid_ERC1271_signature_now(address,bytes32,bytes[65])
that calls is_valid_signature(..)
on the address with the hash and the signature and don't revert if the function doesn't exist and return true or false, depending if the function returns the isValidSignature.selector
. Exactly like what the code does in the second part of the is_valid_signature_now(..)
function.
If you find this addition relevant, I can work on creating the new function (splitting the is_valid_signature_now(..)
function into a new one).
It would be mostly taking this part of the function to a new function (is_valid_ERC1271_signature_now(..)
) that can be re-used in is_valid_signature_now(..)
# Second check: EIP-1271 case.
return_data: Bytes[32] = \
raw_call(signer, _abi_encode(hash, signature, method_id=IERC1271_ISVALIDSIGNATURE_SELECTOR), max_outsize=32, is_static_call=True)
return ((len(return_data) == 32) and (convert(return_data, bytes32) == convert(IERC1271_ISVALIDSIGNATURE_SELECTOR, bytes32)))
@victor-ego proposes to implement ERC-2981
(NFT royalty standard) for π snekmate's ERC721
contract.
This issue is opened to gather the discussion around whether such an implementation is of a value-add.
Personally, I have the following view:
Let me know your thoughts.
There will be a new Vyper version 0.3.8
released in around 4 weeks. We will need to upgrade all π snekmate contracts since the new version will entail various bug fixes and security patches (see, e.g., here).
Implement a full unit test suite for the ERC-721 implementation.
Modern and gas-efficient ERC-721 implementation.
It would be convenient that the VyperDeployer.sol
implemented an easy way of deploying blueprint contracts. In fact, 0xKitsune has a nice function implementing it.
/**
* @dev Compiles a blueprint Vyper contract and returns the address that
* the contract was deployed to.
* If the deployment fails, an error is thrown.
* @param path The directory path of the Vyper contract.
* For example, the path of "utils" is "src/utils/".
* @param fileName The file name of the Vyper contract.
* For example, the file name for "ECDSA.vy" is "ECDSA".
* @return deployedAddress The address that the contract was deployed to.
*/
function deployBlueprint(
string memory path,
string memory fileName
) public returns (address) {
/**
* @dev Create a list of strings with the commands necessary
* to compile Vyper contracts.
*/
string[] memory cmds = new string[](4);
cmds[0] = "vyper";
cmds[1] = "-f";
cmds[2] = "blueprint_bytecode";
cmds[3] = string.concat(path, fileName, ".vy");
/**
* @dev Compile the Vyper contract and return the bytecode.
*/
bytes memory bytecode = cheatCodes.ffi(cmds);
/**
* @notice prepend needed items for Blueprint EIP
* See https://eips.ethereum.org/EIPS/eip-5202 for more details
*/
bytes memory eip_5202_bytecode = bytes.concat(
hex"fe", // EIP_5202_EXECUTION_HALT_BYTE
hex"71", // EIP_5202_BLUEPRINT_IDENTIFIER_BYTE
hex"00", // EIP_5202_VERSION_BYTE
bytecode
);
bytes2 len = bytes2(uint16(eip_5202_bytecode.length));
/**
* @notice prepend the deploy preamble
*/
bytes memory deployBytecode = bytes.concat(
hex"61", // DEPLOY_PREAMBLE_INITIAL_BYTE
len, // DEPLOY_PREAMBLE_BYTE_LENGTH
hex"3d81600a3d39f3", // DEPLOY_PREABLE_POST_LENGTH_BYTES
eip_5202_bytecode
);
/**
* @dev Deploy the bytecode with the `CREATE` instruction.
*/
address deployedAddress;
deployedAddress = deploy(0, deployBytecode);
/**
* @dev Check that the deployment was successful.
*/
if (deployedAddress == address(0)) revert DeploymentFailed(self);
/**
* @dev Return the address that the contract was deployed to.
*/
return deployedAddress;
}
Modern and gas-efficient ERC-20 + EIP-2612 implementation in Vyper.
We should refactor the Merkle proof verification scripts using the new and well-maintained library @openzeppelin/merkle-tree
. Also, see the discussion here OpenZeppelin/openzeppelin-contracts#3813.
Vyper 0.3.8
will support Python 3.11
, see here. We should upgrade the CI Python versions from 3.10
to 3.11
in the CI config files:
.github/workflows/publish-pypi.yml
.github/workflows/publish-test-pypi.yml
.github/workflows/test-contracts.yml
Furthermore, the ""
are not needed for the architecture
and python-version
fields.
Edit: We should also bump the node version to 18
.
Currently, multiple Solidity-based test interfaces contain some functions where the function input parameters contain the data location memory
instead of calldata
. However, Vyper does not allow for memory
location of the input parameters. So we need to refactor this. This poses no security risk, however, in the unit tests since the unit tests are run against the Vyper bytecode.
ERC-4626 tokenised vault implementation.
In the current ECDSA
library, there is support to construct a message to sign according to the EIP191 Standard with the following:
0x45
(E) with to_eth_signed_message_hash(..)
function0x01
with the to_typed_data_hash(..)
function.0x00
version.Version0x00
version is not that used because people are misusing the standard, they are using the 0x45
version for everything: signing messages for off-chain verification, for smart contract execution based on signatures, which is dangerous.
As people could be easily tricked into signing a normal message, thinking it is for login purposes, and then end up having execution based on this signature, so we should have a different mechanism, then different handling for execution based on signatures, and that's why we should make people use the 0x00
version for this case. + some projects are starting to use it like xenium and the lsp-smart-contracts.
I am suggesting adding a new function to_data_with_intended_validator
or to_data_with_intended_validator_hash
to be compatible with the version 0x00
taking 2 parameters, <address validator>
and <bytes dataToSign>
.
A library that supports the case: EIP-191 Signer.
The Opened issue in OpenZeppelin: Implement 0x00 version of EIP191 in ECDSA Library
If you're okay with it I am happy to open the PR, otherwise, you can close the issue π
@external
@pure
def to_data_with_intended_validator(validator: address, data_to_sign: Bytes[1024]=b"") -> bytes32:
"""
@dev Returns an intended validator signed data according to
0x00 version of EIP-191.
@param validator The address validating the signature
@param struct_hash The data passed to be signed
@return bytes32 The 32-byte intended validator signed data.
"""
return keccak256(concat(b"\x19\x00", validator, data_to_sign))
Write Vyper version of https://github.com/Vectorized/solady/blob/main/src/utils/LibSort.sol.
Add some spicy math functions for Vyper peeps.
Once we have got the first major release ready, we should consider adding a CHANGELOG
file that tracks the changes.
Provide a set of functions to operate with Base64
strings. See also here.
README
; add npm
and PyPI
markdown badges; add release date to CHANGELOG
package.json
PyPI
releasePyPI
releasenpm
releasePyPI
release0.0.1
In the new Vyper version 0.3.8
, the commonly-know shift operators x << y
and x >> y
have been enabled and the currently existing operator shift
has been deprecated from version 0.3.8
onwards.
A couple of comments for the sake of documentation:
x
can be uint256
OR int256
.x
must be uint256
whilst for arithmetic shift operations x
must be int256
.x >> y
for negative x
is rounded down (towards negative infinity).0
and not anything else. This contradicts the intuition we have from unchecked arithmetic where everything wraps and starts all over again from 0
but can have any uint256
result. What I mean by that is:@external
@pure
def foo(x: uint8, y: uint8) -> uint8:
return unsafe_add(x, y)
ExampleContract.foo(255, 255)
will result in 254
and not 0
.
ERC-4906 is now final and should be implemented.
Looking at the current implementation of is_valid_ERC1271_signature_now(..)
, we can see that the return value is always checked to be the magic value in order to return true, otherwise, the function should return false.
This is the same behavior implemented in OpenZeppelin's SignatureChecker library.
The only difference is that in the current implementation in vyper, if you call is_valid_ERC1271_signature_now(..)
on a contract that doesn't implement isValidSignature(..)
it will revert the call.
IMO, this should be changed to match the OpenZeppelin behavior, where in case of revert (The signer doesn't implement isValidSignature(..)
nor have the default function) it should return false.
Because the whole purpose of having a utility function like this is to abstract the checks that need to be done after an isValidSignature(..)
call. The person implementing this function just uses it to know whether the signature was valid or invalid and shouldn't expect a revert.
Let me know what do you think.
This code snippet:
return_data: Bytes[32] = \
raw_call(signer, _abi_encode(hash, signature, method_id=IERC1271_ISVALIDSIGNATURE_SELECTOR), max_outsize=32, is_static_call=True)
return ((len(return_data) == 32) and (convert(return_data, bytes32) == convert(IERC1271_ISVALIDSIGNATURE_SELECTOR, bytes32)))
Needs to be changed to something like this: add the revert_on_failure
flag, and in the return line add that it should be a success and ..
success: bool = False
response: Bytes[32] = b""
success, response = \
raw_call(signer, _abi_encode(hash, signature, method_id=IERC1271_ISVALIDSIGNATURE_SELECTOR), max_outsize=32, is_static_call=True, revert_on_failure=False)
return (success and (len(response) == 32) and (convert(response, bytes32) == convert(IERC1271_ISVALIDSIGNATURE_SELECTOR, bytes32)))
0.3.7+commit.6020b8b
No response
In light of the recent malleability issue raised by OpenZeppelin, we need to think about how to implement or patch it. In any case, anyone that uses signatures should not use them as identifiers. In the current version, I have put a warning comment that describes the potential security risk of the function recover_sig
.
Vyper implementation of https://github.com/pcaversaccio/batch-distributor.
The new Solidity release 0.8.18
should be released this or next week (see here).
Write a Vyper implementation similar to Multicall3.sol
or Multicall.sol
.
It's planned by block explorers to use @custom:contract-name
as the contract name and use @title
as a fallback. We should implement this NatSpec field for all π snekmate contracts.
Implement CodeSpell in the CI: https://github.com/codespell-project/actions-codespell:
codespell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run CodeSpell
uses: codespell-project/[email protected]
with:
check_filenames: true
skip: yarn.lock
π snekmate does not currently have an official style guide for contributors to follow. Once we've implemented the ERC-1155
and ERC-4626
contracts and done a little cleanup, we should focus on the official style guide that needs to be followed. Importantly, it should be easily verifiable and enforceable via GitHub actions. There is an unofficial Vyper formatter called mamushi (fork of the popular Black formatter) that we should also investigate.
The Vyper implementation of OpenZeppelin'sΒ AccessControl
contract.
See OpenZeppelin/openzeppelin-contracts#4583. We should remove the functions increase_allowance
and decrease_allowance
from the ERC20
and ERC4626
contracts as they only solve an imaginary problem and could be used to bypass spending restrictions in smart contract wallets. The reason I added it firsthand was to be consistent with the OpenZeppelin/Solady implementations to avoid any confusion. The PR that implements this change requires a CHANGELOG
entry.
The Vyper implementation of OpenZeppelin's Ownable2Step
contract.
The current test suite does not entail any fuzzing. We should implement appropriate fuzz tests, where it makes sense.
The Vyper implementation of OpenZeppelin's Ownable
contract.
Originally posted by jaglinux December 31, 2022
We need to return excess token in distribute_token(), similar to distribute_ether() which refunds excess ether to msg.sender
If this is ok, I will work on the PR to add the code and test case.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.