{"id":25902372,"url":"https://github.com/dapplets/dapplets-eth-example","last_synced_at":"2026-02-11T09:05:58.110Z","repository":{"id":58664740,"uuid":"448036334","full_name":"dapplets/dapplets-eth-example","owner":"dapplets","description":"Tutorial on how to use Dapplets with Ethereum","archived":false,"fork":false,"pushed_at":"2022-07-06T08:27:34.000Z","size":3664,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-04T07:43:10.908Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dapplets.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-14T16:36:57.000Z","updated_at":"2022-07-05T08:26:34.000Z","dependencies_parsed_at":"2022-09-06T02:11:41.507Z","dependency_job_id":null,"html_url":"https://github.com/dapplets/dapplets-eth-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dapplets/dapplets-eth-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapplets%2Fdapplets-eth-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapplets%2Fdapplets-eth-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapplets%2Fdapplets-eth-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapplets%2Fdapplets-eth-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dapplets","download_url":"https://codeload.github.com/dapplets/dapplets-eth-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dapplets%2Fdapplets-eth-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29330858,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T06:13:03.264Z","status":"ssl_error","status_checked_at":"2026-02-11T06:12:55.843Z","response_time":97,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-03T03:15:58.306Z","updated_at":"2026-02-11T09:05:58.093Z","avatar_url":"https://github.com/dapplets.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dapplets × Ethereum example\n\n**Dapplets × Ethereum example** is a [Dapplet](https://dapplets.org) (an Augmentation App) that can parse Twitter tweets and store them in the Ethereum contract. It can also display your saved tweets in the overlay page.\n\n## 1. Introduction\n\n**Dapplets** - applications that interact with web-pages, augment them by inserting different widgets, parsing pages data and adding some new functionality. It may improve user experience of using social media, video services and other sourses.\n\nDapplets use the **extension** we are creating. It gives a simple api for dapplets developers and big abilities for our community. Our platform is decentralized. We use **Near** and **Ethereum** networks for our registries and contracts, and decentralized storages, like **Swarm**, **IPFS** and **Arweave** for hosting dapplets code and multimedia.\n\nTo use our platform at first you need to install the **Dapplets extension**. Currently it's on the alfa-stage and not published to Google Chrome or some other store. To install it follow this steps:\n\n1. Open one of the following browsers: Google Chrome, Firefox, Brave, Tor.\n\n\u003e The following steps are described for Google Chrome. The steps may differ in other browsers.\n\n2. Download the [**Dapplet Browser Extension**](https://github.com/dapplets/dapplet-extension/releases/latest).\n\n3. Open **chrome://extensions** in a new tab.\n4. Switch the **Developer mode** on and press **F5** to refresh the page.\n\n   ![image](https://user-images.githubusercontent.com/43613968/117107075-ad076580-ad89-11eb-9046-58dd1ede2868.png)\n\n5. **Drag and drop** the downloaded file into the extensions page. The extension will install automatically.\n\n   ![image](https://user-images.githubusercontent.com/43613968/117132354-6cb8df00-adab-11eb-93bb-eb17b287e140.png)\n\n   \u003e If you are using Ubuntu or possibly another Linux OS the Dapplets extension can disappear from the Chrome Extensions after restarting the PC. To avoid this unzip the archive and use the `Load unpacked` button to add the extension.\n   \u003e\n   \u003e ![image](https://user-images.githubusercontent.com/43613968/118473499-b93cdc80-b712-11eb-8a1a-d3779e490e8c.png)\n   \u003e\n   \u003e ![image](https://user-images.githubusercontent.com/43613968/118473927-2ea8ad00-b713-11eb-9bbf-f2b7cb33a6bf.png)\n\n6. Click to the **Dapplets** extension icon in extension bar. Try **Dapplets × Ethereum example** dapplet.\n\n## 2. Tutorial\n\nLet's study how this dapplet works and why Dapplets is a Ethereum frendly platform.\n\nThe goal of the example is to show interaction of Ethereum and Dapplets. If it is your very first meeting with Dapplets we recommend you to try our [documentation.](https://docs.dapplets.org) It contains several exercises that explane how to create dapplets and adapters from simple to complex ones. We are highly recommend to go through the [ex01](https://docs.dapplets.org/docs/extra-button) and [ex04](https://docs.dapplets.org/docs/overlays) examples that describe how to create the simpliest dapplet and the dapplet with the overlay. The knowledges you'll get make easy to understand the current example.\n\nThe initial code for this example is here: [exercise](https://github.com/dapplets/dapplets-eth-example)\n\nYou can clone this repo. It won't work directly. Try following steps to start it.\n\nLet's look at the **structure**. There are three components: **dapplet**, **overlay** and **contract**.\n\n**Dapplet** is the entry point of the application. It use adapters to interact with web-pages, define conext to augment and widgets to insert. Also the core functions of the extension are available there. We use **Typescript** in our progect.\n\nWe define **Overlay** as a place where a user can do something with parsed data, connect to core dapplet's functions through the **dapplet bridge** and manage augmentation parameters. It is an impotrant part of the application but it runs in another environment and published as a separate module. In the most cases we use **React** as one of the most popular frameworks. But you can use a framework that you prefer or the pure javascript or typescript.\n\n**Contract** does not connect directly with other modules and may located outside of the dapplet. But this simple **Ethereum** contract created only for this dapplet. So it's comfortable to place it here.\n\nLet'a look at the each module.\n\n### 2.1. Dapplet\n\nLook at the `/dapplet/src/index.ts`.\n\nAt first we create injectable class with decorator `@Injectable` and use `@Inject` to add **Twitter Adapter** as the `adapter` class variable. Also create `activate` method. It runs when selected adapter finds specific context and is loading. It will contain all the main logic.\n\n```typescript\n@Injectable\nexport default class TwitterFeature {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any,  @typescript-eslint/explicit-module-boundary-types\n  @Inject('twitter-adapter.dapplet-base.eth') public adapter: any;\n\n  async activate(): Promise\u003cvoid\u003e {\n    //\n  }\n}\n```\n\nAdd the button to every tweet. Put this code to the `activate`:\n\n```typescript\nconst { button } = this.adapter.exports;\nthis.adapter.attachConfig({\n  POST: (ctx: any) =\u003e\n    button({\n      DEFAULT: {\n        img: EXAMPLE_IMG,\n        tooltip: 'Parse Tweet',\n        exec: () =\u003e {\n          console.log('parsedCtx:', ctx);\n        },\n      },\n    }),\n});\n```\n\nLook at the code. We get widget `button` from the adapter. Then run adapter's method `attachConfig`. It receives an object with names of contexts, that will be used, as keys. Values are functions, that receives parsed context as the only argument and returns widget or the array of widgets. You may also return `null`, `false` or `undefined`.\n\n**Widget** is a function that receives an object consisting of widget states. State parameters are described in the adapters documentation. **Twitter Adapter** documentation you can find [here](https://docs.dapplets.org/docs/adapters-docs-list#name=twitter-adapter.dapplet-base.eth\u0026title=Twitter%20adapter\u0026version=v0.9.0). In our case we add image to the button and tooltip. `exec` is a function that runs on click. Now we just show the parsed contect in the console.\n\nRun the dapplet:\n\n```bash\nnpm i\nnpm start\n```\n\n\u003e :warning: Since the browser is blocking pages with problematic security certificates, go to https://localhost:3001/dapplet.json when the application is running and agree to run in **insecure mode**\n\n\u003e :warning: Since the browser is blocking pages with problematic security certificates, go to https://localhost:3000 when the application is running and agree to run in **insecure mode**.\n\nOpen the extension. Go to Developer tab and turn on the development server: `https://localhost:3001/dapplet.json`.\n\n![image](https://user-images.githubusercontent.com/43613968/138610500-d40d1a49-040d-4524-afd4-18ff630f33ca.png)\n\nTorn on Dapplets tab. You will see the dev badge near our dapplet. Turn it on.\n\n![1](https://user-images.githubusercontent.com/79759758/150325976-fa0f2d61-68e4-4e6f-864f-0757011beba5.png)\n\nNow you can see additional buttons on tweets. Click on the button and open console. You will see the parsed context of the tweet.\n\n![image](https://user-images.githubusercontent.com/43613968/138664005-a8cf6930-b53b-4122-baa8-282d263c8cba.png)\n\nYou've done it! Congratulations!!! Go back to the code.\n\nWe want to show parsed data not in the console but to the users. We use an overlay for this. But before implementing the overlay, add the interaction logic between the dapplet and the overlay to the dapplet.\n\nLet's change our code. Add private class variable `_overlay` of type `any`. In the `activate` add the following code:\n\n```typescript\nif (!this._overlay) {\n  this._overlay = (\u003cany\u003eCore).overlay({ name: 'overlay', title: 'Dapplets x Ethereum example' });\n}\n```\n\nCore function `Core.overlay` (typing problems will be fixed soon) receives an object with a **name** of the overlay and overlay **title** and returns the `Overay` object which we save in the `_overlay` variable.\n\nLet's add **openOverlay** method to the class:\n\n```typescript\nasync openOverlay(props?: any): Promise\u003cvoid\u003e {\n this._overlay.send('data', props);\n}\n```\n\nIn this method we call the method `send` of the overlay. It takes two arguments: the name of this data and the data to send to the overlay.\n\nAdd `openOverlay` to `exec` function and pass the parsed context to the overlay. The current code of the dapplet:\n\n```typescript\nimport {} from '@dapplets/dapplet-extension';\nimport EXAMPLE_IMG from './icons/eth_dapplet_icon_70.png';\nimport ABI from './ABI';\nimport isValidJSON from './helpers';\n\n@Injectable\nexport default class TwitterFeature {\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any,  @typescript-eslint/explicit-module-boundary-types\n  @Inject('twitter-adapter.dapplet-base.eth') public adapter: any;\n\n  private _overlay: any;\n\n  async activate(): Promise\u003cvoid\u003e {\n    if (!this._overlay) {\n      this._overlay = (\u003cany\u003eCore).overlay({\n        name: 'overlay',\n        title: 'Dapplets x Ethereum example',\n      });\n    }\n\n    const { button } = this.adapter.exports;\n    this.adapter.attachConfig({\n      POST: (ctx: any) =\u003e\n        button({\n          DEFAULT: {\n            img: EXAMPLE_IMG,\n            tooltip: 'Parse Tweet',\n            exec: () =\u003e {\n              console.log('parsedCtx:', ctx);\n              this.openOverlay(ctx);\n            },\n          },\n        }),\n    });\n  }\n\n  async openOverlay(props?: any): Promise\u003cvoid\u003e {\n    this._overlay.send('data', props);\n  }\n}\n```\n\nOpen the manifest `./dapplet/dapplet.json`.\n\n```json\n{\n  \"name\": { \"$ref\": \"package.json#/name\" },\n  \"branch\": \"default\",\n  \"version\": { \"$ref\": \"package.json#/version\" },\n  \"type\": \"FEATURE\",\n  \"title\": \"Dapplets x Ethereum example\",\n  \"description\": { \"$ref\": \"package.json#/description\" },\n  \"main\": { \"$ref\": \"package.json#/main\" },\n  \"icon\": \"src/icons/eth_dapplet_icon_70.png\",\n  \"contextIds\": [\"twitter-adapter.dapplet-base.eth\"],\n  \"config\": {\n    \"schema\": \"config/schema.json\",\n    \"default\": \"config/default.json\"\n  },\n  \"overlays\": {\n    \"overlay\": \"https://localhost:3000\"\n  },\n  \"dependencies\": {\n    \"twitter-adapter.dapplet-base.eth\": \"0.9.0\"\n  }\n}\n```\n\nHere we see the URL of the overlay named `'overlay'` for developers mode. During the publication of the dapplet to the registry the overlay will be published to the decentralized storage.\n\nAlso we see the Twitter Adapter in the dependencies with the using version.\n\nLet's go to the overlay.\n\n### 2.2. Overlay\n\nAs I wrote above, the overlay can be created the way you want. We use **React** in most of our projects. I will not analyze the entire overlay code, but only the important points for our architectural aspects.\n\nThe @dapplets/dapplet-overlay-bridge dependency is used to interact with the overlay.`\n\nTo get the data from the dapplet we need the class **Bridge** in the overlay part. Look at the module `./overlay/src/dappletBridge.ts`. Here is the `onData` method where we subscribe on the `'data'` event, which we've described in the dapplet.\n\n```typescript\n_subId: number = 0;\n\nonData(callback: (data?: any) =\u003e void) {\n this.subscribe('data', (data: any) =\u003e {\n   this._subId = Math.trunc(Math.random() * 1_000_000_000);\n   callback(data);\n   return this._subId.toString();\n });\n}\n```\n\nThen we use it in the `App.tsx` module.\n\nIn the onData method, we subscribe to the data event described in ./dapplet/src/index.ts in the openOverlay class method, passing the \"data\" event to the overlay and sending the props that is the context of our tweet.\n\n```typescript\n/* */\nimport { bridge } from './dappletBridge';\n/* */\nexport default () =\u003e {\n  const [parsedCtx, setParsedCtx] = useState\u003cICtx\u003e();\n  /* */\n  useEffect(() =\u003e {\n    bridge.onData((data?: ICtx) =\u003e {\n      setParsedCtx(data);\n    });\n    /* */\n  }, []);\n  /* */\n};\n```\n\nNow save changes and reload the Twitter page. On button click you will see the overlay with the selected tweet data.\n\n![3 (1)](https://user-images.githubusercontent.com/79759758/150392361-8e31b9fd-4fa0-4fc7-bafa-8929c2d84138.png)\n\nThats cool! But our goal is to save this data to Ethereum chain and get it back. So let's see the contract.\n\n### 2.3. Ethereum smart contract\n\nIt stores an array of serialized tweets data by the current user ID. It has methods for saving, removing and retrieving saved tweets.\n\nLet's see how to connect to the smart contract and use its methods in the dapplet.\n\nAdd the folowing code to the `activate` method of the `./dapplet/src/index.ts` module:\n\n```typescript\nconst contract = await Core.contract('ethereum', '0x7702aE3E1E0a96A428052BF3E4CB94965F5C0d7F', ABI);\n```\n\nThere is a `Core.contract` method that receives 3 parameters: name of the network ('near' or 'Ethereum'), contract name and object with view and change methods.\n\nNow we will make the contract methods available in the overlay. In order to pass methods through the dapplets bridge, add a `listen` function to the overlay call. Don't be afraid, just copy and paste this code :)\n\n```typescript\nif (!this._overlay) {\n  this._overlay = (\u003cany\u003eCore).overlay({ name: 'overlay', title: 'Dapplets x ETH example' }).listen({\n    connectWallet: async () =\u003e {\n      try {\n        const wallet = await Core.wallet({ type: 'ethereum', network: 'goerli' });\n        await wallet.connect();\n        const currentAccount = await new Promise((res) =\u003e\n          wallet.sendAndListen('eth_accounts', [], { result: (_, { data }) =\u003e res(data[0]) }),\n        );\n        this._overlay.send('connectWallet_done', currentAccount);\n      } catch (err) {\n        this._overlay.send('connectWallet_undone', err);\n      }\n    },\n    disconnectWallet: async () =\u003e {\n      try {\n        const wallet = await Core.wallet({ type: 'ethereum', network: 'goerli' });\n        await wallet.disconnect();\n        this._overlay.send('disconnectWallet_done');\n      } catch (err) {\n        this._overlay.send('disconnectWallet_undone', err);\n      }\n    },\n    isWalletConnected: async () =\u003e {\n      try {\n        const wallet = await Core.wallet({ type: 'ethereum', network: 'goerli' });\n        const isWalletConnected = await wallet.isConnected();\n        this._overlay.send('isWalletConnected_done', isWalletConnected);\n      } catch (err) {\n        this._overlay.send('isWalletConnected_undone', err);\n      }\n    },\n    getCurrentEthAccount: async () =\u003e {\n      try {\n        const wallet = await Core.wallet({ type: 'ethereum', network: 'goerli' });\n        const currentAccount = await new Promise((res) =\u003e\n          wallet.sendAndListen('eth_accounts', [], { result: (_, { data }) =\u003e res(data[0]) }),\n        );\n        this._overlay.send('getCurrentEthAccount_done', currentAccount);\n      } catch (err) {\n        this._overlay.send('getCurrentEthAccount_undone', err);\n      }\n    },\n    getTweets: async (op: any, { type, message }: any) =\u003e {\n      try {\n        const wallet = await Core.wallet({ type: 'ethereum', network: 'goerli' });\n        const currentAccount = await new Promise((res) =\u003e\n          wallet.sendAndListen('eth_accounts', [], { result: (_, { data }) =\u003e res(data[0]) }),\n        );\n        const tweets = await contract.getTweets(currentAccount);\n        this._overlay.send('getTweets_done', tweets.filter(isValidJSON));\n      } catch (err) {\n        this._overlay.send('getTweets_undone', err);\n      }\n    },\n    addTweet: async (op: any, { type, message }: any) =\u003e {\n      try {\n        this._overlay.send('addTweet_done');\n        await contract.addTweet(message.tweet);\n\n        console.log(message.tweet);\n      } catch (err) {\n        this._overlay.send('addTweet_undone', err);\n      }\n    },\n    removeTweet: async (op: any, { type, message }: any) =\u003e {\n      try {\n        await contract.removeTweet(message.tweet);\n        this._overlay.send('removeTweet_done');\n        console.log(message.tweet);\n      } catch (err) {\n        this._overlay.send('removeTweet_undone', err);\n      }\n    },\n  });\n}\n```\n\nThe last three asynchronius functions pass our contract methods to the overlay. The first four functions need to pair the wallet to the dapplet. To get the `Wallet` object we use a method **`Core.wallet`** with named parameters `name` (`near` or `ethereum`) and `network`. Wallet has methods **`isConnected`**, **`connect`**, **`disconnect`** .\n\nNext step is to change `./overlay/src/dappletBridge.ts`. We have to make functions, that was described in the dapplet, available in the overlay. Copy the foolowing code to the **`Bridge`** class:\n\n```typescript\nasync connectWallet(): Promise\u003cstring\u003e {\n return this.call(\n   'connectWallet',\n   null,\n   'connectWallet_done',\n   'connectWallet_undone'\n );\n}\n\nasync disconnectWallet(): Promise\u003cstring\u003e {\n  return this.call(\n    'disconnectWallet',\n    null,\n    'disconnectWallet_done',\n    'disconnectWallet_undone'\n  );\n}\n\nasync isWalletConnected(): Promise\u003cboolean\u003e {\n  return this.call(\n    'isWalletConnected',\n    null,\n    'isWalletConnected_done',\n    'isWalletConnected_undone'\n  );\n}\n\nasync getCurrentEthAccount(): Promise\u003cstring\u003e {\n  return this.call(\n    'getCurrentEthAccount',\n    null,\n    'getCurrentEthAccount_done',\n    'getCurrentEthAccount_undone'\n  );\n}\n\nasync getTweets(accountId: string): Promise\u003cstring[]\u003e {\n  return this.call(\n    'getTweets',\n    { accountId },\n    'getTweets_done',\n    'getTweets_undone'\n  );\n}\n\nasync addTweet(tweet: string): Promise\u003cstring\u003e {\n  return this.call(\n    'addTweet',\n    { tweet },\n    'addTweet_done',\n    'addTweet_undone'\n  );\n}\n\nasync removeTweet(tweet: string): Promise\u003cstring\u003e {\n  return this.call(\n    'removeTweet',\n    { tweet },\n    'removeTweet_done',\n    'removeTweet_undone'\n  );\n}\n\npublic async call(\n  method: string,\n  args: any,\n  callbackEventDone: string,\n  callbackEventUndone: string\n): Promise\u003cany\u003e {\n  return new Promise((res, rej) =\u003e {\n    this.publish(this._subId.toString(), {\n      type: method,\n      message: args,\n    });\n    this.subscribe(callbackEventDone, (result: any) =\u003e {\n      this.unsubscribe(callbackEventDone);\n      this.unsubscribe(callbackEventUndone);\n      res(result);\n    });\n    this.subscribe(callbackEventUndone, () =\u003e {\n      this.unsubscribe(callbackEventUndone);\n      this.unsubscribe(callbackEventDone);\n      rej('The transaction was rejected.');\n    });\n  });\n}\n```\n\nNow we can use contract methods in the overlay modules. We can authorize the dapplet with the Ethereum testnet wallet and save the data of the selected tweets to the smart contract. Also we see the saved data in the overlay.\n\nUncommit all the commited code in the `./overlay/src/App.tsx`. Save changes and reload the Twitter page.\n\n![3](https://user-images.githubusercontent.com/79759758/150326280-389e983b-7b1a-463e-8fba-99af63fca856.png)\n\nThe cherry on top will be the addition of the ability to view saved tweets without parsing new ones. To do this, it is enough to add the `Core.onAction` method to the `activate` in `./dapplet/src/index.ts` and pass the function of opening the overlay to it.\n\n```typescript\nCore.onAction(() =\u003e this.openOverlay());\n```\n\nNow you will see the home icon near the dapplets name.\n\n![4](https://user-images.githubusercontent.com/79759758/150326322-32455b62-da86-4293-88c8-d52faeb2e953.png)\n\nClick on the button provides opening of the overlay with saved tweets.\n\n![5](https://user-images.githubusercontent.com/79759758/150326425-82673a43-f8bf-427e-ba4a-86b1aa595cb7.png)\n\nCongratulations to everyone who made it to the end of the tutorial! Hope you succeed.\n\nHere is the result: [dapplets-eth-example](https://github.com/dapplets/dapplets-eth-example)\n\nIf something didn't work out for you or you still have questions, welcome to our chats in [Discord](https://discord.gg/YcxbkcyjMV) and [Telegram](https://t.me/dapplets).\n\nThank you for your time. I hope this new knowledge will be useful to you in developing impressive and successful applications on the Dapplets platform using the capabilities of the Ethereum protocol 🚀✨\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdapplets%2Fdapplets-eth-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdapplets%2Fdapplets-eth-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdapplets%2Fdapplets-eth-example/lists"}