Steps to code this yourself:
1. Go to the snap manifest packages/snap/snap.manifest.json
and change the permissions to the following:
"initialPermissions": {
"snap_manageState": {},
"endowment:transaction-insight": {}
},
import { OnTransactionHandler } from '@metamask/snaps-types';
import { text } from '@metamask/snaps-ui';
import { hasProperty, isObject } from '@metamask/utils';
/**
* Handle an incoming transaction, and return any insights.
*
* @param args - The request handler args as object.
* @param args.transaction - The transaction object.
* @returns The transaction insights.
*/
export const onTransaction: OnTransactionHandler = async ({ transaction }) => {
if (
!isObject(transaction) ||
!hasProperty(transaction, 'to') ||
typeof transaction.to !== 'string'
) {
console.warn('Unknown transaction type.');
return { content: text('Unknown transaction') };
}
return { content: text('**Test:** Successful') };
};
This will handle a typical transaction and show a generic message in the transaction insights interface.
Run yarn && yarn start
to build the snap, launch the local server, and install it.
You can then try going to a generic contract on mainnet and interact with it to see the transaction insights displayed: Simple Storage.
return { content: text('**You are interacting with:** ' + transaction.to) };
Go 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.
let state = (await snap.request({
method: 'snap_manageState',
params: { operation: 'get' },
})) as { addresses: {} } || null;
if (!state) { // if no data this is likely null
state = { addresses: {} };
// initialize state if empty and set default data
await snap.request({
method: 'snap_manageState',
params: { operation: 'update', newState: state },
});
}
let interactions = state.addresses['address:'+transaction.to] || 0;
interactions++;
let returnText = 'You have interacted with this address '+interactions+' times.';
if(interactions < 2) {
returnText = 'This is the **first time** you are interacting with this address.';
}
state.addresses['address:'+transaction.to] = interactions;
snap.request({
method: 'snap_manageState',
params: { operation: 'update', newState: state },
});
Add the panel type to the types you request from snaps-ui
:
import { panel, text } from '@metamask/snaps-ui';
And modify the return value to display the number of times the user has interacted with this address:
return { content: panel([
text('**You are interacting with:** ' + transaction.to),
text(returnText)
]) };
The final source code of the snap is:
import { OnTransactionHandler } from '@metamask/snaps-types';
import { panel, text } from '@metamask/snaps-ui';
import { hasProperty, isObject } from '@metamask/utils';
/**
* Handle an incoming transaction, and return any insights.
*
* @param args - The request handler args as object.
* @param args.transaction - The transaction object.
* @returns The transaction insights.
*/
export const onTransaction: OnTransactionHandler = async ({ transaction }) => {
if (
!isObject(transaction) ||
!hasProperty(transaction, 'to') ||
typeof transaction.to !== 'string'
) {
console.warn('Unknown transaction type.');
return { content: text('Unknown transaction') };
}
let state = (await snap.request({
method: 'snap_manageState',
params: { operation: 'get' },
})) as { addresses: {} } || null;
if (!state) { // if no data this is likely null
state = { addresses: {} };
// initialize state if empty and set default data
await snap.request({
method: 'snap_manageState',
params: { operation: 'update', newState: state },
});
}
let interactions = state.addresses['address:'+transaction.to] || 0;
interactions++;
let returnText = 'You have interacted with this address '+interactions+' times.';
if(interactions < 2) {
returnText = 'This is the **first time** you are interacting with this address.';
}
state.addresses['address:'+transaction.to] = interactions;
await snap.request({
method: 'snap_manageState',
params: { operation: 'update', newState: state },
});
return { content: panel([
text('**You are interacting with:** ' + transaction.to),
text(returnText)
]) };
};
Only ~50 lines of code!
Reconnect 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!
- 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.
- 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.