Skip to content
Open
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: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ jobs:

- name: Install foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: 'v1.2.3'

- name: Show Forge version
working-directory: ./tee-worker/omni-executor/contracts/aa
Expand Down
10 changes: 10 additions & 0 deletions tee-worker/omni-executor/contracts/aa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ licensed under the GNU General Public License v3.0.

## Compiling

Install all required git submodules with

`git submodule update --init --recursive`

`forge compile`

## Running tests
Expand Down Expand Up @@ -125,3 +129,9 @@ Each deployment artifact includes:
The included bytecode can be used to verify deployed contracts on block explorers or to ensure the deployed code matches the source.

Also see [DEPLOYMENT.md](./DEPLOYMENT.md) for more information.

### Versioning

All changes resulting in bytecode change should be properly versioned using new files suffixed with V{N} where N is a version number.
Each version directory should contain all files required to build contracts, there should be no cross version imports.
Work on new version begins when first change is introduced after previous version deployment and continues until deployment.
8 changes: 8 additions & 0 deletions tee-worker/omni-executor/contracts/aa/foundry.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"../lib/forge-libs/forge-std": {
"rev": "60acb7aaadcce2d68e52986a0a66fe79f07d138f"
},
"../lib/forge-libs/openzeppelin-contracts": {
"rev": "f27019d48eee32551e5c9d31849afcaa99944545"
}
}
8 changes: 4 additions & 4 deletions tee-worker/omni-executor/contracts/aa/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ pragma solidity ^0.8.28;

import "forge-std/Script.sol";
import "forge-std/console.sol";
import "../src/core/EntryPointV1.sol";
import "../src/accounts/OmniAccountFactoryV1.sol";
import "../src/core/SimplePaymaster.sol";
import "../src/core/ERC20PaymasterV1.sol";
import "../src/v1/core/EntryPointV1.sol";
import "../src/v1/accounts/OmniAccountFactoryV1.sol";
import "../src/v1/core/SimplePaymaster.sol";
import "../src/v1/core/ERC20PaymasterV1.sol";
import "./DeploymentHelper.sol";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
pragma solidity ^0.8.28;

import "forge-std/Script.sol";
import "../src/core/EntryPointV1.sol";
import "../src/accounts/OmniAccountFactoryV1.sol";
import "../src/core/SimplePaymaster.sol";
import "../src/v1/core/EntryPointV1.sol";
import "../src/v1/accounts/OmniAccountFactoryV1.sol";
import "../src/v1/core/SimplePaymaster.sol";
import "../src/TestToken.sol";

contract DeployLocal is Script {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ pragma solidity ^0.8.28;

import "forge-std/Script.sol";
import "forge-std/console.sol";
import "../src/core/EntryPointV1.sol";
import "../src/accounts/OmniAccountFactoryV1.sol";
import "../src/core/SimplePaymaster.sol";
import "../src/core/DemoPaymaster.sol";
import "../src/core/ERC20PaymasterV1.sol";
import "../src/v1/core/EntryPointV1.sol";
import "../src/v1/accounts/OmniAccountFactoryV1.sol";
import "../src/v1/core/SimplePaymaster.sol";
import "../src/v1/core/DemoPaymaster.sol";
import "../src/v1/core/ERC20PaymasterV1.sol";
import "../src/TestToken.sol";
import "./DeploymentHelper.sol";

Expand Down
31 changes: 24 additions & 7 deletions tee-worker/omni-executor/contracts/aa/script/DeploymentHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,15 @@ library DeploymentHelper {
* @notice Reads the ABI from Foundry artifacts
* @return The ABI as a JSON string
*/
function getContractAbi(Vm, /* vm */ string memory /* contractName */ ) internal pure returns (string memory) {
function getContractAbi(
Vm,
/* vm */
string memory /* contractName */
)
internal
pure
returns (string memory)
{
// Due to Solidity limitations with JSON parsing, especially for arrays,
// we'll return a placeholder. In production, use a post-deployment script
// to enrich the deployment artifacts with ABIs from the Foundry artifacts.
Expand Down Expand Up @@ -481,8 +489,9 @@ library DeploymentHelper {
returns (uint256)
{
try vm.projectRoot() returns (string memory root) {
string memory broadcastPath =
string(abi.encodePacked(root, "/broadcast/", scriptName, "/", vm.toString(chainId), "/run-latest.json"));
string memory broadcastPath = string(
abi.encodePacked(root, "/broadcast/", scriptName, "/", vm.toString(chainId), "/run-latest.json")
);

try vm.readFile(broadcastPath) returns (string memory json) {
return parseBlockNumberFromBroadcast(vm, json, contractAddress);
Expand Down Expand Up @@ -517,26 +526,34 @@ library DeploymentHelper {
return block.number;
} catch {
// .receipts should be an array, try to get its length
try vm.parseJson(broadcastJson, ".receipts") returns (bytes memory /* receiptsData */ ) {
try vm.parseJson(broadcastJson, ".receipts") returns (
bytes memory /* receiptsData */
) {
// Look for transactions that created contracts
// We'll check the first few receipts for contract creation (to field is null or empty)
for (uint256 i = 0; i < 20; i++) {
// Check up to 20 transactions
try vm.parseJsonString(
broadcastJson, string(abi.encodePacked(".receipts[", vm.toString(i), "].to"))
) returns (string memory toAddr) {
) returns (
string memory toAddr
) {
// If 'to' is null/empty, this is a contract creation transaction
if (bytes(toAddr).length == 0 || keccak256(bytes(toAddr)) == keccak256(bytes("null"))) {
try vm.parseJsonString(
broadcastJson,
string(abi.encodePacked(".receipts[", vm.toString(i), "].contractAddress"))
) returns (string memory contractAddr) {
) returns (
string memory contractAddr
) {
if (keccak256(bytes(toLowerCase(contractAddr))) == keccak256(bytes(addressLower))) {
// Found our contract! Get its block number
try vm.parseJsonString(
broadcastJson,
string(abi.encodePacked(".receipts[", vm.toString(i), "].blockNumber"))
) returns (string memory blockHex) {
) returns (
string memory blockHex
) {
return hexStringToUint(blockHex);
} catch {
return block.number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,10 @@ contract OmniAccountFactoryV1 {
return OmniAccountV1(payable(addr));
}
ret = OmniAccountV1(
payable(
new ERC1967Proxy{salt: oa}(
payable(new ERC1967Proxy{salt: oa}(
address(accountImplementation),
abi.encodeCall(OmniAccountV1.initialize, (oa, oaType, clientId, root))
)
)
))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ contract OmniAccountV1 is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, In
bytes4 private constant ADD_ROOT_SIGNER_SELECTOR = bytes4(keccak256("addRootSigner(address)"));
bytes4 private constant REMOVE_ROOT_SIGNER_SELECTOR = bytes4(keccak256("removeRootSigner(address)"));
bytes4 private constant ADD_PASSKEY_SIGNER_SELECTOR = bytes4(keccak256("addPasskeySigner((uint256,uint256))"));
bytes4 private constant REMOVE_PASSKEY_SIGNER_SELECTOR = bytes4(keccak256("removePasskeySigner((uint256,uint256))"));
bytes4 private constant REMOVE_PASSKEY_SIGNER_SELECTOR =
bytes4(keccak256("removePasskeySigner((uint256,uint256))"));
bytes4 private constant WITHDRAW_DEPOSIT_SELECTOR = bytes4(keccak256("withdrawDepositTo(address,uint256)"));
bytes4 private constant UPGRADE_TO_AND_CALL_SELECTOR = bytes4(keccak256("upgradeToAndCall(address,bytes)"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ contract DemoPaymaster is BasePaymaster {
* Validate a user operation.
* Sponsors any operation as long as we have sufficient deposit.
*/
function _validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32, /* userOpHash */ uint256 maxCost)
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32,
/* userOpHash */
uint256 maxCost
)
internal
view
override
Expand All @@ -51,7 +56,10 @@ contract DemoPaymaster is BasePaymaster {
bytes calldata context,
uint256 actualGasCost,
uint256 /* actualUserOpFeePerGas */
) internal override {
)
internal
override
{
// Decode sender from context
address sender = abi.decode(context, (address));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ contract ERC20PaymasterV1 is BasePaymaster, ReentrancyGuard {
struct PaymasterData {
address token; // ERC20 token address (must not be address(0))
uint256 exchangeRate; // Exchange rate: how many token units per 1 wei of ETH
// For tokens with different decimals, this should account for the difference
// Example: For 6-decimal USDC at $2000/ETH: rate = 2000 * 10^6 = 2000000000
// Example: For 18-decimal token at 1500:1 ratio: rate = 1500 * 10^18
// Set to 0 for full sponsorship (no token charge)
// For tokens with different decimals, this should account for the difference
// Example: For 6-decimal USDC at $2000/ETH: rate = 2000 * 10^6 = 2000000000
// Example: For 18-decimal token at 1500:1 ratio: rate = 1500 * 10^18
// Set to 0 for full sponsorship (no token charge)
uint256 validUntil; // Timestamp until when this exchange rate is valid
uint256 validAfter; // Timestamp after which this exchange rate is valid
}
Expand Down Expand Up @@ -83,7 +83,12 @@ contract ERC20PaymasterV1 is BasePaymaster, ReentrancyGuard {
/**
* Validate a user operation and handle ERC20 token prefunding
*/
function _validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32, /* userOpHash */ uint256 maxCost)
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32,
/* userOpHash */
uint256 maxCost
)
internal
override
returns (bytes memory context, uint256 validationData)
Expand Down Expand Up @@ -207,11 +212,12 @@ contract ERC20PaymasterV1 is BasePaymaster, ReentrancyGuard {
// since no prefunding occurred during validation
if (actualTokenCost > 0) {
// Use low-level call to prevent revert on failed charge
(bool success,) = postOpContext.token.call(
abi.encodeWithSelector(
IERC20.transferFrom.selector, postOpContext.sender, beneficiary, actualTokenCost
)
);
(bool success,) = postOpContext.token
.call(
abi.encodeWithSelector(
IERC20.transferFrom.selector, postOpContext.sender, beneficiary, actualTokenCost
)
);
if (!success) {
// Charge failed, but don't revert the entire operation
// The approval succeeded, but gas payment failed
Expand All @@ -227,9 +233,8 @@ contract ERC20PaymasterV1 is BasePaymaster, ReentrancyGuard {
if (beneficiary == address(this)) {
// If beneficiary is this contract, we can refund directly
// Use low-level call to prevent revert on failed refund
(bool success,) = postOpContext.token.call(
abi.encodeWithSelector(IERC20.transfer.selector, postOpContext.sender, refundAmount)
);
(bool success,) = postOpContext.token
.call(abi.encodeWithSelector(IERC20.transfer.selector, postOpContext.sender, refundAmount));
if (!success) {
// Refund failed, but don't revert the entire operation
emit UserOpSponsored(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.28;
// solhint-disable no-inline-assembly

import "../interfaces/PackedUserOperation.sol";
import "../core/UserOperationLib.sol";
import "./UserOperationLib.sol";

library Eip7702Support {
// EIP-7702 code prefix before delegate address.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ contract EntryPointSimulations is EntryPointV1, IEntryPointSimulations {
initSenderCreator();

try this.validateSenderAndPaymaster(userOp.initCode, userOp.sender, userOp.paymasterAndData) {
// solhint-disable-next-line no-empty-blocks
} catch Error(string memory revertReason) {
// solhint-disable-next-line no-empty-blocks
}
catch Error(string memory revertReason) {
if (bytes(revertReason).length != 0) {
revert FailedOp(0, revertReason);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ contract EntryPointV1 is IEntryPoint, StakeManager, NonceManager, ReentrancyGuar
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
// note: solidity "type(IEntryPoint).interfaceId" is without inherited methods but we want to check everything
return interfaceId
== (type(IEntryPoint).interfaceId ^ type(IStakeManager).interfaceId ^ type(INonceManager).interfaceId)
== (type(IEntryPoint).interfaceId ^ type(IStakeManager).interfaceId ^ type(INonceManager).interfaceId)
|| interfaceId == type(IEntryPoint).interfaceId || interfaceId == type(IStakeManager).interfaceId
|| interfaceId == type(INonceManager).interfaceId || super.supportsInterface(interfaceId);
}
Expand Down Expand Up @@ -593,16 +593,15 @@ contract EntryPointV1 is IEntryPoint, StakeManager, NonceManager, ReentrancyGuar
uint256 maxContextLength;
uint256 len;
assembly ("memory-safe") {
success :=
call(
paymasterVerificationGasLimit,
paymaster,
0,
add(validatePaymasterCall, 0x20),
mload(validatePaymasterCall),
0,
0
)
success := call(
paymasterVerificationGasLimit,
paymaster,
0,
add(validatePaymasterCall, 0x20),
mload(validatePaymasterCall),
0,
0
)
len := returndatasize()
// return data from validatePaymasterUserOp is (bytes context, validationData)
// encoded as:
Expand Down Expand Up @@ -777,8 +776,9 @@ contract EntryPointV1 is IEntryPoint, StakeManager, NonceManager, ReentrancyGuar
try IPaymaster(paymaster).postOp{gas: mUserOp.paymasterPostOpGasLimit}(
mode, context, actualGasCost, gasPrice
) {
// solhint-disable-next-line no-empty-blocks
} catch {
// solhint-disable-next-line no-empty-blocks
}
catch {
bytes memory reason = Exec.getReturnData(REVERT_REASON_MAX_LEN);
revert PostOpReverted(reason);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ contract SimplePaymaster is BasePaymaster {
* Validate a user operation.
* Only sponsor operations submitted by authorized bundlers.
*/
function _validatePaymasterUserOp(PackedUserOperation calldata userOp, bytes32, /* userOpHash */ uint256 maxCost)
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32,
/* userOpHash */
uint256 maxCost
)
internal
view
override
Expand Down Expand Up @@ -61,7 +66,10 @@ contract SimplePaymaster is BasePaymaster {
bytes calldata context,
uint256 actualGasCost,
uint256 /* actualUserOpFeePerGas */
) internal override {
)
internal
override
{
// Decode sender from context
address sender = abi.decode(context, (address));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ interface IEntryPoint is IStakeManager, INonceManager {
* @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts).
* @param beneficiary - The address to receive the fees.
*/
function handleAggregatedOps(UserOpsPerAggregator[] calldata opsPerAggregator, address payable beneficiary)
external;
function handleAggregatedOps(UserOpsPerAggregator[] calldata opsPerAggregator, address payable beneficiary) external;

/**
* Generate a request Id - unique identifier for this request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ enum OwnerType {
Solana, // 0x08
Google, // 0x09
Passkey // 0x0a

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,4 @@ enum UserOpSigner {
RootKey, // 0x01
SessionKey, // 0x02
Passkey // 0x03

}
Loading
Loading