Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/p256-verifier
2 changes: 1 addition & 1 deletion lib/solady
Submodule solady updated 68 files
+496 −473 .gas-snapshot
+10 −0 .github/issue_template.md
+36 −0 .github/workflows/ci-wake.yml
+0 −40 .github/workflows/ci-woke.yml
+0 −3 .github/workflows/ci.yml
+2 −3 .gitignore
+1 −0 README.md
+0 −0 ext/wake/EIP712Mock.sol
+0 −0 ext/wake/ERC1155Mock.sol
+0 −0 ext/wake/ERC20Mock.sol
+0 −0 ext/wake/ERC721Mock.sol
+0 −0 ext/wake/MerkleProofMock.sol
+0 −0 ext/wake/NoETHMock.sol
+0 −0 ext/wake/SignatureCheckerMock.sol
+0 −0 ext/wake/__init__.py
+1 −3 ext/wake/test_eip712.py
+2 −3 ext/wake/test_eip712_fuzz.py
+1 −1 ext/wake/test_erc1155.py
+2 −3 ext/wake/test_erc1155_fuzz.py
+2 −18 ext/wake/test_erc20.py
+2 −3 ext/wake/test_erc721_fuzz.py
+2 −6 ext/wake/test_merkle_proof.py
+2 −3 ext/wake/test_merkle_proof_fuzz.py
+2 −3 ext/wake/test_signature_checker_fuzz.py
+1 −1 ext/wake/utils.py
+4 −5 ext/wake/wake.toml
+0 −0 ext/wake/weird/Approval.sol
+0 −0 ext/wake/weird/ApprovalToZero.sol
+0 −0 ext/wake/weird/BlockList.sol
+0 −0 ext/wake/weird/Bytes32Metadata.sol
+0 −0 ext/wake/weird/DaiPermit.sol
+0 −0 ext/wake/weird/ERC20.sol
+0 −0 ext/wake/weird/HighDecimals.sol
+0 −0 ext/wake/weird/LowDecimals.sol
+0 −0 ext/wake/weird/MissingReturns.sol
+0 −0 ext/wake/weird/NoRevert.sol
+0 −0 ext/wake/weird/Pausable.sol
+0 −0 ext/wake/weird/Proxied.sol
+0 −0 ext/wake/weird/Reentrant.sol
+0 −0 ext/wake/weird/ReturnsFalse.sol
+0 −0 ext/wake/weird/RevertToZero.sol
+0 −0 ext/wake/weird/RevertZero.sol
+0 −0 ext/wake/weird/TransferFee.sol
+0 −0 ext/wake/weird/Uint96.sol
+0 −0 ext/wake/weird/Upgradable.sol
+0 −27 ext/woke/woke.toml
+1 −1 package.json
+1 −0 src/Milady.sol
+1 −3 src/accounts/ERC4337.sol
+41 −56 src/tokens/ERC6909.sol
+198 −79 src/utils/FixedPointMathLib.sol
+25 −0 src/utils/GasBurnerLib.sol
+40 −13 src/utils/LibString.sol
+12 −15 src/utils/LibZip.sol
+13 −0 src/utils/Multicallable.sol
+18 −24 src/utils/UUPSUpgradeable.sol
+2 −1 test/ERC1967Factory.t.sol
+4 −4 test/ERC6551.t.sol
+78 −24 test/ERC6909.t.sol
+555 −2 test/FixedPointMathLib.t.sol
+31 −0 test/GasBurnerLib.t.sol
+65 −0 test/LibString.t.sol
+4 −4 test/UUPSUpgradeable.t.sol
+5 −0 test/utils/TestPlus.sol
+2 −4 test/utils/forge-std/Script.sol
+0 −1,581 test/utils/forge-std/console.sol
+0 −1 test/utils/mocks/MockERC6909.sol
+6 −2 test/utils/mocks/MockUUPSImplementation.sol
77 changes: 65 additions & 12 deletions src/validator/WeightedECDSAValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ enum ProposalStatus {
struct ProposalStorage {
ProposalStatus status;
ValidAfter validAfter;
uint24 weightApproved;
}
// TODO add validUntil

enum VoteStatus {
NA,
Expand All @@ -49,7 +49,7 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
voteStatus;

function _domainNameAndVersion() internal pure override returns (string memory, string memory) {
return ("WeightedECDSAValidator", "1");
return ("WeightedECDSAValidator", "0.0.1");
}

function enable(bytes calldata _data) external payable override {
Expand All @@ -59,6 +59,7 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
require(weightedStorage[msg.sender].totalWeight == 0, "Already enabled");
weightedStorage[msg.sender].firstGuardian = msg.sender;
for (uint256 i = 0; i < _guardians.length; i++) {
require(_guardians[i] != msg.sender, "Guardian cannot be self");
require(_guardians[i] != address(0), "Guardian cannot be 0");
require(_weights[i] != 0, "Weight cannot be 0");
require(guardian[_guardians[i]][msg.sender].weight == 0, "Guardian already enabled");
Expand All @@ -82,22 +83,52 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
delete weightedStorage[msg.sender];
}

function renew(address[] calldata _guardians, uint24[] calldata _weights, uint24 _threshold, uint48 _delay)
external
payable
{
require(weightedStorage[msg.sender].totalWeight != 0, "Not enabled");
address currentGuardian = weightedStorage[msg.sender].firstGuardian;
while (currentGuardian != msg.sender) {
address nextGuardian = guardian[currentGuardian][msg.sender].nextGuardian;
delete guardian[currentGuardian][msg.sender];
currentGuardian = nextGuardian;
}
delete weightedStorage[msg.sender];
require(_guardians.length == _weights.length, "Length mismatch");
weightedStorage[msg.sender].firstGuardian = msg.sender;
for (uint256 i = 0; i < _guardians.length; i++) {
require(_guardians[i] != msg.sender, "Guardian cannot be self");
require(_guardians[i] != address(0), "Guardian cannot be 0");
require(_weights[i] != 0, "Weight cannot be 0");
require(guardian[_guardians[i]][msg.sender].weight == 0, "Guardian already enabled");
guardian[_guardians[i]][msg.sender] =
GuardianStorage({weight: _weights[i], nextGuardian: weightedStorage[msg.sender].firstGuardian});
weightedStorage[msg.sender].firstGuardian = _guardians[i];
weightedStorage[msg.sender].totalWeight += _weights[i];
}
weightedStorage[msg.sender].delay = _delay;
weightedStorage[msg.sender].threshold = _threshold;
}

function approve(bytes32 _callDataAndNonceHash, address _kernel) external {
require(guardian[msg.sender][_kernel].weight != 0, "Guardian not enabled");
require(weightedStorage[_kernel].threshold != 0, "Kernel not enabled");
ProposalStorage storage proposal = proposalStatus[_callDataAndNonceHash][_kernel];
require(proposal.status == ProposalStatus.Ongoing, "Proposal not ongoing");
VoteStorage storage vote = voteStatus[_callDataAndNonceHash][msg.sender][_kernel];
require(vote.status == VoteStatus.NA, "Already voted");
vote.status = VoteStatus.Approved;
proposal.weightApproved += guardian[msg.sender][_kernel].weight;
if (proposal.weightApproved >= weightedStorage[_kernel].threshold) {
(, bool isApproved) = getApproval(_kernel, _callDataAndNonceHash);
if (isApproved) {
proposal.status = ProposalStatus.Approved;
proposal.validAfter = ValidAfter.wrap(uint48(block.timestamp + weightedStorage[_kernel].delay));
}
}

function approveWithSig(bytes32 _callDataAndNonceHash, address _kernel, bytes calldata sigs) external {
uint256 sigCount = sigs.length / 65;
require(weightedStorage[_kernel].threshold != 0, "Kernel not enabled");
ProposalStorage storage proposal = proposalStatus[_callDataAndNonceHash][_kernel];
require(proposal.status == ProposalStatus.Ongoing, "Proposal not ongoing");
for (uint256 i = 0; i < sigCount; i++) {
Expand All @@ -110,9 +141,10 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
VoteStorage storage vote = voteStatus[_callDataAndNonceHash][signer][_kernel];
require(vote.status == VoteStatus.NA, "Already voted");
vote.status = VoteStatus.Approved;
proposal.weightApproved += guardian[signer][_kernel].weight;
}
if (proposal.weightApproved >= weightedStorage[_kernel].threshold) {

(, bool isApproved) = getApproval(_kernel, _callDataAndNonceHash);
if (isApproved) {
proposal.status = ProposalStatus.Approved;
proposal.validAfter = ValidAfter.wrap(uint48(block.timestamp + weightedStorage[_kernel].delay));
}
Expand All @@ -132,21 +164,25 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
payable
returns (ValidationData validationData)
{
bytes32 callDataAndNonceHash = keccak256(abi.encode(userOp.callData, userOp.nonce));
bytes32 callDataAndNonceHash = keccak256(abi.encode(userOp.sender, userOp.callData, userOp.nonce));
ProposalStorage storage proposal = proposalStatus[callDataAndNonceHash][msg.sender];
WeightedECDSAValidatorStorage storage strg = weightedStorage[msg.sender];
if (proposal.status == ProposalStatus.Ongoing) {
if (strg.threshold == 0) {
return SIG_VALIDATION_FAILED;
}
(uint256 totalWeight, bool passed) = getApproval(msg.sender, callDataAndNonceHash);
uint256 threshold = strg.threshold;
if (proposal.status == ProposalStatus.Ongoing && !passed) {
if (strg.delay != 0) {
// if delay > 0, only allow proposal to be approved before execution
return SIG_VALIDATION_FAILED;
}
bytes calldata sig = userOp.signature;
// parse sig with 65 bytes
uint256 sigCount = sig.length / 65;
uint256 totalWeight = proposal.weightApproved;
address signer;
VoteStorage storage vote;
for (uint256 i = 0; i < sigCount; i++) {
for (uint256 i = 0; i < sigCount && !passed; i++) {
// last sig is for userOpHash verification
signer = ECDSA.recover(
_hashTypedData(
Expand All @@ -160,21 +196,38 @@ contract WeightedECDSAValidator is EIP712, IKernelValidator {
} // skip if already voted
vote.status = VoteStatus.Approved;
totalWeight += guardian[signer][msg.sender].weight;
if (totalWeight >= threshold) {
passed = true;
}
}
if (totalWeight >= strg.threshold) {
if (passed) {
validationData = packValidationData(ValidAfter.wrap(0), ValidUntil.wrap(0));
proposal.status = ProposalStatus.Executed;
} else {
validationData = SIG_VALIDATION_FAILED;
}
} else if (proposal.status == ProposalStatus.Approved) {
} else if (proposal.status == ProposalStatus.Approved || passed) {
validationData = packValidationData(proposal.validAfter, ValidUntil.wrap(0));
proposal.status = ProposalStatus.Executed;
} else {
return SIG_VALIDATION_FAILED;
}
}

function getApproval(address kernel, bytes32 hash) public view returns (uint256 approvals, bool passed) {
WeightedECDSAValidatorStorage storage strg = weightedStorage[kernel];
for (
address currentGuardian = strg.firstGuardian;
currentGuardian != address(0);
currentGuardian = guardian[currentGuardian][kernel].nextGuardian
) {
if (voteStatus[hash][currentGuardian][kernel].status == VoteStatus.Approved) {
approvals += guardian[currentGuardian][kernel].weight;
}
}
passed = approvals >= strg.threshold;
}

function validCaller(address, bytes calldata) external pure override returns (bool) {
return false;
}
Expand Down