Trampoline is a chrome extension boilerplate code to showcase your own Smart Contract Wallets with React 18 and Webpack 5 support.
For any info contact the author & maintainer - Garvit Khatri (plusminushalf)
- Verify that your Node.js version is >= 18.12.0.
- Clone this repository.
- Make sure you configure the providerinsrc/exconfig.tsto theGoerlinetwork.
- Edit the bundlerURL pointing toGoerlinetwork and accepting EntryPoint=0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789
- Run yarn installto install the dependencies.
- Run yarn start
- Load your extension in Chrome by following these steps:
- Go to chrome://extensions/
- Enable Developer mode
- Click on Load unpacked extension
- Select the buildfolder.
 
- Go to 
- Happy hacking.
Warning Auto refresh is disabled by default, so you will have to manually refresh the page. If you make changes in background script or account-api, you will also have to refresh the background page. Check instructions on how to do that below.
Warning Logs of all the blockchain interactions are shown in the background script. Do keep it open for faster debugging.
- Open extension's page: chrome://extensions/
- Find the Trampoline extension, and click Details.
- Check the Inspect viewsarea and click onbackground pageto inspect it's logs
- To refresh click cmd + rorctrl + rin the background inspect page to refresh the background script.
- You can reload the extension completely too, the state is always kept in localstorage so nothing will be lost.
Config of the extension can be set in exconfig.ts file.
export default {
  // Enable or disable password for the user.
  enablePasswordEncryption: true,
  // Show default transaction screen
  showTransactionConfirmationScreen: true,
  // stateVersion is the version of state stored in localstorage of your browser. If you want to reset your extension, change this number to a new version and that will invalidate the older state.
  stateVersion: "0.1",
  // Network that your SCW supports. Currently this app only supports a single network, we will soon have support for multiple networks in future
  network: {
    chainID: "5",
    family: "EVM",
    name: "Goerli",
    provider: "https://goerli.infura.io/v3/bdabe9d2f9244005af0f566398e648da",
    entryPointAddress: "0x0F46c65C17AA6b4102046935F33301f0510B163A",
    bundler: "https://app.stackup.sh/api/v1/bundler/96771b1b09e802669c33a3fc50f517f0f514a40da6448e24640ecfd83263d336",
    baseAsset: {
      symbol: "ETH",
      name: "ETH",
      decimals: 18,
      image: "https://ethereum.org/static/6b935ac0e6194247347855dc3d328e83/6ed5f/eth-diamond-black.webp"
    }
  }
};- Make sure EntryPoint is deployed on the target network.
- Edit the entryPointAddressinsrc/exconfig.json.
- Add your network details in hardhat.config.ts.
- Deploy the factory using INFURA_ID=<required> npx hardhat deploy --network <network>.
- Edit the factory_addressinsrc/exconfig.ts
- Set bundlerinsrc/exconfig.tsto a bundler that points to your network and accepts requests for your EntryPoint.
- Run yarn start
- Run a local hardhat node with npx hardhat nodeor use the node inside the bundler repo.
- Deploy EntryPoint from the account-abstraction repo, you can find the instructions below.
- Edit the entryPointAddressinsrc/exconfig.ts.
- Deploy the factory using npx hardhat deploy --network localhost.
- Edit the factory_addressinsrc/exconfig.ts
- Start a local bunder from the bundler repo, you can find the instructions below.
- Set bundlertohttp://localhost:3000/rpcinsrc/exconfig.ts.
- Run yarn start
- Clone the repo https://github.com/eth-infinitism/account-abstraction
- Run yarn installto install the dependencies.
- Deploy EntryPoint with DEBUG=true MNEMONIC_FILE=<path-to-mnemonic-file> yarn deploy --network dev
- Clone the repo https://github.com/eth-infinitism/bundler
- Run yarn installto install the dependencies.
- Run yarn preprocessto compile all the local dependencies.
- Edit bundler.config.jsonatpackages/bundler/localconfig: a. Editnetworkto your local hardhat node b. Edit theentryPointaddress that you got while deploying it using instrunctions above. c. Change port to9000. d. Make sure your mnemonic & beneficiary are setup correctly.
- Run the bunder using yarn bundler --unsafe --port 9000
- You can change the icons at src/assets/img/icon-34.pngandsrc/assets/img/icon-128.pngfor the chrome extension.
All your extension's account code must be placed in the src/pages/Account folder.
There are two subfolders in src/pages/Account:
- account-api
- components
This folder is used to define the AccountAPI of your specific account implementation. Every implementation must implement AccountApiType.
export abstract class AccountApiType extends BaseAccountAPI {
  abstract serialize: () => Promise<object>;
  /** sign a message for the user */
  abstract signMessage: (
    request?: MessageSigningRequest,
    context?: any
  ) => Promise<string>;
  abstract createUnsignedUserOp(
    info: TransactionDetailsForUserOp,
    context?: any
  ): Promise<UserOperationStruct>;
}
export declare abstract class BaseAccountAPI {
  /**
   * return the value to put into the "initCode" field, if the contract is not yet deployed.
   * this value holds the "factory" address, followed by this account's information
   */
  abstract getAccountInitCode(): Promise<string>;
  /**
   * return current account's nonce.
   */
  abstract getNonce(): Promise<BigNumber>;
  /**
   * encode the call from entryPoint through our account to the target contract.
   * @param target
   * @param value
   * @param data
   */
  abstract encodeExecute(
    target: string,
    value: BigNumberish,
    data: string
  ): Promise<string>;
  /**
   * sign a userOp's hash (userOpHash).
   * @param userOpHash
   */
  abstract signUserOpHash(userOpHash: string): Promise<string>;
}The boilerplate includes a SimpleAccountImplementation by Eth-Infinitism, which you can find here.
This folder is used to define the components that will be used in the Chrome extension. This folder should contain two subfolders.
- onboarding
- sign-message
- transaction
The onboarding folder defines the component that will be displayed to the user during the creation of a new wallet. You can display custom information or collect user inputs if needed.
The signature of the OnboardingComponent is defined as follows.
export interface OnboardingComponentProps {
  onOnboardingComplete: (context?: any) => void;
}
export interface OnboardingComponent
  extends React.FC<OnboardingComponentProps> {}Once the component has collected enough information from the user, it should pass the collected information to onOnboardingComplete as the context parameter. This context will be passed on to your account-api
The signature of the account-api is as follows, which shows how the context will be passed:
export interface AccountApiParamsType extends BaseApiParams {
  context?: any;
}
export type AccountImplementationType = new (
  params: AccountApiParamsType
) => AccountApiType;The sign-message folder defines the component that will be displayed to the user whenever the dapp requests the user to sign any message, i.e. dapp calls personal_sign RPC method. You can display custom information or collect user inputs if needed.
The signature of the SignMessageComponenet is defined as follows.
export interface SignMessageComponenetProps {
  onComplete: (context?: any) => Promise<void>;
}
export interface SignMessageComponenet
  extends React.FC<SignMessageComponenetProps> {}Once the component has collected enough information from the user, it should pass the collected information to onComplete as the context parameter. This context will be passed on to your signMessage function of account-api
The signature of the signMessage is as follows, which shows how the context will be passed:
  /** sign a message for the user */
  abstract signMessage: (
    request?: MessageSigningRequest,
    context?: any
  ) => Promise<string>;The transaction folder defines the component that will be displayed to the user whenever the dapp requests to initiate a transaction, i.e. dapp calls eth_sendTransaction RPC method. You can display custom information or collect user inputs if needed.
The signature of the TransactionComponent is defined as follows.
export interface TransactionComponentProps {
  transaction: EthersTransactionRequest;
  onComplete: (
    modifiedTransaction: EthersTransactionRequest,
    context?: any
  ) => Promise<void>;
}
export interface TransactionComponent
  extends React.FC<TransactionComponentProps> {}Once the component has collected enough information from the user, it should pass the collected information to onComplete as the context parameter. You can also modify the transaction if you want and return it also as a parameter of onComplete function. This context and modifiedTransaction will be passed on to your createUnsignedUserOp function of account-api
The signature of the createUnsignedUserOp is as follows, which shows how the context will be passed:
  /** sign a message for the user */
  abstract createUnsignedUserOp: (
    request?: MessageSigningRequest,
    context?: any
  ) => Promise<string>;If you want you can also attach a paymaster here if your wallet wants to sponsor the transaction as well. The paymaster information will be displayed to the user.
Config of the extension can be set in excnfig.json file.
{
  // Enable or disable password for the user.
  "enablePasswordEncryption": true,
  // Show default transaction screen
  "showTransactionConfirmationScreen": true,
  // Network that your SCW supports. Currently this app only supports a single network, we will soon have support for multiple networks in future
  "network": {
    "chainID": "5",
    "family": "EVM",
    "name": "Goerli",
    "provider": "https://goerli.infura.io/v3/bdabe9d2f9244005af0f566398e648da",
    "entryPointAddress": "0x0F46c65C17AA6b4102046935F33301f0510B163A",
    "bundler": "https://app.stackup.sh/api/v1/bundler/96771b1b09e802669c33a3fc50f517f0f514a40da6448e24640ecfd83263d336",
    "baseAsset": {
      "symbol": "ETH",
      "name": "ETH",
      "decimals": 18,
      "image": "https://ethereum.org/static/6b935ac0e6194247347855dc3d328e83/6ed5f/eth-diamond-black.webp"
    }
  }
}No you can disable that by setting enablePasswordEncryption flag to false in exconfig.json.
Warning: the local storage will be unencrypted and your wallet must return an encrypted state when
serializefunction ofaccount-apiwillo be called or else the user's fund will be at risk.
If you want to show a custom screen then you must present it to the user in TransactionComponent and set showTransactionConfirmationScreen to false.
You must return the paymaster information in the userOp constructed by the function createUnsignedUserOp.
Warnming: If
showTransactionConfirmationScreenhas been disabled then the user will not be aware of paymaster and you must inform the user about paymaster in your custom transaction confirmation screen.
This repository is based on the boilerplate code found at lxieyang/chrome-extension-boilerplate-react. To understand how hot-reloading and content scripts work, refer to its README.
