{"id":20306675,"url":"https://github.com/montoya/insights-counter","last_synced_at":"2025-04-11T15:06:16.489Z","repository":{"id":149473895,"uuid":"621875630","full_name":"Montoya/insights-counter","owner":"Montoya","description":"A MetaMask snap that displays how many times you have interacted with an address.","archived":false,"fork":false,"pushed_at":"2023-04-19T03:38:54.000Z","size":1316,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T04:03:31.374Z","etag":null,"topics":["metamask","snaps"],"latest_commit_sha":null,"homepage":"https://montoya.github.io/insights-counter/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Montoya.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.APACHE2","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-31T15:18:12.000Z","updated_at":"2023-04-25T19:16:14.000Z","dependencies_parsed_at":"2023-07-06T12:46:11.031Z","dependency_job_id":null,"html_url":"https://github.com/Montoya/insights-counter","commit_stats":{"total_commits":16,"total_committers":2,"mean_commits":8.0,"dds":0.0625,"last_synced_commit":"1593f8f7774eed26ff68ca6ce40aabe81240ad9d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":"MetaMask/template-snap-monorepo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montoya%2Finsights-counter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montoya%2Finsights-counter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montoya%2Finsights-counter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montoya%2Finsights-counter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Montoya","download_url":"https://codeload.github.com/Montoya/insights-counter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248429076,"owners_count":21101782,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["metamask","snaps"],"created_at":"2024-11-14T17:14:28.141Z","updated_at":"2025-04-11T15:06:16.468Z","avatar_url":"https://github.com/Montoya.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Insights Snap\n\n## Tutorial\n\nSteps to code this yourself: \n\n### 1. Go to the snap manifest `packages/snap/snap.manifest.json` and change the permissions to the following:  \n\n```JSON\n  \"initialPermissions\": {\n    \"snap_manageState\": {}, \n    \"endowment:transaction-insight\": {}\n  },\n```\n\n### 2. Go to the snap source code `packages/snap/src/index.ts` and replace it with this: \n\n```TypeScript\nimport { OnTransactionHandler } from '@metamask/snaps-types';\nimport { text } from '@metamask/snaps-ui';\nimport { hasProperty, isObject } from '@metamask/utils';\n\n/**\n * Handle an incoming transaction, and return any insights.\n *\n * @param args - The request handler args as object.\n * @param args.transaction - The transaction object.\n * @returns The transaction insights.\n */\nexport const onTransaction: OnTransactionHandler = async ({ transaction }) =\u003e {\n  if (\n    !isObject(transaction) ||\n    !hasProperty(transaction, 'to') ||\n    typeof transaction.to !== 'string'\n  ) {\n    console.warn('Unknown transaction type.');\n    return { content: text('Unknown transaction') };\n  }\n\n  return { content: text('**Test:** Successful') };\n};\n```\n\nThis will handle a typical transaction and show a generic message in the transaction insights interface. \n\nRun `yarn \u0026\u0026 yarn start` to build the snap, launch the local server, and install it. \n\nYou can then try going to a generic contract on mainnet and interact with it to see the transaction insights displayed: [Simple Storage](https://etherscan.io/address/0x48b4cb193b587c6f2dab1a9123a7bd5e7d490ced#writeContract). \n\n### 3. Modify the snap source code return to get and display the address you are interacting with:\n\n```Typescript\n  return { content: text('**You are interacting with:** ' + transaction.to) };\n```\n\nGo back to the dapp, reconnect the snap to install the latest version, and go back to the contract to interact with it. This time you will see the address of the contract. \n\n### 4. Use manageState to store a counter for each address you interact with: \n\n```TypeScript\n  let state = (await snap.request({\n    method: 'snap_manageState',\n    params: { operation: 'get' },\n  })) as { addresses: {} } || null; \n\n  if (!state) { // if no data this is likely null \n    state = { addresses: {} };\n    // initialize state if empty and set default data\n    await snap.request({\n      method: 'snap_manageState',\n      params: { operation: 'update', newState: state },\n    });\n  }\n\n  let interactions = state.addresses['address:'+transaction.to] || 0; \n\n  interactions++; \n\n  let returnText = 'You have interacted with this address '+interactions+' times.'; \n  if(interactions \u003c 2) { \n    returnText = 'This is the **first time** you are interacting with this address.'; \n  }\n\n  state.addresses['address:'+transaction.to] = interactions; \n\n  snap.request({\n    method: 'snap_manageState',\n    params: { operation: 'update', newState: state },\n  });\n```\n\nAdd the panel type to the types you request from `snaps-ui`: \n\n```TypeScript\nimport { panel, text } from '@metamask/snaps-ui';\n```\n\nAnd modify the return value to display the number of times the user has interacted with this address: \n\n```TypeScript\n  return { content: panel([\n    text('**You are interacting with:** ' + transaction.to),\n    text(returnText)\n  ]) };\n```\n\nThe final source code of the snap is: \n\n```TypeScript\nimport { OnTransactionHandler } from '@metamask/snaps-types';\nimport { panel, text } from '@metamask/snaps-ui';\nimport { hasProperty, isObject } from '@metamask/utils';\n\n/**\n * Handle an incoming transaction, and return any insights.\n *\n * @param args - The request handler args as object.\n * @param args.transaction - The transaction object.\n * @returns The transaction insights.\n */\nexport const onTransaction: OnTransactionHandler = async ({ transaction }) =\u003e {\n  if (\n    !isObject(transaction) ||\n    !hasProperty(transaction, 'to') ||\n    typeof transaction.to !== 'string'\n  ) {\n    console.warn('Unknown transaction type.');\n    return { content: text('Unknown transaction') };\n  }\n\n  let state = (await snap.request({\n    method: 'snap_manageState',\n    params: { operation: 'get' },\n  })) as { addresses: {} } || null; \n\n  if (!state) { // if no data this is likely null \n    state = { addresses: {} };\n    // initialize state if empty and set default data\n    await snap.request({\n      method: 'snap_manageState',\n      params: { operation: 'update', newState: state },\n    });\n  }\n\n  let interactions = state.addresses['address:'+transaction.to] || 0; \n\n  interactions++; \n\n  let returnText = 'You have interacted with this address '+interactions+' times.'; \n  if(interactions \u003c 2) { \n    returnText = 'This is the **first time** you are interacting with this address.'; \n  }\n\n  state.addresses['address:'+transaction.to] = interactions; \n\n  await snap.request({\n    method: 'snap_manageState',\n    params: { operation: 'update', newState: state },\n  });\n\n  return { content: panel([\n    text('**You are interacting with:** ' + transaction.to),\n    text(returnText)\n  ]) };\n};\n```\n\n_Only ~50 lines of code!_\n\nReconnect the snap to install the latest version, then try interacting with a contract multiple times to see the count go up. You can try interacting with different addresses and you will that the result matches how many times you interact with each one!\n\n## Caveats\n\n1. This snap only runs when the user views the transaction insights tab for this snap. So it is really a count of that. If the user interacts with an address without viewing the tab, then that is never counted. \n2. The transaction insights tab is loaded more than once when viewed, so the counter will fire multiple times, giving innacurate counts. To mitigate this, I have added a timestamp checker to only update the count if at least 4 seconds have passed since the last update. The source code in this repository has this feature. ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontoya%2Finsights-counter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmontoya%2Finsights-counter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontoya%2Finsights-counter/lists"}