Den 22. august 2026 stanset The Sandbox alle bro-overføringer mellom Base og BNB Smart Chain etter at noen klarte å prege SAND-tokener uten dekning på begge kjedene. Sikkerhetsselskapet Blockaid satte pålydende av de forfalskede tokenene til rundt 49 milliarder dollar fordelt på over 400 transaksjoner, selv om selskapet selv anslo reell eksponering til under 0,01 % av total forsyning. Fire måneder tidligere hadde Kelp DAO mistet 292 millioner dollar i rsETH fordi ett enkelt verifiseringsnode fikk godkjenne en forfalsket krysskjede-melding alene. Begge hendelsene brukte LayerZero som meldingslag, og begge hendelsene handlet om det samme grunnleggende problemet: hvem har egentlig lov til å si at en melding kom fra riktig kjede.
Denne artikkelen er en praktisk, kodenær veiledning for deg som bygger eller vedlikeholder omnichain-kontrakter. Du får 12 steg for å sette opp en LayerZero-basert bro med ekte meldingsverifisering, nonce-sperring og eksponeringsgrenser, pluss test-koden som beviser at forfalskede meldinger faktisk blir avvist. Målgruppen er Solidity-utviklere med grunnleggende erfaring, ikke sluttbrukere som bare skal flytte tokener trygt (den guiden har vi skrevet separat).
Hvorfor krysskjede-meldinger er blitt DeFis svakeste ledd
Bro-protokoller flytter ikke faktiske tokener mellom kjeder. De sender en melding fra kjede A som sier “lås inn X token her, mint tilsvarende verdi der”, og mottakerkjeden stoler på at meldingen er ekte. Hele sikkerhetsmodellen hviler dermed på verifiseringslaget, ikke på selve token-logikken. Det er derfor bro-hack har dominert skadestatistikken de siste to årene.
Ifølge analyseselskapet DeFiLlama gikk andre kvartal 2026 inn i historien som det verste kvartalet noensinne målt i antall hendelser, med 83 separate hack og 755 millioner dollar tapt totalt, og krysskjede-broer alene stod for rundt 351 millioner av det beløpet, nesten halvparten av kvartalets skader. CoinGecko har regnet ut at plattformer mistet over 3,63 milliarder dollar til cyberangrep og stjålne passord mellom januar 2025 og juli 2026, ifølge tall gjengitt av CNBC. August 2026 satte i tillegg rekord i antall separate hendelser med 50 hack på én måned, selv om totalbeløpet falt rundt 49 % til cirka 136,3 millioner dollar sammenlignet med juli.
LayerZero er ikke alene om problemet, men protokollen er blitt bransjestandarden for omnichain-tokener og dermed også det mest utsatte målet. Chainalysis har knyttet Kelp DAO-angrepet til nordkoreanske Lazarus Group, som skal ha kompromittert interne RPC-noder og samtidig utført tjenestenekt-angrep mot eksterne noder for å tvinge gjennom en falsk verifisering. Angrepet tømte 116 500 rsETH fordelt på over 20 kjeder, og fordi 89 567 av disse tokenene lå som sikkerhet på Aave for 190 millioner dollar i lånt WETH, måtte både Aave, SparkLend og Fluid fryse rsETH-markedene sine for å unngå ytterligere tap.
Tallene blir enda tydeligere når man ser dem over tid. Blockchain.news har regnet ut at DeFiLlamas historiske oversikt nå viser over 16,5 milliarder dollar i akkumulerte krypto-hack, hvorav rundt 7,7 milliarder er DeFi-spesifikke tap og cirka 2,9 milliarder kommer fra bro-relaterte hendelser alene. Det er ikke fordi bro-kode i seg selv er dårligere skrevet enn annen smart-kontraktkode. Det er fordi en bro per definisjon må stole på en ekstern, uavhengig kilde for sannhet, og det uavhengige laget er nøyaktig der angriperne har lært seg å lete.
Poenget med denne artikkelen er ikke å skremme deg bort fra omnichain-arkitektur. LayerZero flytter enorme verdier trygt hver eneste dag, og de fleste integrasjoner går aldri i nærheten av en sikkerhetshendelse. Poenget er at standardoppsettet ikke beskytter deg automatisk mot feilkonfigurasjon, og at de fleste av hendelsene over kunne vært stoppet med kontrollene du bygger steg for steg i denne guiden.
Hva er meldingsforfalskning i et omnichain-system
LayerZero V2 sender ikke meldinger direkte fra kjede til kjede. Arkitekturen består av en Endpoint-kontrakt på hver kjede, et sett med uavhengige Decentralized Verifier Networks (DVN-er) som bekrefter at en melding faktisk ble sendt, og en Executor som utfører leveringen på mottakerkjeden. Applikasjonen din, kalt en OApp, definerer selv hvor mange DVN-er som må være enige og hvilke adresser (peers) den stoler på på hver kjede.
Meldingsforfalskning skjer når en angriper klarer å få en melding levert til mottakerkontrakten din uten at den faktisk kom fra din egen, legitime avsenderkontrakt på den oppgitte kildekjeden. I Kelp DAO-saken var problemet at kun én DVN-node var satt opp til å verifisere meldinger, en såkalt 1-av-1-konfigurasjon. Når angriperen klarte å mate feil data til den ene noden, ble den falske meldingen godkjent uten motstand. I The Sandbox-saken lå feilen et annet sted, nemlig i en “approveAndCall”-hook som lot angriperen kapre delegat-rettigheter og dermed sende gyldig signerte, men uautoriserte, mint-instruksjoner.
Trusted remotes i V1 versus peers i V2
Eldre integrasjoner bygget på LayerZero V1 bruker en konfigurasjon som kalles trusted remotes, der du pakker sammen avsender- og mottakeradresse i én 40-byte verdi per kjedepar. V2 erstatter dette med en enklere peers-mapping der hver kjede-id kobles til én tillatt avsenderadresse. Funksjonelt løser begge det samme problemet, men mange eldre broer som ble bygget på V1 og senere migrert til V2 uten å rydde opp i gamle trusted remote-oppføringer, har endt opp med to parallelle tillitslister som kan komme i utakt med hverandre. Sjekk alltid at gamle V1-konfigurasjoner er fjernet fullstendig hvis du migrerer en eksisterende kontrakt.
Lock-and-mint versus burn-and-mint
De fleste omnichain-tokener bruker én av to modeller. I lock-and-mint låses den opprinnelige tokenen fast i en escrow-kontrakt på hjemkjeden, mens en representasjon mintes på destinasjonskjeden. I burn-and-mint finnes det ingen fast hjemkjede: tokenen brennes der den sendes fra, og en tilsvarende mengde mintes der den skal til. The Sandbox brukte en variant av burn-and-mint for SAND på tvers av kjeder, og nettopp derfor kunne et hull i mint-logikken skape tokener helt uten underliggende dekning. Lock-and-mint er generelt tryggere fordi den underliggende verdien alltid finnes et sted i systemet, men den krever at escrow-kontrakten på hjemkjeden aldri kan tømmes raskere enn meldingene faktisk verifiseres.
Tabellen under viser hvordan komponentene i LayerZero V2 sitt sikkerhetslag (Security Stack) fordeler ansvar, og hvem som faktisk kontrollerer hver del.
| Komponent | Rolle | Hvem styrer den |
|---|---|---|
| Endpoint | Mottar og ruter meldinger mellom OApp og resten av protokollen på hver kjede | LayerZero-protokollen (uendret av utvikler) |
| Sending/Receiving Library | Koder og pakker meldinger for overføring, angir hvilke DVN-er som kreves | OApp-eier via konfigurasjon |
| DVN (Decentralized Verifier Network) | Bekrefter uavhengig at en melding faktisk ble sendt fra kildekjeden | Velges og vektes av OApp-eier, driftes av tredjepart |
| Executor | Utfører selve leveringen (kaller lzReceive) på mottakerkjeden | Kan være LayerZero Labs sin egen eller egendefinert |
| OApp (din kontrakt) | Definerer peers, nonce-håndtering og forretningslogikk | Deg som utvikler |
Ifølge LayerZeros egen integrasjonssjekkliste er det nettopp konfigurasjonen av DVN-kvorum og peer-verifisering som er de vanligste feilkildene i produksjonsmiljøer, ikke selve protokollkoden. Det stemmer godt med hendelsene vi har gått gjennom over.
Forutsetninger før du begynner
Du trenger følgende på plass før du starter på stegene:
- Node.js 20 LTS eller nyere
- Solidity 0.8.24 eller nyere
- Foundry (nyeste versjon) for testing, eventuelt Hardhat 2.22 eller nyere som alternativ
- npm-pakken
@layerzerolabs/lz-evm-oapp-v2i nyeste versjon - OpenZeppelin Contracts versjon 5.x
- En lommebok med testnett-midler, for eksempel på Base Sepolia og Arbitrum Sepolia
- Grunnleggende kjennskap til Solidity, events og modifiers
- Git og en kodeeditor som støtter Solidity-utvidelser
Sett av 60 til 90 minutter for å komme gjennom alle stegene med testnett-utrulling inkludert.
Steg 1: Kartlegg din egen omnichain-arkitektur
Før du skriver en eneste linje kode, tegn opp hvilke kjeder tokenet eller appen din skal leve på, og hvilken retning meldingene går. Skal du støtte fritt flyt begge veier mellom for eksempel Ethereum, Base og Arbitrum, eller er det én hjemkjede med utgående kopier andre steder? Dette avgjør om du trenger en lock-and-mint-modell (lås på kildekjeden, mint på mottakerkjeden) eller burn-and-mint (brenn på avsenderkjeden, mint på mottakerkjeden), slik vi forklarte over. Skriv ned et maksimalt tap-scenario for hver kjede, altså hvor mye verdi som realistisk kan stå i sirkulasjon på en enkelt kjede til enhver tid. Dette tallet bruker du senere til å sette eksponeringsgrenser i steg 8.
Lag også en enkel liste over hvem som skal ha administratorrettigheter på hver kontrakt, og hvor mange signaturer som kreves for hver type endring. Skill mellom rutinemessige endringer (som å legge til en ny støttet kjede) og kritiske endringer (som å endre DVN-kvorum eller flytte eierskap). Kritiske endringer bør alltid kreve flere signaturer og gjerne en tidslås, mens rutineendringer kan gå raskere uten å svekke sikkerheten nevneverdig.
Steg 2: Sett opp prosjektet med Foundry og LayerZero-pakkene
Start et nytt Foundry-prosjekt og installer avhengighetene:
mkdir omnichain-token && cd omnichain-token
forge init --no-commit
npm init -y
npm install @layerzerolabs/lz-evm-oapp-v2 @layerzerolabs/lz-evm-protocol-v2 @openzeppelin/contracts
forge install foundry-rs/forge-std --no-commit
Legg til en remappings.txt som peker Solidity-importene dine mot node_modules, slik at Foundry finner LayerZero-kontraktene når du kompilerer.
Steg 3: Bygg avsenderkontrakten med eksplisitt peer-håndtering
Avsenderkontrakten arver fra OApp-basen og sender meldinger via _lzSend. Det viktige her er at kontrakten aldri hardkoder mottakeradressen, men slår opp peer-adressen for hver destinasjonskjede.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {OApp, Origin, MessagingFee} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
contract OmniTokenSender is OApp {
event MeldingSendt(uint32 dstEid, bytes32 mottaker, uint256 belop);
constructor(address _endpoint, address _owner) OApp(_endpoint, _owner) {}
function sendOverfoering(
uint32 _dstEid,
address _mottaker,
uint256 _belop,
bytes calldata _options
) external payable {
bytes memory melding = abi.encode(_mottaker, _belop, block.timestamp);
MessagingFee memory fee = _quote(_dstEid, melding, _options, false);
require(msg.value >= fee.nativeFee, "Utilstrekkelig gebyr");
_lzSend(_dstEid, melding, _options, fee, payable(msg.sender));
emit MeldingSendt(_dstEid, bytes32(uint256(uint160(_mottaker))), _belop);
}
}
Legg merke til at vi inkluderer block.timestamp i meldingen. Det gir mottakerkontrakten et ekstra datapunkt å validere mot i steg 6, uten at det erstatter den egentlige nonce-beskyttelsen.
Steg 4: Bygg mottakerkontrakten med streng peer-verifisering
Dette er kontrakten som faktisk avgjorde utfallet i Kelp DAO-saken. OApp-rammeverket gir deg en innebygd sjekk mot peers-mappingen, men den må faktisk være satt riktig, og du bør aldri stole blindt på at Executor-kallet alene er nok bevis på ekthet.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {OApp, Origin} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
contract OmniTokenReceiver is OApp {
mapping(uint32 => mapping(uint64 => bool)) private behandledeNonces;
mapping(address => uint256) public saldo;
event MeldingMottatt(uint32 srcEid, uint64 nonce, address mottaker, uint256 belop);
constructor(address _endpoint, address _owner) OApp(_endpoint, _owner) {}
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _melding,
address _executor,
bytes calldata _extraData
) internal override {
// OApp-basen har allerede sjekket _origin.sender mot peers[_origin.srcEid]
// her legger vi til egen nonce-sperre i tillegg, se steg 6
require(!behandledeNonces[_origin.srcEid][_origin.nonce], "Nonce allerede brukt");
behandledeNonces[_origin.srcEid][_origin.nonce] = true;
(address mottaker, uint256 belop, ) = abi.decode(_melding, (address, uint256, uint256));
saldo[mottaker] += belop;
emit MeldingMottatt(_origin.srcEid, _origin.nonce, mottaker, belop);
}
}
Den innebygde peer-sjekken i OApp-rammeverket kaster automatisk om _origin.sender ikke stemmer med adressen du har satt via setPeer. Problemet oppstår når utviklere glemmer å faktisk kalle setPeer på alle kjeder, eller setter feil adresse ved en inkurie. Test alltid at peer-oppsettet er identisk på begge sider før du rører produksjonsmidler.
Steg 5: Konfigurer Security Stack og DVN-kvorum
Standardkonfigurasjonen i LayerZero bruker gjerne én DVN. Det er nettopp denne konfigurasjonen som gjorde Kelp DAO sårbar, ifølge OpenZeppelins gjennomgang av hendelsen. Du bør kreve minst to uavhengige DVN-er som må være enige før en melding godkjennes.
// Kjøres som et deploy-script, ikke inne i selve kontrakten
const requiredDVNs = [dvnAdresse1, dvnAdresse2]; // to uavhengige DVN-er
const optionalDVNs = [];
const confirmations = 15; // antall blokkbekreftelser på kildekjeden
const ulnConfig = {
confirmations,
requiredDVNCount: requiredDVNs.length,
optionalDVNCount: optionalDVNs.length,
optionalDVNThreshold: 0,
requiredDVNs,
optionalDVNs,
};
await endpoint.setConfig(
omniTokenReceiver.address,
receiveLibAddress,
[{ eid: srcEid, configType: 2, config: encodeUlnConfig(ulnConfig) }]
);
Med to uavhengige DVN-er må en angriper kompromittere begge samtidig for å forfalske en melding, ikke bare den ene noden som var nok i Kelp DAO-hendelsen. Vurder å bruke DVN-er fra forskjellige driftsselskaper, ikke to noder driftet av samme part.
Slik velger du riktige DVN-leverandører
Ikke alle DVN-er er like. Se etter driftere med dokumentert oppetidshistorikk over flere måneder, egen infrastruktur atskilt fra andre DVN-leverandører du allerede bruker, og helst egen sikkerhetsrevisjon av selve nodeprogramvaren. Spør spesifikt om hvor noden fysisk kjører og hvem som drifter den bak kulissene. To DVN-er som begge kjører på samme skyleverandør i samme region gir deg juridisk to leverandører, men praktisk sett fortsatt ett enkelt feilpunkt hvis den underliggende infrastrukturen faller ut. Vurder også hvor mange andre store protokoller som allerede stoler på samme DVN, siden det gir en indikasjon på hvor mye som faktisk står på spill for driftsselskapet dersom noden skulle levere feil data.
Steg 6: Legg til eksplisitt nonce- og replay-beskyttelse
LayerZero V2 leverer meldinger i rekkefølge per kanal som standard, men appen din må likevel selv nekte gjenbruk av en nonce, slik vi allerede gjorde i mottakerkontrakten i steg 4. Grunnen er enkel: hvis Executor-laget noen gang skulle levere samme melding to ganger, eller hvis en fremtidig sårbarhet i biblioteket lar en angriper gjenspille en gammel, gyldig melding, er det din nonce-sperre som stopper dobbel utbetaling. Amnext-hendelsen på BNB Smart Chain 9. september 2026, der en feil i kreditt-brenning lot en angriper hente ut samme premie 116 100 dollar flere ganger, er et godt eksempel på hva som skjer når denne typen sperre mangler eller er ufullstendig.
Test nonce-sperren eksplisitt: send samme melding to ganger i en testfil og verifiser at det andre forsøket reverterer med feilmeldingen din, ikke bare med en generisk feil fra rammeverket.
Steg 7: Skriv en forfalskningstest med Foundry
Den viktigste testen du kan skrive er en som beviser at kontrakten din avviser en melding fra en useriøs avsender. Simuler et angrep der en tilfeldig adresse prøver å late som om den er Endpoint-kontrakten og kaller lzReceive direkte.
// test/SpoofingTest.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import {OmniTokenReceiver} from "../src/OmniTokenReceiver.sol";
import {Origin} from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
contract SpoofingTest is Test {
OmniTokenReceiver receiver;
address angriper = address(0xBAD);
function setUp() public {
receiver = new OmniTokenReceiver(address(endpointMock()), address(this));
receiver.setPeer(30184, bytes32(uint256(uint160(address(0xEKTE)))));
}
function testForfalsketAvsenderRevertes() public {
Origin memory falskOrigin = Origin({
srcEid: 30184,
sender: bytes32(uint256(uint160(angriper))), // ikke satt som peer
nonce: 1
});
vm.prank(address(endpointMock()));
vm.expectRevert(); // OApp-basen skal avvise ukjent peer
receiver.lzReceive(falskOrigin, bytes32(0), abi.encode(angriper, 1_000_000 ether, block.timestamp), address(0), "");
}
function endpointMock() internal pure returns (address) {
return address(0xE0D);
}
}
Eksempel på utdata når testen kjøres riktig:
$ forge test --match-test testForfalsketAvsenderRevertes -vv
[PASS] testForfalsketAvsenderRevertes() (gas: 42118)
Logs:
Revert-arsak: "LZ_ULN: peer not set"
Test result: ok. 1 passed; 0 failed; 0 skipped
Hvis testen din i stedet gir et “PASS” på et scenario der pengene faktisk beveger seg, har du en åpen dør. Snu testen om og verifiser at den feiler når peer-sjekken er kommentert bort, slik du vet at testen faktisk tester noe reelt og ikke bare alltid består.
Steg 8: Sett eksponeringsgrenser og hastighetsbegrensning
Selv med perfekt meldingsverifisering bør du aldri stole hundre prosent på at koden er feilfri. En rate-limiter som begrenser hvor mye verdi som kan flyttes gjennom broen per tidsvindu, er den siste sikkerhetsmekanismen som hindrer et fullstendig tap dersom noe likevel går galt, slik det gikk med Notional Finance 4. september 2026, hvor en heltallsfeil i en gammel escrow-kontrakt lot en angriper hente ut 1,73 millioner dollar før noen rakk å reagere.
uint256 public constant MAKS_PER_VINDU = 500_000 ether;
uint256 public constant VINDU_LENGDE = 1 hours;
uint256 private vindusStart;
uint256 private belopIVindu;
modifier innenforGrense(uint256 _belop) {
if (block.timestamp >= vindusStart + VINDU_LENGDE) {
vindusStart = block.timestamp;
belopIVindu = 0;
}
require(belopIVindu + _belop <= MAKS_PER_VINDU, "Overskrider timesgrense for bro");
belopIVindu += _belop;
_;
}
Sett grensen ut fra tallet du regnet ut i steg 1. Grensen skal være høy nok til å ikke plage vanlig trafikk, men lav nok til at et eventuelt hack begrenses til noe teamet kan håndtere manuelt før mer skade skjer.
Steg 9: Bygg sanntidsovervåking av krysskjede-meldinger
Du trenger et script som lytter etter uvanlige mønstre på tvers av kjedene dine, ikke bare på én kjede om gangen. Et enkelt utgangspunkt er å følge med på volum per time og varsle når det nærmer seg grensen du satte i forrige steg.
// monitor.js (ethers.js)
import { ethers } from "ethers";
const kjeder = [
{ navn: "Base", rpc: process.env.BASE_RPC, adresse: BASE_RECEIVER },
{ navn: "Arbitrum", rpc: process.env.ARBITRUM_RPC, adresse: ARB_RECEIVER },
];
for (const kjede of kjeder) {
const provider = new ethers.JsonRpcProvider(kjede.rpc);
const kontrakt = new ethers.Contract(kjede.adresse, ABI, provider);
kontrakt.on("MeldingMottatt", (srcEid, nonce, mottaker, belop, event) => {
const belopEth = ethers.formatEther(belop);
if (Number(belopEth) > 50_000) {
varsleTeam({
kjede: kjede.navn,
srcEid,
nonce: nonce.toString(),
belop: belopEth,
tx: event.log.transactionHash,
});
}
});
}
Eksempel på varsel som sendes videre til Slack eller PagerDuty når terskelen brytes:
{
"kjede": "Base",
"srcEid": 30110,
"nonce": "4821",
"belop": "62000.0",
"tx": "0x7f3a...e91c",
"alvorlighet": "hoy",
"tidspunkt": "2026-09-12T08:41:03Z"
}
Kjør dette som en langtkjørende prosess med automatisk omstart, og test varslingskjeden manuelt minst én gang i måneden. Et overvåkingssystem som aldri har sendt et falskt positivt, har sannsynligvis aldri blitt testet. Vurder også å logge alle meldinger, ikke bare de som overskrider terskelverdien, til en egen database. Historiske data over normal trafikk gjør det langt enklere å sette fornuftige terskler senere, og gir deg et sammenligningsgrunnlag hvis dere en dag må rekonstruere nøyaktig hva som skjedde i timene før en mistenkelig hendelse.
Steg 10: Bestill ekstern revisjon og etabler et bug-bounty-program
Egen testing, uansett hvor grundig, finner ikke alt. ChainSecuritys revisjon av LayerZeros egne OFT/OApp-kontrakter er et godt eksempel på hvor detaljert en ekstern gjennomgang av krysskjede-logikk bør være. Be om at revisor spesifikt vurderer peer-konfigurasjon, DVN-kvorum og nonce-håndtering, ikke bare generell Solidity-hygiene. Del gjerne fallgruve-listen lenger ned i denne artikkelen med revisorteamet før de starter, slik at de vet nøyaktig hvilke angrepsklasser som allerede har rammet lignende systemer i 2026.
Sett i tillegg opp et bug-bounty-program med en dusør som står i forhold til hvor mye verdi broen din faktisk kan flytte, gjerne på en plattform med offentlig sporbar historikk. En vanlig tommelfingerregel er å sette toppbeløpet for en kritisk feil til rundt 5 til 10 % av maksimal eksponering i broen, siden det gjør det mer lønnsomt for en etisk hacker å rapportere feilen enn å utnytte den. Publiser tydelig omfang for programmet, inkludert hvilke kontrakter, hvilke kjeder og hvilke angrepsklasser som er dekket, slik at forskere ikke kaster bort tid på områder du allerede har dekket internt.
Steg 11: Test pause- og nødstoppmekanismer før lansering
The Sandbox klarte å begrense skaden 22. august nettopp fordi de kunne stanse broen raskt uten å måtte vente på en tidslåst styringsprosess. Selskapet meldte at ingen brukerlommebøker ble kompromittert og at SAND på Ethereum og Polygon forble upåvirket, delvis fordi isoleringen skjedde før angrepet rakk å spre seg til flere kjeder. Legg inn en pause()-funksjon kontrollert av en multisig med lav terskel for hastesaker, atskilt fra den vanlige eierskaps-multisigen som styrer oppgraderinger. Test eksplisitt at ingen overføringer, verken innkommende eller utgående, kan gjennomføres mens broen er satt på pause, og at pause-funksjonen selv ikke kan blokkeres av en angriper som først har fått fotfeste i systemet.
Steg 12: Lag en hendelsesresponsplan og øv på den
Skriv ned, på forhånd, hvem som har fullmakt til å trykke på pause-knappen klokken tre om natten, hvilken kommunikasjonskanal som brukes, og hvilke eksterne parter (revisorer, DVN-driftere, børser) som skal varsles i hvilken rekkefølge. Planen bør også inneholde ferdigskrevne utkast til brukerkommunikasjon, slik at teamet ikke må formulere en forklaring under press mens klokka tikker. Øv scenariet minst én gang i kvartalet med en simulert hendelse, gjerne uten forvarsel til hele teamet, slik at øvelsen faktisk tester beredskapen og ikke bare bekrefter at alle husker planen fra sist gang.
Team som har øvd på dette på forhånd, bruker minutter i stedet for timer på å reagere når noe faktisk skjer, og differansen mellom de to er ofte differansen mellom et begrenset tap og en total tømming. The Sandbox sin evne til å isolere Base og BNB Chain samme dag som exploiten ble oppdaget, i stedet for uker senere slik man så i flere andre 2026-hendelser, er et konkret eksempel på hvor mye en forhåndsdefinert prosess betyr i praksis.
Vanlige fallgruver ved bygging av krysskjede-broer
Når man ser nærmere på hendelsene fra 2026, går de samme grunnfeilene igjen om og om igjen på tvers av protokoller og team. Ingen av dem krever et gjennombrudd i kryptoanalyse å utnytte, de krever bare at ett enkelt kontrollpunkt mangler.
- 1-av-1 DVN-oppsett. Å stole på én enkelt verifiseringsnode er nøyaktig det som gjorde Kelp DAO-angrepet mulig. Krev alltid minst to uavhengige DVN-er.
- Manglende eller feil peer-konfigurasjon. Hvis
setPeerikke er kalt identisk på begge kjeder, kan enten legitime meldinger avvises eller, verre, feil adresser bli godtatt. - Ubegrenset mint uten forsyningstak. The Sandbox-hendelsen viste hvor fort en enkelt hook-feil kan la noen prege enorme mengder tokener. Sett alltid en øvre grense på mint-funksjoner, uavhengig av hvor mye du stoler på meldingslaget.
- Usikker heltallshåndtering ved konvertering. Notional-hendelsen skyldtes en direkte konvertering fra signert til usignert 128-bits heltall uten grensekontroll. Bruk trygge matte-biblioteker og eksplisitte grenseverdier for alle konverteringer som rører balanser.
- Å hoppe over replay-testing. Test alltid at samme melding sendt to ganger avvises på nytt, ikke bare én gang i en happy-path-test.
- Ingen atskilt nødpause. Hvis eneste vei til å stanse broen går via en treg, tidslåst styringsprosess, rekker du ikke å reagere før midlene er borte.
De fleste av disse punktene handler ikke om eksotiske kryptografiske svakheter, men om grunnleggende sikker Solidity-praksis som blir ekstra kritisk når kontraktene dine flytter verdier mellom flere kjeder samtidig.
Feilsøking: vanlige problemer og hvordan du løser dem
De fleste problemene du vil støte på under utvikling dukker opp enten under testnett-utrulling, når meldinger ikke dukker opp som forventet, eller under selve revisjonsprosessen når en ekstern part stiller spørsmål ved en antagelse du ikke selv hadde testet. Tabellen under samler de vanligste symptomene, hva som faktisk forårsaker dem, og hvordan du løser dem uten å måtte grave gjennom hele kodebasen på nytt.
| Problem | Sannsynlig årsak | Løsning |
|---|---|---|
| Meldingen leveres aldri på mottakerkjeden | Executor mangler gass eller peer er ikke satt på destinasjonen | Sjekk Executor-status og bekreft setPeer på begge kjeder |
lzReceive reverterer med "peer not set" | Peer-adressen er ikke registrert for kildekjedens eid | Kall setPeer med korrekt eid og adresse formatert som bytes32 |
| Testen består selv når peer-sjekken er fjernet | Testen bruker en mock som ikke faktisk håndhever OApp-basens logikk | Test mot ekte OApp-arv, ikke en forenklet stub |
| Dobbel utbetaling ved gjentatt levering | Manglende egen nonce-sperre utover rammeverkets standard | Legg til eksplisitt behandledeNonces-mapping som i steg 4 og 6 |
| Gebyrestimatet feiler med "insufficient fee" | Options-parameteren mangler riktig gassgrense for mottakerkjeden | Bygg options med korrekt lzReceive-gassgrense via options-builderen |
| DVN-kvorum når aldri konsensus | En av DVN-driftene er nede eller feilkonfigurert | Overvåk DVN-oppetid separat og ha en reserve-DVN klar |
| Rate-limiter blokkerer legitim trafikk | Grensen er satt for lavt i forhold til reelt volum | Juster MAKS_PER_VINDU basert på historiske overføringsdata |
| Pause-funksjonen kan ikke kalles under press | Nødpause krever samme multisig-terskel som vanlige oppgraderinger | Skill ut en egen, raskere nødpause-multisig som i steg 11 |
| Overvåkingsscriptet mangler hendelser fra én kjede | RPC-tilkoblingen har droppet WebSocket-abonnementet | Legg til automatisk gjenoppkobling og separat helsesjekk per kjede |
Hvis du er usikker på om testdekningen din faktisk fanger opp disse scenariene, kan det være verdt å kjøre kontraktene gjennom statisk analyse og fuzz-testing i tillegg til de manuelle testene i denne guiden.
Oversikt: krysskjede-hendelser i 2026
For å sette risikoen i perspektiv, her er en oversikt over de mest relevante krysskjede-hendelsene i 2026 og hva som gikk galt i hver av dem.
| Dato | Hendelse | Tap | Grunnårsak |
|---|---|---|---|
| 18. april 2026 | Kelp DAO / LayerZero-bro | 292 mill. USD | 1-av-1 DVN-verifisering, forfalsket melding |
| 18. mai 2026 | Verus-Ethereum-bro | 11,58 mill. USD | Svakhet i krysskjede-verifisering |
| Mai 2026 | CrossCurve (Axelar-tilknyttet kontrakt) | Ca. 3 mill. USD | Forfalskede krysskjede-meldinger |
| 22. august 2026 | The Sandbox (Base/BNB Chain) | Under 0,01 % av forsyning reelt tapt | Kapret delegat-rettighet via approveAndCall-hook |
| 4. september 2026 | Notional Finance (V1-escrow) | 1,73 mill. USD | Usikker uint128-konvertering |
| 9. september 2026 | Amnext (BNB Smart Chain) | 116 100 USD | Manglende sperre mot gjentatt premieuttak |
Til sammenligning viste også hendelser på andre kryssjede-protokoller, som Cosmos EVM-hacket som rammet seks kjeder og Switchboard-hacket på fire kjeder, at problemet med utilstrekkelig verifisering av krysskjede-data ikke er unikt for LayerZero. Det er en systemisk utfordring i hele omnichain-kategorien, og prinsippene i denne artikkelen, altså flere uavhengige verifiseringskilder, eksplisitt nonce-sperre og atskilt nødpause, gjelder like mye for team som bygger på andre meldingsprotokoller.
Hva ekstra sikkerhetslag koster i gass og ventetid
Et vanlig innvendig mot å legge til en ekstra DVN, en nonce-sperre og en rate-limiter er at det koster mer gass og gjør overføringer tregere. Det stemmer delvis, men kostnaden er langt mindre enn kostnaden ved et hack. Hver ekstra DVN i kvorumet legger typisk til én ekstern verifiseringskall på destinasjonskjeden, som i praksis betyr noen titusen ekstra gassenheter per melding, ikke en dobling av totalkostnaden. Nonce-sjekken din er en enkel mapping-oppslag og koster tilnærmende ingenting sammenlignet med selve krysskjede-leveringen. Rate-limiteren legger til to lagringsoperasjoner per kall, som er merkbart, men fortsatt en brøkdel av gebyret for selve den krysskjedede meldingen.
Ventetiden påvirkes mer av antall bekreftelser du krever på kildekjeden (parameteren confirmations i steg 5) enn av selve DVN-antallet. Flere bekreftelser betyr lenger tid før en melding regnes som endelig, men beskytter samtidig mot at en melding bygger på en blokk som senere reorganiseres bort. For overføringer med høy verdi er det verdt å akseptere noen minutter ekstra ventetid mot en vesentlig lavere sjanse for at en reorganisert blokk skaper en inkonsistent tilstand mellom kjedene.
Avanserte tips for erfarne utviklere
Optimistisk verifisering med forsinkelse for store beløp
Når grunnoppsettet er på plass, er det flere lag du kan legge til for ytterligere robusthet. Vurder en optimistisk verifiseringsmodell med en forsinkelsesperiode for store overføringer, slik at et team rekker å oppdage og stanse et mistenkelig mønster før midlene faktisk frigis på mottakerkjeden. For beløp over en gitt terskel, legg inn en 15 til 30 minutters forsinkelse med mulighet for manuell gjennomgang før utbetaling. Dette er nøyaktig den typen forsinkelse som kunne gitt The Sandbox-teamet enda mer tid til å stanse den forfalskede mint-transaksjonen før den nådde et sekundærmarked.
Bruk gjerne en dedikert blokkutforsker for omnichain-trafikk til å krysssjekke egne overvåkingsdata mot en uavhengig kilde. Hvis ditt eget system og en ekstern utforsker er uenige om hvor mange meldinger som er levert, har du funnet en feil verdt å undersøke umiddelbart, uavhengig av om avviket i seg selv ser skadelig ut eller ikke.
Kanarideployment og formell verifisering
Vurder også kanarideployment: rull ut nye versjoner av broen først med svært lave eksponeringsgrenser i noen dager, og øk gradvis etter hvert som du bygger tillit til at den nye koden oppfører seg som forventet i produksjon. Formell verifisering av de mest kritiske funksjonene, spesielt _lzReceive og mint/burn-logikken, er verdt investeringen for broer som flytter over noen titalls millioner dollar i verdi. Et verifiseringsverktøy som beviser at total mintet mengde aldri kan overstige total låst mengde, fanger opp nettopp den typen tilstandsbrudd som oppstod i The Sandbox-hendelsen, uavhengig av hvilken spesifikk kodefeil som utløser det.
Komplett prosjekt: fra kode til produksjonsklar bro
Satt sammen består det ferdige prosjektet av seks deler: avsenderkontrakten fra steg 3, mottakerkontrakten med peer- og nonce-sjekk fra steg 4 og 6, DVN-konfigurasjonsscriptet fra steg 5, rate-limiteren fra steg 8, forfalskningstesten fra steg 7, og overvåkingsscriptet fra steg 9. Legg alt i en mappestruktur som skiller kontrakter, scripts og tester tydelig fra hverandre:
omnichain-token/
├── src/
│ ├── OmniTokenSender.sol
│ └── OmniTokenReceiver.sol
├── script/
│ ├── Deploy.s.sol
│ └── ConfigureDVN.s.sol
├── test/
│ ├── SpoofingTest.t.sol
│ └── ReplayTest.t.sol
├── monitoring/
│ └── monitor.js
└── foundry.toml
Før du ruller ut til hovednett, gå gjennom en enkel sjekkliste: minst to uavhengige DVN-er konfigurert, peer satt identisk på alle kjeder, nonce-sperre testet med gjentatt levering, rate-limiter satt til et tall du faktisk har regnet ut, nødpause testet fra en atskilt multisig, ekstern revisjon bestilt eller gjennomført, og overvåkingsscriptet kjørende med bekreftet varsling. Det er nøyaktig disse syv punktene som skiller de fleste broene som ble hacket i 2026 fra dem som ikke ble det.
Sett hele prosjektet inn i kontinuerlig integrasjon slik at hele testsuiten, inkludert forfalskningstesten fra steg 7 og replay-testen, kjører automatisk på hver eneste endring før den slippes gjennom til en gjennomgang. Krev at minst to personer godkjenner enhver endring som rører peer-konfigurasjon, DVN-kvorum eller mint-logikk, uavhengig av hvor liten endringen ser ut til å være. De fleste av 2026-hendelsene vi har gått gjennom i denne artikkelen startet ikke som store, åpenbare feil, men som små konfigurasjonsjusteringer eller optimaliseringer som ingen fikk sett grundig nok gjennom før de ble en del av produksjonskoden.
Ofte stilte spørsmål om LayerZero-sikkerhet
Er LayerZero usikkert som protokoll?
Nei. Protokollkoden i seg selv har vært gjennom flere eksterne revisjoner, blant annet fra ChainSecurity. De fleste hendelsene i 2026 skyldtes feil konfigurasjon eller feil i applikasjonslaget bygget oppå protokollen, ikke feil i selve LayerZero-kjernen.
Hvor mange DVN-er bør jeg bruke minimum?
To uavhengige DVN-er fra forskjellige driftsselskaper regnes som et fornuftig minimum for broer som flytter betydelig verdi. Kelp DAO-hendelsen skjedde nettopp fordi kun én ble brukt.
Holder det å bare stole på den innebygde peer-sjekken i OApp-rammeverket?
Den er nødvendig, men ikke tilstrekkelig alene. Legg alltid til egen nonce-sperre, forsyningstak på mint-funksjoner og en rate-limiter som ekstra lag.
Hvor lang tid bør en nødpause ta å utløse?
Målet bør være minutter, ikke timer. Bruk en atskilt multisig med lav terskel spesifikt for nødstopp, adskilt fra den vanlige styringsprosessen for oppgraderinger.
Kan jeg bruke samme kontraktsmønster på andre omnichain-protokoller enn LayerZero?
Prinsippene, altså flere uavhengige verifiseringskilder, eksplisitt nonce-sperre, forsyningstak og atskilt nødpause, gjelder uavhengig av hvilken protokoll du bygger på. Selve kodedetaljene vil variere med hvilket rammeverk protokollen tilbyr.
Hvor ofte bør jeg teste hendelsesresponsplanen?
Minst én gang i kvartalet, og alltid etter enhver større endring i broens kode eller konfigurasjon.
Er det nok å revidere koden én gang før lansering?
Nei. Konfigurasjon som DVN-kvorum og peer-adresser endres over tid, og hver endring bør gjennom samme kontrollnivå som ved første lansering. Vurder også en ny revisjon ved større funksjonalitetsendringer.
Hva er forskjellen på en DVN og en klassisk oracle?
En oracle henter typisk eksterne data, som prisfeeder, inn i en kjede. En DVN gjør noe snevrere: den bekrefter kun at en spesifikk melding faktisk ble sendt fra en gitt kildekjede. Begge er tredjeparts tillitskomponenter, men angrepsflaten og verifiseringslogikken er forskjellig.
Bør jeg bygge min egen DVN i stedet for å bruke tredjeparts-tilbydere?
Kun hvis du har ressurser til å drifte og sikre infrastrukturen kontinuerlig. For de fleste team gir det bedre sikkerhet å kombinere to eller flere etablerte, uavhengige tredjeparts-DVN-er enn å drifte én egen node som i praksis blir et nytt enkelt feilpunkt.




