Skip to content

tkhq/gas-station

Repository files navigation

TK Gas Station lets a user have all their gas paid for by another party using metatransactions.

Deployments

All contracts are deployed at the same address across all networks:

  • TKGasStation: 0x00000000008c57a1CE37836a5e9d36759D070d8c
  • TKGasDelegate: 0x000066a00056CD44008768E2aF00696e19A30084

Ethereum Mainnet

Base Mainnet

Sepolia Testnet

Base Sepolia Testnet

Polygon Mainnet

Celo Mainnet

Arbitrum One

Optimism

Unichain Mainnet

Polygon Amoy Testnet

Monad Testnet

Overall Flow

  1. The user signs a type 4 transaction to delegate access to TKGasDelegate (EIP-7702). This can be broadcasted by the paymaster
  2. The user then signs a metatransaction (EIP-712) to give permissions to the paymaster to initiate a transaction on behalf of the user
  3. The paymaster then submits the metatransaction to the TKGasStation Transaction Flow Diagram

Security Design Decisions

  • Contracts are immutable
  • There are no re-entry protections by design. Re-entrancy should be guarded by the contracts the user is interacting with (as in a normal EoA)
    • The nonce for execute and batch execute will naturally protect against re-entrancy, but this should not be relied upon
    • There is no built in re-entrancy protection for session based auth since it is meant to be replayed
  • Both the delegate and the gas station are not using DRY. This is a purpsoseful design choice to save gas during run time
  • Paymasters (and anyone else) can interact with TKGasDelegate through the TKGasStation or directly through the delegate itself
  • The gas station has helper external functions for hashing for the type hash. This is just to help for external development and testing, and are not used during execution
  • There are session metatransactions that give one particular wallet unlimited execution on behalf of a user
    • This is a footgun and should be used carefully
    • This limits to only one wallet in the typehash
    • Each one has a counter
    • Multiple signatures (sessions) can be on a single counter
    • The counter is non sequential
    • The purpose of the counter is to act as a "log out" functionality to expire the session before the deadline - Burning this will invalidate all signatures with that counter
  • The standard execution metatransactions should limit by nonce, deadline, interacting contract, and arguments
  • Batch transactions for standard execution should share one nonce per batch and one signature that includes the whole batch
  • For session batch execution, only the session limitations of sender, counter, and deadline are verified. Not the batch
  • All execute will revert if it gets a failure. Anything interacting with the gas station should be able to handle that
  • Batch transactions are capped at 20 per batch currently
  • Burning a nonce only burns the current nonce. Ones that are premade will be valid
  • Nonces are sequential and can only be used sequentially
  • A user can burn their own counter or nonce without a 712
  • The gas delegate implements recievers for ERC-721 and ERC-1155
  • The Gas station cannot use session based auth. This is because authorizing the gas station to send arbitrary messages would enable anyone to send arbitrary messages through the gas station
  • There is no requirement for the paymaster to interact with the gas station. The paymaster can interact with the delegate directly if they trust that the user is using the right delegate by doing off-chain validation.
  • The delegate does not implement EIP-7821[https://eips.ethereum.org/EIPS/eip-7821] as described since the execute function is payable. As a security measure to not drain the paymaster, no execute functions by design are allowed to be payable
  • An attack that can be pulled off to reset/modify the nonce/counters is as follows:
    1. A user delegates and uses it as normal. The nonce iterates up
    2. The user then delegates to a contract that changes the nonce or resets it to 0 since that storage slot stays with the user's address, not the delegated contract
    3. The user then delegates back to TKGasDelegate
    4. Since the nonce is reset, old transactions can be replayed. This is accepted because we have a deadline transactions and on step 2, if you delegate to a malicious contract the attacker already has control.

Packing data for calling the fallback function in the delegate

The fall back function can call the execute and session execution functions. It does not call the burn functions

To use it: The first byte should be a null byte 0x00. No function selector in the delegate or the gas station starts with a null byte, so there would be no name collision. The gas station will just parse the target contract and then call the delegate fall back function. The second byte is a combination of the first nibble that acts as the function selector and the second nibble that acts as a boolean that says whether or not to return values or not The eth value is 10 bytes, a uint80

Function selectors without to return or not:

  • 00 - Execute
  • 10 - ApproveThenExecute
  • 20 - ExecuteBatch
  • 30 - ExecuteSession
  • 40 - ExecuteBatchSession
  • 50 - ExecuteArbitrarySession
  • 60 - ExecuteBatchArbitrarySession

For example, a normal execute with no return would be 0x00. A normal execute with a return would be 0x01.

Reporting A Vulnerability/Bug Bounty

See our documentation[https://docs.turnkey.com/security/reporting-a-vulnerability] about our bug bounty program.

Deployment

All contracts are deployed with the create2 factory with the zero address as the deployer Anyone can canonically deploy this to a new network

The deploy scripts have the salt for bot the delegate and the gas station purposefully hardcoded

The delegate should be deployed before the gas station

  1. Install Foundry if you haven't already Go to https://getfoundry.sh/introduction/installation/ and install if needed

  2. Clone the repo

git clone https://github.com/tkhq/gas-station.git

  1. Add the RPC configuration to the foundry.toml

Fill <networkName> with your desired network name. I.e. Base

[rpc_endpoints]
<networkName> = "<PUBLIC-RPC-URL>

[etherscan]
<networkName> = { key = "${ETHERSCAN_API_KEY}", url = "<NETWORK-ETHERSCAN-URL>" }
  1. Create an .env file, and add your private key and etherscan API key Create the file
cp ./env.example ./.env

In the file add your keys:

PRIVATE_KEY=your_private_key_here

# API Key for contract verification (works for both Base and Ethereum)
ETHERSCAN_API_KEY=your_etherscan_api_key_here
  1. Install and deploy
cd ./gas-station

forge install

forge build

forge script script/DeployTKGasDelegate.s.sol:DeployTKGasDelegate --rpc-url <networkName> --broadcast --verify

forge script script/DeployTKGasStation.s.sol:DeployTKGasStation --rpc-url <networkName> --broadcast --verify
  1. Add the contract addresses to the readme

About

Gas abstraction for EVM chains that support EIP-7702 using signed intents

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published