Skip to content

Commit

Permalink
remove forge cache + add dependencies + add reference
Browse files Browse the repository at this point in the history
  • Loading branch information
livingrockrises committed Apr 25, 2023
1 parent 40e8db2 commit 72cf893
Show file tree
Hide file tree
Showing 3 changed files with 2,933 additions and 85 deletions.
152 changes: 152 additions & 0 deletions contracts/references/AdvancedVerifyingPaymaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.17;

import { IEntryPoint } from "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
import { UserOperation } from "@account-abstraction/contracts/interfaces/UserOperation.sol";
import { UserOperationLib } from "@account-abstraction/contracts/interfaces/UserOperation.sol";
import { BasePaymaster } from "@account-abstraction/contracts/core/BasePaymaster.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import "@account-abstraction/contracts/core/Helpers.sol" as Helpers;

// Stackup paymaster
/**
* A paymaster based on the eth-infinitism sample VerifyingPaymaster contract.
* It has the same functionality as the sample, but with added support for withdrawing ERC20 tokens.
* All withdrawn tokens will be transferred to the owner address.
* Note that the off-chain signer should have a strategy in place to handle a failed token withdrawal.
*
* See account-abstraction/contracts/samples/VerifyingPaymaster.sol for detailed comments.
*/
contract StackupVerifyingPaymaster is BasePaymaster {
using ECDSA for bytes32;
using UserOperationLib for UserOperation;
using SafeERC20 for IERC20;

mapping(address => uint256) public senderNonce;

uint256 private constant VALID_PND_OFFSET = 20;

uint256 private constant SIGNATURE_OFFSET = 148;

uint256 public constant POST_OP_GAS = 35000;

constructor(IEntryPoint _entryPoint, address _owner) BasePaymaster(_entryPoint) {
_transferOwnership(_owner);
}

function pack(UserOperation calldata userOp) internal pure returns (bytes memory ret) {
bytes calldata pnd = userOp.paymasterAndData;
// solhint-disable-next-line no-inline-assembly
assembly {
let ofs := userOp
let len := sub(sub(pnd.offset, ofs), 32)
ret := mload(0x40)
mstore(0x40, add(ret, add(len, 32)))
mstore(ret, len)
calldatacopy(add(ret, 32), ofs, len)
}
}

function getHash(
UserOperation calldata userOp,
uint48 validUntil,
uint48 validAfter,
address erc20Token,
uint256 exchangeRate
) public view returns (bytes32) {
return
keccak256(
abi.encode(
pack(userOp),
block.chainid,
address(this),
senderNonce[userOp.getSender()],
validUntil,
validAfter,
erc20Token,
exchangeRate
)
);
}

function _validatePaymasterUserOp(
UserOperation calldata userOp,
bytes32 /*userOpHash*/,
uint256 requiredPreFund
) internal override returns (bytes memory context, uint256 validationData) {
(requiredPreFund);

(
uint48 validUntil,
uint48 validAfter,
address erc20Token,
uint256 exchangeRate,
bytes calldata signature
) = parsePaymasterAndData(userOp.paymasterAndData);
// solhint-disable-next-line reason-string
require(
signature.length == 64 || signature.length == 65,
"VerifyingPaymaster: invalid signature length in paymasterAndData"
);
bytes32 hash = ECDSA.toEthSignedMessageHash(getHash(userOp, validUntil, validAfter, erc20Token, exchangeRate));
senderNonce[userOp.getSender()]++;
context = "";
if (erc20Token != address(0)) {
context = abi.encode(
userOp.sender,
erc20Token,
exchangeRate,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas
);
}

if (owner() != ECDSA.recover(hash, signature)) {
return (context, Helpers._packValidationData(true, validUntil, validAfter));
}

return (context, Helpers._packValidationData(false, validUntil, validAfter));
}

function _postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) internal override {
(address sender, IERC20 token, uint256 exchangeRate, uint256 maxFeePerGas, uint256 maxPriorityFeePerGas) = abi
.decode(context, (address, IERC20, uint256, uint256, uint256));

uint256 opGasPrice;
unchecked {
if (maxFeePerGas == maxPriorityFeePerGas) {
opGasPrice = maxFeePerGas;
} else {
opGasPrice = Math.min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
}
}

uint256 actualTokenCost = ((actualGasCost + (POST_OP_GAS * opGasPrice)) * exchangeRate) / 1e18;
if (mode != PostOpMode.postOpReverted) {
token.safeTransferFrom(sender, owner(), actualTokenCost);
}
}

function parsePaymasterAndData(
bytes calldata paymasterAndData
)
public
pure
returns (
uint48 validUntil,
uint48 validAfter,
address erc20Token,
uint256 exchangeRate,
bytes calldata signature
)
{
(validUntil, validAfter, erc20Token, exchangeRate) = abi.decode(
paymasterAndData[VALID_PND_OFFSET:SIGNATURE_OFFSET],
(uint48, uint48, address, uint256)
);
signature = paymasterAndData[SIGNATURE_OFFSET:];
}
}
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,31 @@
"ts-node": "^10.9.1",
"typechain": "^8.1.1",
"typescript": "^5.0.4"
},
"dependencies": {
"@account-abstraction/contracts": "^0.6.0",
"@chainlink/contracts": "^0.4.1",
"@ethersproject/abstract-signer": "^5.6.2",
"@ethersproject/constants": "^5.6.1",
"@openzeppelin/contracts": "4.8.1",
"@openzeppelin/contracts-upgradeable": "4.8.1",
"@typechain/hardhat": "^2.3.0",
"@types/mocha": "^9.0.0",
"chai-as-promised": "^7.1.1",
"chai-string": "^1.5.0",
"eth-gas-reporter": "^0.2.24",
"ethereumjs-util": "^7.1.0",
"ethereumjs-wallet": "^1.0.1",
"ethers": "^5.6.8",
"ganache": "^7.1.0",
"ganache-cli": "^6.12.2",
"hardhat": "^2.9.5",
"hardhat-deploy": "^0.9.3",
"hardhat-deploy-ethers": "^0.3.0-beta.11",
"hardhat-gas-reporter": "^1.0.7",
"solc": "^0.8.15",
"solidity-bytes-utils": "^0.8.0",
"source-map-support": "^0.5.19",
"typescript": "^4.3.5"
}
}
Loading

0 comments on commit 72cf893

Please sign in to comment.