De fleste DeFi-hack skjer ikke fordi en smartkontrakt har en skjult bug. De skjer fordi en bruker signerte noe de ikke forsto. En “approve”-transaksjon som ser ut som en vanlig token-bytte, kan i realiteten gi en fremmed kontrakt ubegrenset tilgang til lommeboken din. I januar 2026 ble sju DeFi-protokoller hacket med et samlet tap på rundt 86 millioner dollar, og en enkelt sosial manipulasjon mot en Trezor-bruker kostet rundt 282 millioner dollar. Denne guiden viser deg, steg for steg, hvordan du simulerer transaksjonen din før du signerer, leser calldata manuelt, og tilbakekaller gamle tillatelser før noen får sjansen til å misbruke dem. Du trenger ingen forkunnskaper i Solidity, bare en terminal, Node.js og 40-50 minutter.

Derfor er token-tillatelser DeFis svakeste ledd

Norges Bank har målt at 97 prosent av nordmenn over 16 år kjenner til kryptoaktiva, men bare 17 prosent kjenner til DeFi som begrep. Av dem som faktisk kjenner til DeFi, oppgir 74 prosent at de aldri har brukt det. Det gapet er ikke tilfeldig. DeFi krever at brukeren selv forstår hva en tillatelse (allowance) faktisk gir en kontrakt lov til å gjøre, og de fleste lommebok-grensesnitt gjør en dårlig jobb med å forklare det.

Uniswap var den mest brukte DeFi-plattformen i Norges Banks måling fra 2024, mens Polymarket var mest brukt i den nyeste målingen. Samtidig handler norske kryptobrukere fortsatt primært via sentraliserte børser: 49 prosent bruker norske børser og 30 prosent utenlandske børser som hovedhandelssted. Stablecoins brukes i hovedsak til selve handelen (55 prosent oppgir dette som hovedgrunn), mens 30 prosent bruker dem til verdilagring. Når disse brukerne beveger seg fra en trygg børs-konto til sin egen lommebok og en DeFi-protokoll for første gang, er det nettopp signering av approve- og permit-transaksjoner som utgjør den største risikoen.

Et angrep trenger ikke utnytte en feil i selve smartkontrakten. Et tilsynelatende legitimt token-bytte kan samtidig gi en annen adresse en ubegrenset tillatelse, som en angriper senere bruker uten at du signerer noe nytt. Det er derfor mange sikkerhetsteam nå anbefaler å behandle signeringsøyeblikket, ikke bare kontraktskoden, som selve sikkerhetsgrensen. Et fagmiljø beskriver token-tillatelser som selve inngangsporten til de fleste lommebok-tømminger som skjer i dag, fordi tillatelsen ofte lever videre lenge etter at du har glemt dAppen du ga den til.

Det er også verdt å se dette i sammenheng med hvordan nordmenn faktisk handler krypto i dag. De fleste starter på en sentralisert børs, der plattformen håndterer nøklene og godkjenningene for deg. Det er først når du flytter midler over i egen lommebok og begynner å samhandle direkte med smartkontrakter at du selv blir ansvarlig for hver eneste godkjenning du signerer. Denne overgangen skjer ofte brått, gjerne drevet av en enkeltstående mulighet som en ny lansering eller en airdrop, og da er det lett å signere raskt uten å lese hva transaksjonen faktisk ber om.

Dette trenger du før du starter

Du trenger ikke være utvikler for å følge denne guiden, men noen av stegene bruker et lite Node.js-skript slik at du kan automatisere kontrollen fremfor å gjøre alt manuelt hver gang. Sett opp følgende før du går videre:

VerktøyVersjon / statusBruksområde i denne guiden
Node.js20.x eller nyere (siste LTS er 24.21.0)Kjøre skriptene for sjekk og tilbakekalling
npm10.8 eller nyereInstallere pakker
ethers.js6.17.0Lese og sende transaksjoner mot Ethereum
MetaMask eller annen lommebokSiste versjon fra offisiell nettbutikkSignere transaksjoner og teste simulering
Revoke.cashNettbasert verktøy, ingen installasjonVisuell oversikt over aktive tillatelser
En RPC-leverandør (Alchemy, Infura eller lignende)Gratis nivå er nok til denne guidenKoble skriptene til Ethereum-nettverket

Sett også av en liten separat testlommebok med maks noen hundre kroner i den. Du skal aldri teste ukjente dApper eller kjøre skript mot hovedlommeboken din med hele porteføljen. Det er billigere å miste testbeløpet enn å finne ut at et skript hadde en skrivefeil.

Regn med rundt 40-50 minutter for å gå gjennom alle stegene første gang, inkludert installasjon og de første testtransaksjonene. Har du fra før erfaring med Node.js og en lommebok som MetaMask, kan du trolig gjennomføre det raskere. Er alt dette nytt for deg, ta deg heller god tid på steg 1 til 3 før du rører ekte transaksjoner, siden resten av guiden bygger direkte videre på det grunnlaget.

Hva skiller dette fra generell DeFi-overvåking?

Porteføljeverktøy som varsler deg om prisbevegelser, likviderings-risiko eller mistenkelig aktivitet på tvers av flere kjeder løser et annet problem enn det denne guiden tar for seg. De ser typisk på tilstanden etter at en transaksjon allerede er bekreftet på kjeden. Denne guiden handler i stedet om øyeblikket rett før du signerer, og om hvilke tillatelser som allerede står åpne fra tidligere transaksjoner du kanskje har glemt.

Forskjellen har praktisk betydning for hvilket verktøy du bør bruke når. En sanntids porteføljevarsling forteller deg at noe skjedde. Transaksjonssimulering og calldata-lesing fra stegene under forteller deg hva som er i ferd til å skje, mens du fortsatt har mulighet til å avbryte. Begge deler hører hjemme i en fullstendig sikkerhetsrutine, men de dekker ulike tidsvinduer, og denne guiden fokuserer bevisst på vinduet før signering og på selve ryddejobben med gamle tillatelser.

Steg 1: Forstå forskjellen mellom approve, permit og Permit2

Før du kan sjekke eller tilbakekalle noe, må du vite hva slags tillatelse du faktisk gir bort. Det finnes tre vanlige mekanismer, og de oppfører seg ulikt både i hva de gir tilgang til og hvordan du fjerner tilgangen igjen.

MekanismeHvordan den fungererHvordan du tilbakekaller
approve (klassisk ERC-20)En on-chain transaksjon setter et fast beløp én kontrakt får lov til å flytteNy approve-transaksjon med beløp 0
permit (EIP-2612)Du signerer en gasløs melding off-chain, som en tredjepart senere sender inn on-chainIngen direkte tilbakekalling, men allowance kan settes til 0 etterpå
Permit2 (Uniswap)Én global tillatelse gir en rutingkontrakt lov til å administrere tillatelser til flere dApper samtidigEgen revoke-funksjon i Permit2-kontrakten, eller tidsutløp du selv setter

Standarden bak permit er beskrevet i EIP-2612, mens Uniswap sin egen mekanisme er dokumentert i Permit2-dokumentasjonen. Forskjellen har praktisk betydning: en permit-signatur koster ingen gass å opprette, noe som gjør den enklere å lure noen til å signere, fordi lommeboken ofte viser mindre advarsel enn ved en vanlig on-chain transaksjon.

Hvorfor Permit2 fortjener ekstra oppmerksomhet

Permit2 ble laget for å gjøre brukeropplevelsen bedre, ikke verre. Problemet er at når du først har godkjent Permit2-kontrakten for en token, kan enhver dApp som er koblet til Permit2-systemet be deg signere en gasløs tillatelse for akkurat den tokenen, uten at du trenger å godkjenne på nytt on-chain. Det gjør at én enkelt kompromittert dApp i teorien kan bevege seg videre til andre tokens du allerede har godkjent Permit2 for. MetaMask og andre lommebøker har derfor begynt å vise tydeligere advarsler ved permit-signaturer, noe Consensys har beskrevet i sin gjennomgang av tillatelser og signaturer i MetaMask.

Steg 2: Sett opp en separat DeFi-lommebok

Opprett en ny lommebok kun til testing og daglig DeFi-bruk. Overfør et lite beløp i ETH til gass og maks det beløpet du er komfortabel med å eksperimentere med. Hovedlommeboken med sparepengene dine bør aldri interagere direkte med nye eller uverifiserte kontrakter. Denne separasjonen er den enkleste og mest effektive sikkerhetsforanstaltningen i hele denne guiden, og den koster deg ingenting annet enn litt ekstra administrasjon.

Noter ned adressen til testlommeboken. Du kommer til å bruke den i skriptene i de neste stegene, sammen med adressen til hovedlommeboken din (kun for lesing, aldri for signering i denne guiden).

Har du allerede en hardware-lommebok til langsiktig lagring, kan den gjerne stå urørt ved siden av denne separate DeFi-lommeboken. Poenget er ikke å erstatte den ene med den andre, men å ha et tydelig skille mellom midler som skal ligge stille og midler som aktivt samhandler med nye kontrakter. Signerer du med en hardware-lommebok direkte mot en dApp, gjelder alle stegene i denne guiden like fullt, siden det er selve transaksjonsinnholdet, ikke hvor nøkkelen ligger lagret, som avgjør hvilken tillatelse du gir bort.

Steg 3: Installer verktøyene dine

Opprett en ny mappe og installer ethers.js. Vi bruker versjon 6.17.0, som er siste stabile utgivelse på npm i skrivende stund.

mkdir defi-tillatelse-vakt && cd defi-tillatelse-vakt
npm init -y
npm install [email protected] dotenv

Opprett en .env-fil med RPC-adressen din og adressene du vil overvåke. Legg aldri den private nøkkelen til hovedlommeboken din i denne filen, kun testlommeboken.

RPC_URL=https://eth-mainnet.g.alchemy.com/v2/DIN_API_NOKKEL
WSS_URL=wss://eth-mainnet.g.alchemy.com/v2/DIN_API_NOKKEL
PRIVATE_KEY=din_testlommebok_privatnokkel
TOKENS=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
SPENDERS=0x000000000022D473030F116dDEE9F6B43aC78BA3

Legg til .env i en .gitignore-fil med det samme, før du glemmer det. En privat nøkkel som havner i et offentlig kodelager, må regnes som kompromittert i det øyeblikket den blir synlig, uansett hvor raskt du sletter commiten etterpå. For denne guiden holder det med en testlommebok-nøkkel med begrenset saldo, men vanen med å aldri committe hemmeligheter bør du ta med deg videre til alt annet du bygger.

Steg 4: Simuler transaksjonen før du signerer

Før du trykker “Bekreft” i lommeboken, kjør transaksjonen gjennom en simulator som Tenderly eller simuleringsvisningen som mange moderne lommebøker allerede bygger inn. Simuleringen viser deg forventet saldoendring og hvilke kontrakter som faktisk blir kalt, uten at noe sendes til kjeden.

Sjekk tre ting i simuleringsresultatet: hvilken adresse som mottar en eventuell ny tillatelse, hvilket beløp tillatelsen gjelder for, og om transaksjonen rører andre tokens enn den du forventer. En bytte-transaksjon som skal gi deg USDC, bør for eksempel aldri samtidig flytte NFT-er til en ukjent adresse.

Husk at en simulering ikke er en garanti. Blokktilstanden kan endre seg mellom simuleringen og den faktiske innsendingen, prisforhold kan flytte seg, og enkelte kontrakter har logikk som avhenger av tidspunkt eller tilstand. Simuler derfor rett før du signerer, ikke minutter i forveien, og bruk simuleringen som et filter for åpenbare avvik, ikke som en fullstendig garanti.

Det finnes også en risiko simuleringen normalt ikke fanger opp: MEV. Sikkerhetslitteraturen beskriver MEV som en trussel som kobler sammen konsensuslaget og applikasjonslaget, fordi validatorer og bots kan utnytte retten til å inkludere og ordne transaksjoner i en blokk. En transaksjon kan se helt korrekt ut i simuleringen, men likevel ende opp med en dårligere pris fordi noen har plassert egne transaksjoner rett før og etter din i samme blokk. Dette er en egen risikokategori atskilt fra selve godkjenningsproblematikken, men verdt å ha i bakhodet når du vurderer om et resultat i simulatoren er “godt nok”.

Steg 5: Les calldata manuelt med ethers.js

Simulering fanger opp mye, men å kunne lese den faktiske calldataen selv gir deg en siste kontroll ingen plugin kan erstatte. Under følger et lite skript som dekoder en approve-transaksjon og viser deg mottaker og beløp i klartekst, basert på ABI-strukturen definert i OpenZeppelins ERC-20-dokumentasjon.

import { Interface } from "ethers";

const erc20Abi = [
  "function approve(address spender, uint256 amount) returns (bool)"
];

const iface = new Interface(erc20Abi);

function dekodApproveCalldata(calldata) {
  const parsed = iface.parseTransaction({ data: calldata });
  return {
    funksjon: parsed.name,
    mottaker: parsed.args[0],
    belop: parsed.args[1].toString()
  };
}

const eksempel = dekodApproveCalldata(
  "0x095ea7b3000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);

console.log(eksempel);

Ser du et beløp fullt av “f”-tegn, altså det maksimale tallet en uint256 kan holde, er dette en ubegrenset tillatelse. Det er ikke automatisk farlig, men det betyr at kontrakten kan flytte hele din fremtidige balanse av tokenen, ikke bare summen i den transaksjonen du er i ferd med å utføre.

Steg 6: Sjekk alle aktive tillatelser med et skript

Neste steg er å kartlegge hvilke tillatelser lommeboken din faktisk har stående i dag. Skriptet under leser allowance() direkte fra kjeden for hver kombinasjon av token og spender du gir det.

import { ethers } from "ethers";
import "dotenv/config";

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);

const erc20Abi = [
  "function allowance(address owner, address spender) view returns (uint256)",
  "function decimals() view returns (uint8)"
];

async function sjekkTillatelse(token, eier, spender) {
  const kontrakt = new ethers.Contract(token, erc20Abi, provider);
  const [belop, desimaler] = await Promise.all([
    kontrakt.allowance(eier, spender),
    kontrakt.decimals()
  ]);
  return ethers.formatUnits(belop, desimaler);
}

sjekkTillatelse(
  process.env.TOKENS,
  "0xDinTestlommebokAdresse",
  process.env.SPENDERS
).then((tillatelse) => console.log(`Aktiv tillatelse: ${tillatelse}`));

Kjør skriptet med node index.js. Får du et tall langt over det du noensinne har brukt i den dAppen, er det en kandidat for tilbakekalling i neste steg. Kjør denne sjekken på alle tokens og spendere du kjenner til, ikke bare den siste dAppen du brukte.

Skal du sjekke mange tokens og mange spendere samtidig, vokser antall kall raskt (én sjekk per kombinasjon av token og spender). Hold antallet nede ved å først hente ut en liste over adresser lommeboken din faktisk har interagert med, for eksempel via transaksjonshistorikken på Etherscan, og bruk den listen som input til skriptet fremfor å gjette deg frem til hvilke spendere som er relevante. Det er raskere og mer presist enn å teste tilfeldige adresser.

Steg 7: Bruk Revoke.cash som visuell kontroll

Skriptet ditt sjekker kun tokens og spendere du selv lister opp. For å få en fullstendig oversikt over alt lommeboken noensinne har godkjent, er et visuelt verktøy raskere. Revoke.cash kobler seg til lommeboken din i skrivemodus, viser hver eneste aktive tillatelse på tvers av tokens og kjeder, og lar deg tilbakekalle direkte fra grensesnittet.

Bruk dette verktøyet som et supplement til skriptet ditt, ikke som erstatning. Indekserte verktøy kan ha forsinkelser eller mangle støtte for enkelte nyere tokens, mens et direkte kall mot allowance()-funksjonen alltid gir deg den ferskeste tilstanden på kjeden.

De fleste slike tjenester støtter i dag flere EVM-kjeder samtidig, slik at du kan bytte nettverk i et nedtrekksmenu og få samme oversikt på Arbitrum, Base eller Polygon som på Ethereum mainnet. Det gjør det praktisk å gå gjennom hele porteføljen din på tvers av kjeder i én økt, i stedet for å måtte bytte RPC-adresse og kjøre skriptet fra steg 6 på nytt for hver kjede. Vær oppmerksom på at du fortsatt må sende en egen tilbakekallingstransaksjon per kjede, siden hver kjede har sin egen uavhengige tilstand.

Steg 8: Tilbakekall en tillatelse på kjeden

Å tilbakekalle en klassisk ERC-20-tillatelse er en vanlig transaksjon der du setter beløpet til 0. Skriptet under gjør nettopp det, og venter på bekreftelse før det avslutter.

import { ethers } from "ethers";
import "dotenv/config";

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

const erc20Abi = [
  "function approve(address spender, uint256 amount) returns (bool)"
];

async function tilbakekallTillatelse(token, spender) {
  const kontrakt = new ethers.Contract(token, erc20Abi, wallet);
  const tx = await kontrakt.approve(spender, 0n);
  console.log(`Sender tilbakekalling: ${tx.hash}`);
  const kvittering = await tx.wait();
  console.log(`Bekreftet i blokk ${kvittering.blockNumber}`);
}

tilbakekallTillatelse(process.env.TOKENS, process.env.SPENDERS);

Legg merke til at dette koster vanlig nettverksgass, akkurat som enhver annen transaksjon. Det finnes ingen gratis måte å tilbakekalle på uten å sende en transaksjon fra lommeboken som opprinnelig ga tillatelsen.

Steg 9: Automatiser overvåking med hendelseslytting

Manuell sjekk er greit av og til, men den virkelige verdien kommer når du oppdager en ny, mistenkelig tillatelse i det øyeblikket den blir opprettet, ikke uker senere. Skriptet under lytter etter Approval-hendelser knyttet til lommeboken din i sanntid via en WebSocket-tilkobling.

import { ethers } from "ethers";
import "dotenv/config";

const provider = new ethers.WebSocketProvider(process.env.WSS_URL);
const minAdresse = "0xDinTestlommebokAdresse";

const approvalTopic = ethers.id("Approval(address,address,uint256)");
const paddetAdresse = ethers.zeroPadValue(minAdresse, 32);

provider.on(
  { topics: [approvalTopic, paddetAdresse] },
  (log) => {
    console.log("Ny tillatelse oppdaget:", log.transactionHash);
    varsleTelegram(log.transactionHash);
  }
);

console.log("Lytter etter nye tillatelser...");

Filtrer bort støy fra kjente DEX-rutere

Kjører du denne overvåkingen over tid, oppdager du raskt at store deler av trafikken kommer fra godkjenninger til velkjente, mye brukte kontrakter som Uniswap-ruteren eller Permit2. Disse er ikke i seg selv et faresignal hver eneste gang, og om du varsler på absolutt alt, ender du fort opp med å ignorere varslene fullstendig etter noen uker. Bygg derfor en enkel allow-liste over kjente, velrenommerte spender-adresser du allerede har vurdert, og la skriptet dempe varsler for disse, mens alt annet fortsatt gir full varsling. Denne listen bør du oppdatere manuelt, ikke automatisk, slik at du alltid har bevisst tatt stilling til hva som står på den.

Kjør dette skriptet i bakgrunnen med en prosessbehandler som pm2, slik at det starter på nytt automatisk hvis tilkoblingen faller ut.

Legg merke til at filteret bruker den paddede adressen din som andre topic i loggfilteret. Approval-hendelsen fra ERC-20-standarden definerer eier som det første indekserte feltet, så ved å filtrere direkte i abonnementet unngår du å måtte laste ned og sortere gjennom hver eneste Approval-hendelse som skjer på hele nettverket. Det sparer både båndbredde og de fleste RPC-leverandørers ratebegrensning betydelig sammenlignet med å hente alle hendelser og filtrere lokalt i etterkant.

Steg 10: Sett opp varsling til Telegram

En hendelseslytter er verdiløs om ingen ser varselet før det er for sent. Koble den til en Telegram-bot slik at du får et push-varsel med en gang en ny tillatelse dukker opp på kjeden.

async function varsleTelegram(txHash) {
  const url = `https://api.telegram.org/bot${process.env.TG_TOKEN}/sendMessage`;
  await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      chat_id: process.env.TG_CHAT_ID,
      text: `Ny token-tillatelse oppdaget: https://etherscan.io/tx/${txHash}`
    })
  });
}

Opprett en bot via BotFather i Telegram, hent token-nøkkelen den gir deg, og legg den i .env-filen sammen med chat_id for samtalen du vil motta varsler i.

Steg 11: Test alt med småbeløp først

Før du stoler på skriptene dine med reelle beløp, kjør hele kjeden av steg, fra sjekk til tilbakekalling til varsling, med testlommeboken og et symbolsk beløp. Bekreft at varselet faktisk kommer frem, at tilbakekallingen faktisk går gjennom, og at skriptet håndterer feilmeldinger uten å krasje stille.

Dette steget føles unødvendig når alt ser riktig ut på skjermen, men det er akkurat her de fleste hjemmesnekrede sikkerhetsverktøy svikter i praksis. En feil i adresseformatet eller en feiltolket desimal oppdages først når du tester med ekte transaksjoner, ikke ved å lese koden.

Skriv gjerne ned de faktiske transaksjonshashene fra testkjøringen, slik at du kan slå dem opp på Etherscan i etterkant og se med egne øyne at approve-beløpet, mottakeren og statusen stemmer med det skriptet rapporterte. Denne doble kontrollen tar bare et par minutter, men gir deg en helt annen trygghet før du peker skriptet mot lommeboken med de reelle verdiene dine.

Steg 12: Bygg dette inn i en fast rutine

Et engangstiltak løser problemet for i dag, men nye tillatelser hoper seg opp hver gang du prøver en ny dApp. Sett en fast, tilbakevendende sjekk, for eksempel hver søndag kveld, der du kjører sjekkeskriptet på alle lommebøkene dine og tilbakekaller alt du ikke lenger aktivt bruker.

Kombiner dette med hendelseslytteren fra steg 9, som fanger opp nye tillatelser fortløpende, og den ukentlige gjennomgangen, som rydder opp i det som samler seg over tid selv når ingenting akutt skjer.

Sett en tilbakevendende kalenderpåminnelse med en fast dag og et fast tidspunkt, akkurat som du ville gjort med andre faste gjøremål. En rutine som kun eksisterer “når du husker det”, forsvinner erfaringsmessig i løpet av noen få uker, mens en rutine med et fast punkt i kalenderen overlever langt bedre over tid.

AI-baserte sikkerhetsverktøy: nyttige, men ikke tilstrekkelige alene

Flere lommebøker og nettleserutvidelser har begynt å bygge inn AI-baserte varslinger som skal fange opp mistenkelige transaksjoner automatisk. I februar 2026 ble EVMbench lansert som en åpen benchmark for nettopp dette formålet: den tester hvor godt AI-agenter klarer å oppdage, utnytte og reparere feil i smartkontrakter. Et slikt benchmark er nyttig fordi det gir en objektiv måte å sammenligne ulike verktøy på, i stedet for å stole på markedsføringspåstander fra hver enkelt leverandør.

Det viktige poenget for deg som bruker er at slike verktøy bør behandles som et ekstra lag, ikke som en erstatning for stegene i denne guiden. En AI-modell som er trent på tidligere angrepsmønstre, kan i praksis overse en helt ny variant av et angrep den ikke har sett før, mens en enkel regel som “sjekk om beløpet er ubegrenset” fanger opp problemet uansett hvor nytt angrepet er. Bruk gjerne AI-baserte varslinger som et tidlig varselsystem, men fortsett å kjøre den manuelle sjekken fra steg 5 og 6 på transaksjoner med reelt store beløp.

Vanlige fallgruver

  • Å tro at “Disconnect” fjerner tillatelsen. Å koble en dApp fra lommeboken din stopper kun kommunikasjonen mellom nettsiden og lommeboken. Selve tillatelsen på kjeden lever videre til du eksplisitt tilbakekaller den.
  • Å godkjenne ubegrenset beløp “for enkelhets skyld”. Mange dApper foreslår et uendelig beløp som standard for å slippe å be om ny godkjenning senere. Sett heller et beløp tilpasset det du faktisk skal bruke.
  • Å stole blindt på et grønt sikkerhetsikon. Skanner-plugins og AI-baserte varslingstjenester fanger opp mye, men ikke alt. De er et filter, ikke en garanti.
  • Å bruke hovedlommeboken til å teste nye, uverifiserte dApper. Skader fra et kompromittert nytt prosjekt rammer da hele porteføljen din, ikke bare testbeløpet.
  • Å glemme at Permit2 er global på tvers av dApper. En tillatelse du ga én dApp via Permit2, kan i praksis brukes av enhver annen dApp koblet til samme Permit2-kontrakt for den tokenen.
  • Å ikke sjekke om spender-adressen faktisk stemmer med dAppen. Enkelte angrep bytter ut adressen en dApp peker mot uten at det synes tydelig i grensesnittet.
  • Å anta at en vellykket simulering garanterer et vellykket resultat. Tilstanden på kjeden kan endre seg mellom simulering og innsending.

Ingen av disse fallgruvene krever avansert teknisk innsikt å unngå. Det de har til felles er at de alle oppstår i det korte tidsvinduet mellom at du åpner lommeboken og trykker “Bekreft”. Det er nettopp derfor stegene i denne guiden er lagt opp til å flytte kontrollen fra det øyeblikket, over til en rutine du kan gjennomføre i ro og mak enten før du signerer eller i en fast ukentlig gjennomgang.

Feilsøking: 9 vanlige problemer og løsninger

Tabellen under dekker de problemene du mest sannsynlig støter på når du setter opp og kjører skriptene fra denne guiden. De fleste løses på under fem minutter når du vet hvor du skal se.

ProblemVanlig årsakLøsning
“insufficient funds for gas” ved tilbakekallingFor lite native token (ETH) i testlommebokenOverfør et lite beløp til gass, tilbakekalling koster vanlig transaksjonsgass
Transaksjonen henger i “pending”For lav gasspris i forhold til nettverksbelastningØk maxPriorityFeePerGas eller bruk “speed up” i lommeboken
Revoke.cash viser ikke alle tillatelserIndekseringsforsinkelse eller manglende støtte for tokenenKryssjekk manuelt med allowance()-kallet fra steg 6
Skriptet kaster “could not decode result data”Feil ABI, eller kontrakten er en proxyHent implementasjons-ABI via “Read as Proxy” på Etherscan
“nonce too low” ved sending av transaksjonEn annen transaksjon fra samme lommebok er allerede i køVent til forrige transaksjon bekreftes, eller sett nonce manuelt
Telegram-varsling kommer aldri fremFeil chat_id eller boten er ikke lagt til i samtalenSend /start til boten og hent riktig chat_id via getUpdates-endepunktet
WebSocket-tilkoblingen dør stilleGratis RPC-leverandører kutter ofte inaktive tilkoblingerLegg inn automatisk reconnect med økende ventetid
Simulering viser suksess, ekte transaksjon feilerTilstanden på kjeden endret seg mellom simulering og innsendingSimuler på nytt rett før signering, ikke minutter i forveien
approve(spender, 0) reverterer for enkelte tokensEldre ERC-20-implementasjoner (som USDT) krever nullstilling før nytt beløpKall alltid med 0 først, hopp aldri rett til et nytt beløp på slike tokens

Avanserte tips for erfarne brukere

Når grunnrutinen sitter, er det noen ekstra grep som gir en tydelig sikkerhetsgevinst uten mye ekstra arbeid. For større beløp, flytt tunge posisjoner til en Safe-multisig med tidslås på transaksjoner over en selvvalgt terskel, slik at et kompromittert enkeltsignatur aldri er nok til å tømme hele porteføljen umiddelbart.

Del opp bruken din på flere lommebøker etter kategori, for eksempel én for langsiktig lagring, én for aktiv DeFi-bruk og én for eksperimentering med nye prosjekter. Utnytt også utløpsfeltet i Permit2-signaturer der dApper støtter det, slik at en gasløs tillatelse dør av seg selv etter noen timer i stedet for å ligge aktiv i månedsvis.

Kjør allowance-vakten som en periodisk jobb (for eksempel hver sjette time med en cron-jobb) fremfor bare hendelsesbasert varsling, og send et samlet sammendrag i stedet for én melding per hendelse. Det gjør det lettere å se mønstre over tid, og du unngår varslingstretthet når mange småhendelser skjer samtidig. For produksjonsbruk, invester i en dedikert RPC-leverandør med webhook-støtte fremfor et gratis endepunkt, siden gratis nivåer ofte har strengere rategrenser og mindre stabile WebSocket-tilkoblinger.

Sett en hard øvre grense i skriptet ditt for hvilket allowance-beløp som anses som “normalt” for en gitt token, og la alt over den grensen trigge et eget, mer påtrengende varsel enn de vanlige rapportene. En terskel på for eksempel ti ganger det du historisk har handlet for i én transaksjon, fanger opp de fleste unormalt høye tillatelser uten å generere for mye støy fra legitime, men noe større handler.

Vurder til slutt å føre en enkel logg, gjerne bare en tekstfil eller et regneark, over hvilke dApper du bevisst har gitt tillatelser til og hvorfor. Når allowance-vakten flagger noe om seks måneder, er det uvurderlig å raskt kunne slå opp om tillatelsen stammer fra en dApp du faktisk husker og stoler på, eller om den er helt ukjent for deg.

Har du mange tillatelser å rydde opp i samtidig, koster det unødvendig mye gass å sende én transaksjon per tilbakekalling. Noen lommebøker og verktøy støtter batch-tilbakekalling via en multicall-kontrakt, der flere approve-kall til 0 samles i én enkelt transaksjon. Gevinsten er størst når du rydder opp etter en lengre periode med mange småprosjekter, og mindre merkbar om du følger den ukentlige rutinen og sjelden har mer enn én eller to tillatelser å fjerne om gangen.

Komplett eksempelprosjekt: allowance-vakt i Node.js

Under følger et samlet skript som kombinerer sjekk, rapport og valgfri tilbakekalling i én kommando. Lagre det som index.js i mappen du opprettet i steg 3, og kjør det med node index.js for å se en rapport, eller node index.js –revoke for å tilbakekalle alt som blir funnet. Dette er selve kjernen du bygger videre på med hendelseslytting og varsling fra stegene over.

import { ethers } from "ethers";
import "dotenv/config";

const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

const erc20Abi = [
  "function allowance(address owner, address spender) view returns (uint256)",
  "function approve(address spender, uint256 amount) returns (bool)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)"
];

async function listAktiveTillatelser(tokens, eier, spenders) {
  const funn = [];
  for (const token of tokens) {
    const kontrakt = new ethers.Contract(token, erc20Abi, provider);
    const [desimaler, symbol] = await Promise.all([
      kontrakt.decimals(),
      kontrakt.symbol()
    ]);
    for (const spender of spenders) {
      const belop = await kontrakt.allowance(eier, spender);
      if (belop > 0n) {
        funn.push({
          token,
          symbol,
          spender,
          belop: ethers.formatUnits(belop, desimaler)
        });
      }
    }
  }
  return funn;
}

async function tilbakekallAlle(funn) {
  for (const { token, spender, symbol } of funn) {
    const kontrakt = new ethers.Contract(token, erc20Abi, wallet);
    const tx = await kontrakt.approve(spender, 0n);
    console.log(`Tilbakekaller ${symbol} -> ${spender}: ${tx.hash}`);
    await tx.wait();
  }
}

async function main() {
  const tokens = process.env.TOKENS.split(",");
  const spenders = process.env.SPENDERS.split(",");
  const aktive = await listAktiveTillatelser(tokens, wallet.address, spenders);

  console.log(`Fant ${aktive.length} aktive tillatelse(r):`);
  console.table(aktive);

  if (process.argv.includes("--revoke")) {
    await tilbakekallAlle(aktive);
    console.log("Alle funne tillatelser er tilbakekalt.");
  }
}

main().catch((feil) => {
  console.error("Noe gikk galt:", feil.message);
  process.exit(1);
});

Et typisk utdrag fra en rapport kan se slik ut, der du raskt ser at én spender-adresse har en mistenkelig høy tillatelse sammenlignet med de andre:

Fant 2 aktive tillatelse(r):
┌─────────┬──────────────────────────────────────────┬────────┬──────────────────────────────────────────┬───────────────────────┐
│ (index) │ token                                     │ symbol │ spender                                    │ belop                  │
├─────────┼──────────────────────────────────────────┼────────┼──────────────────────────────────────────┼───────────────────────┤
│    0    │ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' │ 'USDC' │ '0x000000000022D473030F116dDEE9F6B43aC78BA3' │ '115792089237316195423' │
│    1    │ '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' │ 'USDC' │ '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' │ '50'                    │
└─────────┴──────────────────────────────────────────┴────────┴──────────────────────────────────────────┴───────────────────────┘

Legg merke til den første raden. Beløpet er praktisk talt ubegrenset, mens den andre raden viser en tillatelse på 50 USDC, som er langt mer rimelig for en enkeltbytte. Den første raden er kandidaten du bør tilbakekalle først.

Ofte stilte spørsmål

Hva er forskjellen på å tilbakekalle en tillatelse og å koble fra en dApp?

Å koble fra en dApp stopper kun kommunikasjonen mellom nettsiden og lommeboken din i den økten. Selve tillatelsen ligger fortsatt aktiv på kjeden til du sender en egen transaksjon som setter beløpet til 0.

Koster det gass å tilbakekalle en tillatelse?

Ja. En tilbakekalling er en vanlig on-chain transaksjon og koster omtrent det samme som en ny approve-transaksjon. Det finnes ingen gratis snarvei utenom å samle flere tilbakekallinger i én batch-transaksjon om lommeboken din støtter det.

Er Permit2 farligere enn vanlig approve?

Ikke iboende farligere, men rekkevidden er annerledes. Fordi Permit2 er en delt kontrakt flere dApper kobler seg til, kan én tillatelse i teorien nå lenger enn en tillatelse gitt direkte til én enkelt dApp. Sjekk derfor Permit2-tillatelser like grundig som klassiske approve-tillatelser.

Kan jeg tilbakekalle tillatelser uten å bruke tredjeparts-nettsteder?

Ja. Skriptene i denne guiden gjør nettopp det, direkte mot kjeden via din egen RPC-tilkobling, uten å stole på et eksternt nettsted for selve transaksjonen. Revoke.cash og lignende tjenester er praktiske for oversikten, men du kan alltid sende revoke-transaksjonen selv.

Hvor ofte bør jeg sjekke tillatelsene mine?

En ukentlig gjennomgang kombinert med sanntidsvarsling fra hendelseslytteren i steg 9 dekker de fleste behov. Er du en aktiv DeFi-bruker som prøver mange nye prosjekter, bør du vurdere en tettere rutine, for eksempel hver gang du har brukt en ny dApp.

Fungerer denne metoden på andre kjeder enn Ethereum?

Ja. Alle EVM-kompatible kjeder, som Arbitrum, Base og Polygon, bruker samme ERC-20-standard for allowance og approve. Du trenger kun å peke RPC-adressen i .env-filen mot riktig nettverk og bruke tokenadressene for den kjeden.

Hvor mye koster det å tilbakekalle en tillatelse?

Kostnaden varierer med hvor travelt nettverket er akkurat da du sender transaksjonen, men en tilbakekalling er beregningsmessig enkel og koster normalt omtrent det samme som en enkel token-overføring, altså mindre enn en full bytte-transaksjon i en DeFi-protokoll. Skal du rydde opp i mange tillatelser samtidig, kan en batch-løsning via multicall gjøre den totale kostnaden lavere enn å sende hver tilbakekalling for seg.

Hva gjør jeg om jeg allerede har blitt utsatt for et allowance-tømmingsangrep?

Tilbakekall umiddelbart alle gjenværende tillatelser fra den kompromitterte lommeboken, flytt eventuelle verdier som fortsatt er igjen til en ny lommebok med en ny seed-frase, og gå gjennom hele listen med aktive tillatelser på nytt med skriptet fra steg 6. Anta at enhver dApp du signerte noe i nær tid før hendelsen kan ha vært involvert, og fjern tillatelser til alle, ikke bare den du mistenker mest.