xdapp-book/print.html

2739 lines
287 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js ayu">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Wormhole Development Book</title>
<meta name="robots" content="noindex" />
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "ayu";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('ayu')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch (e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<a href="/" class="logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 110" preserveAspectRatio="xMidYMid meet">
<defs>
<clipPath id="__lottie_element_594">
<rect width="600" height="110" x="0" y="0"></rect>
</clipPath>
<mask id="__lottie_element_596">
<path fill="#ffffff" clip-rule="nonzero"
d="M0,0 h600 v110 h-600 v-110 M0.1770000010728836,-0.014000000432133675 C0.1770000010728836,-0.014000000432133675 0,-0.014000000432133675 0,-0.014000000432133675 C0,-0.014000000432133675 0,0.9950000047683716 0,0.9950000047683716 C0,0.9950000047683716 0.1770000010728836,0.9950000047683716 0.1770000010728836,0.9950000047683716 C0.1770000010728836,0.9950000047683716 0.1770000010728836,-0.014000000432133675 0.1770000010728836,-0.014000000432133675"
fill-opacity="1"></path>
</mask>
<filter id="__lottie_element_597">
<feColorMatrix type="matrix" color-interpolation-filters="sRGB"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"></feColorMatrix>
<feColorMatrix type="matrix" color-interpolation-filters="sRGB"
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"></feColorMatrix>
</filter>
<mask id="__lottie_element_599">
<path fill="#ffffff" clip-rule="nonzero"
d="M0,0 h600 v110 h-600 v-110 M0.1770000010728836,-0.014000000432133675 C0.1770000010728836,-0.014000000432133675 0,-0.014000000432133675 0,-0.014000000432133675 C0,-0.014000000432133675 0,0.9950000047683716 0,0.9950000047683716 C0,0.9950000047683716 0.1770000010728836,0.9950000047683716 0.1770000010728836,0.9950000047683716 C0.1770000010728836,0.9950000047683716 0.1770000010728836,-0.014000000432133675 0.1770000010728836,-0.014000000432133675"
fill-opacity="1"></path>
</mask>
<filter id="__lottie_element_600">
<feColorMatrix type="matrix" color-interpolation-filters="sRGB"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"></feColorMatrix>
<feColorMatrix type="matrix" color-interpolation-filters="sRGB"
values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"></feColorMatrix>
</filter>
</defs>
<g clip-path="url(#__lottie_element_594)">
<g mask="url(#__lottie_element_596)" filter="url(#__lottie_element_597)"
transform="matrix(1,0,0,1,6.5,3)" opacity="1" style="display: block;">
<g opacity="1" transform="matrix(1,0,0,1,51.62099838256836,51.57899856567383)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M0,51.57899856567383 C-13.680000305175781,51.56999969482422 -26.79800033569336,46.13399887084961 -36.47600173950195,36.46500015258789 C-46.15299987792969,26.79599952697754 -51.599998474121094,13.682999610900879 -51.62099838256836,0.003000000026077032 C-51.599998474121094,-13.678000450134277 -46.15299987792969,-26.79199981689453 -36.47600173950195,-36.46200180053711 C-26.798999786376953,-46.13100051879883 -13.680999755859375,-51.56800079345703 0,-51.57899856567383 C13.680999755859375,-51.56800079345703 26.798999786376953,-46.13100051879883 36.47600173950195,-36.46200180053711 C46.15399932861328,-26.79199981689453 51.599998474121094,-13.678000450134277 51.62099838256836,0.003000000026077032 C51.599998474121094,13.682999610900879 46.15299987792969,26.79599952697754 36.47600173950195,36.46500015258789 C26.79800033569336,46.13399887084961 13.680000305175781,51.56999969482422 0,51.57899856567383z M0,-47.608001708984375 C-12.628000259399414,-47.5989990234375 -24.73699951171875,-42.582000732421875 -33.66999816894531,-33.65599822998047 C-42.60300064086914,-24.729999542236328 -47.63100051879883,-12.625 -47.650001525878906,0.003000000026077032 C-47.63800048828125,12.631999969482422 -42.61199951171875,24.740999221801758 -33.676998138427734,33.66600036621094 C-24.743000030517578,42.59299850463867 -12.628999710083008,47.606998443603516 0,47.606998443603516 C12.630000114440918,47.606998443603516 24.743000030517578,42.59299850463867 33.678001403808594,33.66600036621094 C42.612998962402344,24.740999221801758 47.63800048828125,12.631999969482422 47.650001525878906,0.003000000026077032 C47.63100051879883,-12.625 42.604000091552734,-24.729999542236328 33.66999816894531,-33.65599822998047 C24.73699951171875,-42.582000732421875 12.628000259399414,-47.5989990234375 0,-47.608001708984375z">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,58.14699935913086,52.26499938964844)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M0.0010000000474974513,42.733001708984375 C-11.333000183105469,42.724998474121094 -22.20199966430664,38.22100067138672 -30.2189998626709,30.208999633789062 C-38.236000061035156,22.197999954223633 -42.74800109863281,11.333999633789062 -42.76599884033203,0 C-42.74800109863281,-11.333999633789062 -38.236000061035156,-22.197999954223633 -30.2189998626709,-30.209999084472656 C-22.20199966430664,-38.220001220703125 -11.333000183105469,-42.724998474121094 0.0010000000474974513,-42.733001708984375 C11.333999633789062,-42.724998474121094 22.201000213623047,-38.220001220703125 30.2189998626709,-30.209999084472656 C38.236000061035156,-22.197999954223633 42.749000549316406,-11.333999633789062 42.76599884033203,0 C42.749000549316406,11.333999633789062 38.236000061035156,22.197999954223633 30.2189998626709,30.208999633789062 C22.201000213623047,38.22100067138672 11.333999633789062,42.724998474121094 0.0010000000474974513,42.733001708984375z M0.0010000000474974513,-39.64899826049805 C-10.515000343322754,-39.63999938964844 -20.597999572753906,-35.46099853515625 -28.035999298095703,-28.027999877929688 C-35.4739990234375,-20.594999313354492 -39.65999984741211,-10.515000343322754 -39.67599868774414,0 C-39.65800094604492,10.515000343322754 -35.47100067138672,20.591999053955078 -28.034000396728516,28.023000717163086 C-20.59600067138672,35.45500183105469 -10.512999534606934,39.63399887084961 0.0010000000474974513,39.643001556396484 C10.515000343322754,39.63399887084961 20.59600067138672,35.45500183105469 28.034000396728516,28.023000717163086 C35.47200012207031,20.591999053955078 39.659000396728516,10.515000343322754 39.67599868774414,0 C39.659000396728516,-10.513999938964844 35.47200012207031,-20.591999053955078 28.034000396728516,-28.02400016784668 C20.59600067138672,-35.45600128173828 10.515000343322754,-39.63399887084961 0.0010000000474974513,-39.643001556396484 C0.0010000000474974513,-39.643001556396484 0.0010000000474974513,-39.64899826049805 0.0010000000474974513,-39.64899826049805z">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,64.6719970703125,52.957000732421875)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M0,33.8849983215332 C-8.987000465393066,33.87900161743164 -17.604000091552734,30.305999755859375 -23.961999893188477,23.95400047302246 C-30.319000244140625,17.60099983215332 -33.89699935913086,8.98799991607666 -33.9109992980957,0.0010000000474974513 C-33.89699935913086,-8.986000061035156 -30.319000244140625,-17.601999282836914 -23.961999893188477,-23.95400047302246 C-17.604000091552734,-30.305999755859375 -8.987000465393066,-33.87799835205078 0,-33.8849983215332 C8.98799991607666,-33.880001068115234 17.606000900268555,-30.30900001525879 23.96299934387207,-23.95599937438965 C30.320999145507812,-17.60300064086914 33.89899826049805,-8.987000465393066 33.9109992980957,0.0010000000474974513 C33.89699935913086,8.98799991607666 30.319000244140625,17.60099983215332 23.961000442504883,23.95400047302246 C17.604000091552734,30.305999755859375 8.987000465393066,33.87900161743164 0,33.8849983215332z M0,-31.680999755859375 C-8.402999877929688,-31.676000595092773 -16.461000442504883,-28.336999893188477 -22.405000686645508,-22.398000717163086 C-28.350000381469727,-16.45800018310547 -31.69499969482422,-8.402999877929688 -31.70800018310547,0 C-31.69300079345703,8.402000427246094 -28.347000122070312,16.45599937438965 -22.402999877929688,22.393999099731445 C-16.458999633789062,28.332000732421875 -8.402000427246094,31.66900062561035 0,31.673999786376953 C8.402000427246094,31.66900062561035 16.458999633789062,28.332000732421875 22.402999877929688,22.393999099731445 C28.347000122070312,16.45599937438965 31.69300079345703,8.402000427246094 31.70800018310547,0 C31.69499969482422,-8.402999877929688 28.350000381469727,-16.45800018310547 22.405000686645508,-22.398000717163086 C16.461000442504883,-28.336999893188477 8.402999877929688,-31.676000595092773 0,-31.680999755859375z">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,71.1969985961914,53.64400100708008)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M0,25.040000915527344 C-6.639999866485596,25.03499984741211 -13.006999969482422,22.393999099731445 -17.704999923706055,17.701000213623047 C-22.402000427246094,13.008000373840332 -25.045000076293945,6.642000198364258 -25.055999755859375,0.003000000026077032 C-25.047000885009766,-6.638999938964844 -22.40399932861328,-13.005000114440918 -17.707000732421875,-17.698999404907227 C-13.008999824523926,-22.395000457763672 -6.640999794006348,-25.03499984741211 0,-25.040000915527344 C6.642000198364258,-25.03499984741211 13.010000228881836,-22.395000457763672 17.707000732421875,-17.698999404907227 C22.405000686645508,-13.005000114440918 25.047000885009766,-6.638999938964844 25.055999755859375,0.003000000026077032 C25.04599952697754,6.642000198364258 22.402000427246094,13.008000373840332 17.704999923706055,17.701000213623047 C13.008000373840332,22.393999099731445 6.640999794006348,25.03499984741211 0,25.040000915527344z M0,-23.716999053955078 C-6.289999961853027,-23.711999893188477 -12.321000099182129,-21.209999084472656 -16.770999908447266,-16.763999938964844 C-21.219999313354492,-12.317000389099121 -23.724000930786133,-6.288000106811523 -23.73200035095215,0.003000000026077032 C-23.722000122070312,6.291999816894531 -21.216999053955078,12.321000099182129 -16.768999099731445,16.766000747680664 C-12.319000244140625,21.211999893188477 -6.289000034332275,23.709999084472656 0,23.715999603271484 C6.289999961853027,23.709999084472656 12.319999694824219,21.211999893188477 16.768999099731445,16.766000747680664 C21.218000411987305,12.321000099182129 23.722000122070312,6.291999816894531 23.73200035095215,0.003000000026077032 C23.724000930786133,-6.288000106811523 21.22100067138672,-12.317000389099121 16.770999908447266,-16.763999938964844 C12.321999549865723,-21.209999084472656 6.290999889373779,-23.711999893188477 0,-23.716999053955078z">
</path>
</g>
</g>
<g mask="url(#__lottie_element_599)" filter="url(#__lottie_element_600)"
transform="matrix(1,0,0,1,6.5,3)" opacity="1" style="display: block;">
<g opacity="1" transform="matrix(1,0,0,1,165.5959930419922,50.316001892089844)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M26.5310001373291,-18.825000762939453 C26.5310001373291,-18.825000762939453 12.987000465393066,19.18199920654297 12.987000465393066,19.18199920654297 C12.925000190734863,19.364999771118164 12.803000450134277,19.523000717163086 12.640000343322754,19.6299991607666 C12.47700023651123,19.73699951171875 12.282999992370605,19.785999298095703 12.08899974822998,19.770999908447266 C12.08899974822998,19.770999908447266 11.062000274658203,19.770999908447266 11.062000274658203,19.770999908447266 C10.866999626159668,19.77899932861328 10.673999786376953,19.72599983215332 10.510000228881836,19.6200008392334 C10.347000122070312,19.514999389648438 10.220000267028809,19.36199951171875 10.147000312805176,19.18199920654297 C10.147000312805176,19.18199920654297 -0.007000000216066837,-7.4730000495910645 -0.007000000216066837,-7.4730000495910645 C-0.007000000216066837,-7.4730000495910645 -10.144000053405762,19.18199920654297 -10.144000053405762,19.18199920654297 C-10.211000442504883,19.36199951171875 -10.335000038146973,19.516000747680664 -10.496000289916992,19.621999740600586 C-10.657999992370605,19.72800064086914 -10.848999977111816,19.780000686645508 -11.043000221252441,19.770999908447266 C-11.043000221252441,19.770999908447266 -12.086000442504883,19.770999908447266 -12.086000442504883,19.770999908447266 C-12.279000282287598,19.7810001373291 -12.470999717712402,19.729999542236328 -12.633000373840332,19.624000549316406 C-12.795000076293945,19.51799964904785 -12.918000221252441,19.363000869750977 -12.984000205993652,19.18199920654297 C-12.984000205993652,19.18199920654297 -26.527999877929688,-18.812000274658203 -26.527999877929688,-18.812000274658203 C-26.736000061035156,-19.400999069213867 -26.511999130249023,-19.766000747680664 -25.934999465942383,-19.766000747680664 C-25.934999465942383,-19.766000747680664 -21.743000030517578,-19.766000747680664 -21.743000030517578,-19.766000747680664 C-21.54599952697754,-19.78499984741211 -21.347999572753906,-19.73900032043457 -21.180999755859375,-19.631999969482422 C-21.014999389648438,-19.524999618530273 -20.889999389648438,-19.364999771118164 -20.827999114990234,-19.17799949645996 C-20.827999114990234,-19.17799949645996 -11.173999786376953,9.597999572753906 -11.173999786376953,9.597999572753906 C-11.173999786376953,9.597999572753906 -1.3580000400543213,-16.7549991607666 -1.3580000400543213,-16.7549991607666 C-1.2879999876022339,-16.937000274658203 -1.1610000133514404,-17.090999603271484 -0.996999979019165,-17.19700050354004 C-0.8320000171661377,-17.30299949645996 -0.6389999985694885,-17.354000091552734 -0.4429999887943268,-17.3439998626709 C-0.4429999887943268,-17.3439998626709 0.4390000104904175,-17.3439998626709 0.4390000104904175,-17.3439998626709 C0.6330000162124634,-17.354000091552734 0.8240000009536743,-17.301000595092773 0.9860000014305115,-17.19499969482422 C1.1480000019073486,-17.089000701904297 1.2710000276565552,-16.93600082397461 1.3380000591278076,-16.7549991607666 C1.3380000591278076,-16.7549991607666 11.444999694824219,9.597999572753906 11.444999694824219,9.597999572753906 C11.444999694824219,9.597999572753906 20.874000549316406,-19.17799949645996 20.874000549316406,-19.17799949645996 C20.937000274658203,-19.364999771118164 21.06100082397461,-19.524999618530273 21.22800064086914,-19.631999969482422 C21.393999099731445,-19.73900032043457 21.591999053955078,-19.78499984741211 21.790000915527344,-19.766000747680664 C21.790000915527344,-19.766000747680664 25.917999267578125,-19.766000747680664 25.917999267578125,-19.766000747680664 C26.51099967956543,-19.766000747680664 26.736000061035156,-19.400999069213867 26.527999877929688,-18.825000762939453">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,230.92799377441406,49.98500061035156)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M20.148000717163086,0.017999999225139618 C20.110000610351562,3.931999921798706 18.905000686645508,7.747000217437744 16.68600082397461,10.984000205993652 C14.467000007629395,14.220000267028809 11.333000183105469,16.732999801635742 7.677999973297119,18.20400047302246 C4.021999835968018,19.677000045776367 0.009999999776482582,20.042999267578125 -3.8540000915527344,19.256999969482422 C-7.7179999351501465,18.469999313354492 -11.26099967956543,16.566999435424805 -14.03600025177002,13.78600025177002 C-16.812000274658203,11.003999710083008 -18.69700050354004,7.46999979019165 -19.452999114990234,3.628000020980835 C-20.208999633789062,-0.21299999952316284 -19.80299949645996,-4.191999912261963 -18.28499984741211,-7.804999828338623 C-16.767000198364258,-11.418000221252441 -14.204999923706055,-14.505000114440918 -10.92300033569336,-16.676000595092773 C-7.639999866485596,-18.847000122070312 -3.7839999198913574,-20.0049991607666 0.16099999845027924,-20.0049991607666 C2.812000036239624,-20.04199981689453 5.443999767303467,-19.548999786376953 7.8979997634887695,-18.55299949645996 C10.352999687194824,-17.558000564575195 12.579000473022461,-16.08099937438965 14.444999694824219,-14.211999893188477 C16.31100082397461,-12.342000007629395 17.777999877929688,-10.119000434875488 18.757999420166016,-7.673999786376953 C19.73699951171875,-5.229000091552734 20.209999084472656,-2.611999988555908 20.148000717163086,0.017999999225139618z M14.604999542236328,0.017999999225139618 C14.604999542236328,-9.196000099182129 8.54800033569336,-15.543999671936035 0.16099999845027924,-15.543999671936035 C-8.22599983215332,-15.543999671936035 -14.281000137329102,-9.196000099182129 -14.281000137329102,0.017999999225139618 C-14.281000137329102,9.232999801635742 -8.22599983215332,15.579000473022461 0.16099999845027924,15.579000473022461 C8.54800033569336,15.579000473022461 14.604999542236328,9.232999801635742 14.604999542236328,0.017999999225139618z">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,289.0769958496094,50.01300048828125)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M14.923999786376953,17.861000061035156 C15.052000045776367,18.006999969482422 15.133000373840332,18.190000534057617 15.154000282287598,18.382999420166016 C15.175999641418457,18.576000213623047 15.137999534606934,18.770000457763672 15.045999526977539,18.94099998474121 C14.95300006866455,19.11199951171875 14.810999870300293,19.250999450683594 14.63700008392334,19.34000015258789 C14.46399974822998,19.429000854492188 14.267000198364258,19.46299934387207 14.072999954223633,19.43899917602539 C14.072999954223633,19.43899917602539 10.29699993133545,19.43899917602539 10.29699993133545,19.43899917602539 C10.173999786376953,19.434999465942383 10.055000305175781,19.402000427246094 9.947999954223633,19.34000015258789 C9.842000007629395,19.280000686645508 9.75100040435791,19.194000244140625 9.685999870300293,19.089000701904297 C9.685999870300293,19.089000701904297 0.20999999344348907,4.900000095367432 0.20999999344348907,4.900000095367432 C0.20999999344348907,4.900000095367432 -9.973999977111816,4.900000095367432 -9.973999977111816,4.900000095367432 C-9.973999977111816,4.900000095367432 -9.973999977111816,18.643999099731445 -9.973999977111816,18.643999099731445 C-9.97700023651123,18.854999542236328 -10.062000274658203,19.055999755859375 -10.211999893188477,19.20599937438965 C-10.362000465393066,19.354999542236328 -10.567000389099121,19.440000534057617 -10.779000282287598,19.44300079345703 C-10.779000282287598,19.44300079345703 -14.467000007629395,19.44300079345703 -14.467000007629395,19.44300079345703 C-14.678999900817871,19.43899917602539 -14.880999565124512,19.35300064086914 -15.031000137329102,19.20400047302246 C-15.180999755859375,19.054000854492188 -15.265999794006348,18.854000091552734 -15.267999649047852,18.643999099731445 C-15.267999649047852,18.643999099731445 -15.267999649047852,-18.663000106811523 -15.267999649047852,-18.663000106811523 C-15.265000343322754,-18.874000549316406 -15.180000305175781,-19.075000762939453 -15.031000137329102,-19.224000930786133 C-14.881999969482422,-19.37299919128418 -14.678999900817871,-19.458999633789062 -14.467000007629395,-19.46299934387207 C-14.467000007629395,-19.46299934387207 0.039000000804662704,-19.46299934387207 0.039000000804662704,-19.46299934387207 C10.192000389099121,-19.46299934387207 15.206000328063965,-14.168000221252441 15.206000328063965,-7.2829999923706055 C15.267999649047852,-4.546999931335449 14.340999603271484,-1.878999948501587 12.593999862670898,0.23800000548362732 C10.847000122070312,2.3550000190734863 8.395000457763672,3.7820000648498535 5.679999828338623,4.260000228881836 C5.679999828338623,4.260000228881836 14.923999786376953,17.861000061035156 14.923999786376953,17.861000061035156z M-0.9959999918937683,0.43799999356269836 C6.98799991607666,0.43799999356269836 9.668999671936035,-2.8480000495910645 9.668999671936035,-7.28000020980835 C9.668999671936035,-11.711999893188477 6.98799991607666,-14.996999740600586 -0.9959999918937683,-14.996999740600586 C-0.9959999918937683,-14.996999740600586 -9.977999687194824,-14.996999740600586 -9.977999687194824,-14.996999740600586 C-9.977999687194824,-14.996999740600586 -9.977999687194824,0.43799999356269836 -9.977999687194824,0.43799999356269836 C-9.977999687194824,0.43799999356269836 -0.9959999918937683,0.43799999356269836 -0.9959999918937683,0.43799999356269836z">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,347.6789855957031,49.689998626708984)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M18.70400047302246,-19.766000747680664 C18.70400047302246,-19.766000747680664 19.908000946044922,-19.766000747680664 19.908000946044922,-19.766000747680664 C20.016000747680664,-19.773000717163086 20.12299919128418,-19.756999969482422 20.225000381469727,-19.7189998626709 C20.325000762939453,-19.68199920654297 20.416000366210938,-19.62299919128418 20.493000030517578,-19.547000885009766 C20.569000244140625,-19.472000122070312 20.628000259399414,-19.381000518798828 20.666000366210938,-19.2810001373291 C20.70400047302246,-19.180999755859375 20.72100067138672,-19.073999404907227 20.71299934387207,-18.966999053955078 C20.71299934387207,-18.966999053955078 20.71299934387207,18.9689998626709 20.71299934387207,18.9689998626709 C20.72100067138672,19.075000762939453 20.70400047302246,19.183000564575195 20.666000366210938,19.283000946044922 C20.628000259399414,19.382999420166016 20.569000244140625,19.472999572753906 20.493000030517578,19.548999786376953 C20.416000366210938,19.624000549316406 20.325000762939453,19.683000564575195 20.225000381469727,19.72100067138672 C20.12299919128418,19.757999420166016 20.016000747680664,19.774999618530273 19.908000946044922,19.76799964904785 C19.908000946044922,19.76799964904785 16.22100067138672,19.76799964904785 16.22100067138672,19.76799964904785 C16.11199951171875,19.774999618530273 16.003999710083008,19.757999420166016 15.904000282287598,19.72100067138672 C15.803999900817871,19.683000564575195 15.711000442504883,19.624000549316406 15.63599967956543,19.548999786376953 C15.559000015258789,19.472999572753906 15.5,19.382999420166016 15.461999893188477,19.283000946044922 C15.423999786376953,19.183000564575195 15.407999992370605,19.075000762939453 15.414999961853027,18.9689998626709 C15.414999961853027,18.9689998626709 15.414999961853027,-7.9629998207092285 15.414999961853027,-7.9629998207092285 C15.414999961853027,-7.9629998207092285 0.5540000200271606,13.385000228881836 0.5540000200271606,13.385000228881836 C0.5040000081062317,13.489999771118164 0.4269999861717224,13.579000473022461 0.328000009059906,13.642999649047852 C0.2290000021457672,13.704999923706055 0.11500000208616257,13.73799991607666 -0.0020000000949949026,13.73799991607666 C-0.11900000274181366,13.73799991607666 -0.2329999953508377,13.704999923706055 -0.3319999873638153,13.642999649047852 C-0.4300000071525574,13.579000473022461 -0.5090000033378601,13.489999771118164 -0.5569999814033508,13.385000228881836 C-0.5569999814033508,13.385000228881836 -15.416000366210938,-7.9629998207092285 -15.416000366210938,-7.9629998207092285 C-15.416000366210938,-7.9629998207092285 -15.416000366210938,18.96500015258789 -15.416000366210938,18.96500015258789 C-15.409000396728516,19.07200050354004 -15.425000190734863,19.18000030517578 -15.463000297546387,19.27899932861328 C-15.50100040435791,19.3799991607666 -15.5600004196167,19.47100067138672 -15.63599967956543,19.54599952697754 C-15.711999893188477,19.621999740600586 -15.803999900817871,19.68000030517578 -15.904000282287598,19.716999053955078 C-16.0049991607666,19.7549991607666 -16.11199951171875,19.770999908447266 -16.22100067138672,19.763999938964844 C-16.22100067138672,19.763999938964844 -19.909000396728516,19.763999938964844 -19.909000396728516,19.763999938964844 C-20.017000198364258,19.770999908447266 -20.124000549316406,19.7549991607666 -20.225000381469727,19.716999053955078 C-20.326000213623047,19.68000030517578 -20.41699981689453,19.621999740600586 -20.493999481201172,19.54599952697754 C-20.569000244140625,19.47100067138672 -20.628999710083008,19.3799991607666 -20.66699981689453,19.27899932861328 C-20.704999923706055,19.18000030517578 -20.72100067138672,19.07200050354004 -20.714000701904297,18.96500015258789 C-20.714000701904297,18.96500015258789 -20.714000701904297,-18.97100067138672 -20.714000701904297,-18.97100067138672 C-20.72100067138672,-19.077999114990234 -20.704999923706055,-19.18400001525879 -20.66699981689453,-19.284000396728516 C-20.628999710083008,-19.384000778198242 -20.569000244140625,-19.475000381469727 -20.493999481201172,-19.551000595092773 C-20.41699981689453,-19.625999450683594 -20.326000213623047,-19.684999465942383 -20.225000381469727,-19.722999572753906 C-20.124000549316406,-19.760000228881836 -20.017000198364258,-19.775999069213867 -19.909000396728516,-19.768999099731445 C-19.909000396728516,-19.768999099731445 -18.7189998626709,-19.768999099731445 -18.7189998626709,-19.768999099731445 C-18.518999099731445,-19.76799964904785 -18.320999145507812,-19.7189998626709 -18.14699935913086,-19.624000549316406 C-17.97100067138672,-19.52899932861328 -17.820999145507812,-19.39299964904785 -17.71299934387207,-19.226999282836914 C-17.71299934387207,-19.226999282836914 -0.007000000216066837,5.309000015258789 -0.007000000216066837,5.309000015258789 C-0.007000000216066837,5.309000015258789 17.711999893188477,-19.226999282836914 17.711999893188477,-19.226999282836914 C17.815000534057617,-19.392000198364258 17.959999084472656,-19.527000427246094 18.131000518798828,-19.621999740600586 C18.30299949645996,-19.716999053955078 18.4950008392334,-19.76799964904785 18.69099998474121,-19.768999099731445">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,410.510986328125,50.000999450683594)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M17.5,-18.652000427246094 C17.5,-18.652000427246094 17.5,18.652000427246094 17.5,18.652000427246094 C17.496000289916992,18.863000869750977 17.40999984741211,19.06399917602539 17.260000228881836,19.214000701904297 C17.108999252319336,19.36199951171875 16.9060001373291,19.447999954223633 16.694000244140625,19.451000213623047 C16.694000244140625,19.451000213623047 13.005000114440918,19.451000213623047 13.005000114440918,19.451000213623047 C12.793000221252441,19.447999954223633 12.59000015258789,19.36199951171875 12.4399995803833,19.214000701904297 C12.289999961853027,19.06399917602539 12.204999923706055,18.863000869750977 12.20199966430664,18.652000427246094 C12.20199966430664,18.652000427246094 12.20199966430664,1.6579999923706055 12.20199966430664,1.6579999923706055 C12.20199966430664,1.6579999923706055 -12.201000213623047,1.6579999923706055 -12.201000213623047,1.6579999923706055 C-12.201000213623047,1.6579999923706055 -12.201000213623047,18.652000427246094 -12.201000213623047,18.652000427246094 C-12.204999923706055,18.863000869750977 -12.291000366210938,19.06399917602539 -12.4399995803833,19.214000701904297 C-12.590999603271484,19.36199951171875 -12.793000221252441,19.447999954223633 -13.005000114440918,19.451000213623047 C-13.005000114440918,19.451000213623047 -16.694000244140625,19.451000213623047 -16.694000244140625,19.451000213623047 C-16.9060001373291,19.447999954223633 -17.107999801635742,19.36199951171875 -17.259000778198242,19.214000701904297 C-17.409000396728516,19.06399917602539 -17.496000289916992,18.863000869750977 -17.5,18.652000427246094 C-17.5,18.652000427246094 -17.5,-18.652000427246094 -17.5,-18.652000427246094 C-17.496000289916992,-18.863000869750977 -17.409000396728516,-19.062999725341797 -17.259000778198242,-19.211999893188477 C-17.107999801635742,-19.361000061035156 -16.9060001373291,-19.44700050354004 -16.694000244140625,-19.451000213623047 C-16.694000244140625,-19.451000213623047 -13.005000114440918,-19.451000213623047 -13.005000114440918,-19.451000213623047 C-12.793000221252441,-19.44700050354004 -12.590999603271484,-19.361000061035156 -12.4399995803833,-19.211999893188477 C-12.291000366210938,-19.062999725341797 -12.204999923706055,-18.863000869750977 -12.201000213623047,-18.652000427246094 C-12.201000213623047,-18.652000427246094 -12.201000213623047,-2.805000066757202 -12.201000213623047,-2.805000066757202 C-12.201000213623047,-2.805000066757202 12.20199966430664,-2.805000066757202 12.20199966430664,-2.805000066757202 C12.20199966430664,-2.805000066757202 12.20199966430664,-18.652000427246094 12.20199966430664,-18.652000427246094 C12.204999923706055,-18.863000869750977 12.289999961853027,-19.062999725341797 12.4399995803833,-19.211999893188477 C12.59000015258789,-19.361000061035156 12.793000221252441,-19.44700050354004 13.005000114440918,-19.451000213623047 C13.005000114440918,-19.451000213623047 16.694000244140625,-19.451000213623047 16.694000244140625,-19.451000213623047 C16.9060001373291,-19.44700050354004 17.108999252319336,-19.361000061035156 17.260000228881836,-19.211999893188477 C17.40999984741211,-19.062999725341797 17.496000289916992,-18.863000869750977 17.5,-18.652000427246094z">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,470.5679931640625,49.98500061035156)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M20.14699935913086,0.017999999225139618 C20.108999252319336,3.931999921798706 18.905000686645508,7.747000217437744 16.68600082397461,10.984000205993652 C14.467000007629395,14.220000267028809 11.333000183105469,16.732999801635742 7.677999973297119,18.20400047302246 C4.0229997634887695,19.677000045776367 0.009999999776482582,20.042999267578125 -3.8540000915527344,19.256999969482422 C-7.7179999351501465,18.469999313354492 -11.26099967956543,16.566999435424805 -14.036999702453613,13.78600025177002 C-16.812999725341797,11.003999710083008 -18.697999954223633,7.46999979019165 -19.45400047302246,3.628000020980835 C-20.208999633789062,-0.21299999952316284 -19.80299949645996,-4.191999912261963 -18.28499984741211,-7.804999828338623 C-16.767000198364258,-11.418000221252441 -14.204999923706055,-14.505000114440918 -10.92300033569336,-16.676000595092773 C-7.639999866485596,-18.847000122070312 -3.7839999198913574,-20.0049991607666 0.16099999845027924,-20.0049991607666 C2.812000036239624,-20.04199981689453 5.442999839782715,-19.548999786376953 7.8979997634887695,-18.55299949645996 C10.35200023651123,-17.558000564575195 12.579000473022461,-16.08099937438965 14.444000244140625,-14.211999893188477 C16.309999465942383,-12.342000007629395 17.777000427246094,-10.119000434875488 18.756999969482422,-7.673999786376953 C19.73699951171875,-5.229000091552734 20.208999633789062,-2.611999988555908 20.14699935913086,0.017999999225139618z M14.604000091552734,0.017999999225139618 C14.604000091552734,-9.196000099182129 8.54800033569336,-15.543999671936035 0.16099999845027924,-15.543999671936035 C-8.22599983215332,-15.543999671936035 -14.281999588012695,-9.196000099182129 -14.281999588012695,0.017999999225139618 C-14.281999588012695,9.232999801635742 -8.22599983215332,15.579000473022461 0.16099999845027924,15.579000473022461 C8.54800033569336,15.579000473022461 14.604000091552734,9.232999801635742 14.604000091552734,0.017999999225139618z">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,527.3350219726562,50.00400161743164)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M13.864999771118164,15.779999732971191 C13.864999771118164,15.779999732971191 13.864999771118164,18.648000717163086 13.864999771118164,18.648000717163086 C13.871999740600586,18.756000518798828 13.854999542236328,18.86199951171875 13.817000389099121,18.961999893188477 C13.779000282287598,19.062000274658203 13.718999862670898,19.152999877929688 13.642999649047852,19.22800064086914 C13.567000389099121,19.304000854492188 13.475000381469727,19.363000869750977 13.375,19.399999618530273 C13.27400016784668,19.437999725341797 13.166999816894531,19.45400047302246 13.057999610900879,19.44700050354004 C13.057999610900879,19.44700050354004 -13.07699966430664,19.44700050354004 -13.07699966430664,19.44700050354004 C-13.288999557495117,19.444000244140625 -13.491999626159668,19.357999801635742 -13.642000198364258,19.209999084472656 C-13.791999816894531,19.059999465942383 -13.878000259399414,18.858999252319336 -13.881999969482422,18.648000717163086 C-13.881999969482422,18.648000717163086 -13.881999969482422,-18.655000686645508 -13.881999969482422,-18.655000686645508 C-13.878000259399414,-18.865999221801758 -13.791999816894531,-19.066999435424805 -13.642000198364258,-19.215999603271484 C-13.491999626159668,-19.364999771118164 -13.288999557495117,-19.450000762939453 -13.07699966430664,-19.45400047302246 C-13.07699966430664,-19.45400047302246 -9.387999534606934,-19.45400047302246 -9.387999534606934,-19.45400047302246 C-9.175999641418457,-19.448999404907227 -8.973999977111816,-19.36400032043457 -8.824999809265137,-19.21500015258789 C-8.675000190734863,-19.06599998474121 -8.59000015258789,-18.865999221801758 -8.586999893188477,-18.655000686645508 C-8.586999893188477,-18.655000686645508 -8.586999893188477,14.980999946594238 -8.586999893188477,14.980999946594238 C-8.586999893188477,14.980999946594238 13.069000244140625,14.980999946594238 13.069000244140625,14.980999946594238 C13.177000045776367,14.973999977111816 13.284000396728516,14.991000175476074 13.385000228881836,15.027999877929688 C13.486000061035156,15.065999984741211 13.57699966430664,15.125 13.654000282287598,15.199999809265137 C13.729999542236328,15.276000022888184 13.788999557495117,15.366999626159668 13.82699966430664,15.467000007629395 C13.864999771118164,15.565999984741211 13.880999565124512,15.673999786376953 13.87399959564209,15.779999732971191">
</path>
</g>
<g opacity="1" transform="matrix(1,0,0,1,577.2100219726562,49.99800109863281)">
<path fill="rgb(0,0,0)" fill-opacity="1"
d=" M14.913000106811523,15.784000396728516 C14.913000106811523,15.784000396728516 14.913000106811523,18.652000427246094 14.913000106811523,18.652000427246094 C14.920000076293945,18.759000778198242 14.902999877929688,18.864999771118164 14.864999771118164,18.96500015258789 C14.82699966430664,19.06399917602539 14.767999649047852,19.1560001373291 14.692999839782715,19.23200035095215 C14.616000175476074,19.30699920654297 14.524999618530273,19.365999221801758 14.423999786376953,19.40399932861328 C14.322999954223633,19.44099998474121 14.21500015258789,19.45800018310547 14.107000350952148,19.451000213623047 C14.107000350952148,19.451000213623047 -14.116999626159668,19.451000213623047 -14.116999626159668,19.451000213623047 C-14.329000473022461,19.44700050354004 -14.531999588012695,19.36199951171875 -14.682000160217285,19.21299934387207 C-14.831999778747559,19.06399917602539 -14.918000221252441,18.863000869750977 -14.92199993133545,18.652000427246094 C-14.92199993133545,18.652000427246094 -14.92199993133545,-18.652000427246094 -14.92199993133545,-18.652000427246094 C-14.918000221252441,-18.86199951171875 -14.831999778747559,-19.065000534057617 -14.682000160217285,-19.214000701904297 C-14.531999588012695,-19.363000869750977 -14.329000473022461,-19.447999954223633 -14.116999626159668,-19.451000213623047 C-14.116999626159668,-19.451000213623047 13.468000411987305,-19.451000213623047 13.468000411987305,-19.451000213623047 C13.574999809265137,-19.45800018310547 13.682999610900879,-19.44099998474121 13.784000396728516,-19.40399932861328 C13.885000228881836,-19.365999221801758 13.97599983215332,-19.30699920654297 14.052000045776367,-19.23200035095215 C14.128000259399414,-19.1560001373291 14.185999870300293,-19.065000534057617 14.223999977111816,-18.96500015258789 C14.26200008392334,-18.864999771118164 14.279999732971191,-18.757999420166016 14.27299976348877,-18.652000427246094 C14.27299976348877,-18.652000427246094 14.27299976348877,-15.782999992370605 14.27299976348877,-15.782999992370605 C14.279999732971191,-15.675999641418457 14.26200008392334,-15.569999694824219 14.223999977111816,-15.468999862670898 C14.185999870300293,-15.369999885559082 14.128000259399414,-15.279000282287598 14.052000045776367,-15.20300006866455 C13.97599983215332,-15.128000259399414 13.885000228881836,-15.069000244140625 13.784000396728516,-15.031000137329102 C13.682999610900879,-14.994000434875488 13.574999809265137,-14.977999687194824 13.468000411987305,-14.984999656677246 C13.468000411987305,-14.984999656677246 -9.633000373840332,-14.984999656677246 -9.633000373840332,-14.984999656677246 C-9.633000373840332,-14.984999656677246 -9.633000373840332,-2.805000066757202 -9.633000373840332,-2.805000066757202 C-9.633000373840332,-2.805000066757202 10.89799976348877,-2.805000066757202 10.89799976348877,-2.805000066757202 C11.003999710083008,-2.812000036239624 11.111000061035156,-2.796999931335449 11.210000038146973,-2.759999990463257 C11.309000015258789,-2.7239999771118164 11.39900016784668,-2.6659998893737793 11.473999977111816,-2.5920000076293945 C11.550999641418457,-2.5179998874664307 11.609999656677246,-2.428999900817871 11.64799976348877,-2.3310000896453857 C11.687000274658203,-2.2330000400543213 11.706000328063965,-2.128000020980835 11.701000213623047,-2.0230000019073486 C11.701000213623047,-2.0230000019073486 11.701000213623047,0.8610000014305115 11.701000213623047,0.8610000014305115 C11.706999778747559,0.9670000076293945 11.690999984741211,1.0750000476837158 11.652999877929688,1.1740000247955322 C11.614999771118164,1.2730000019073486 11.555000305175781,1.3630000352859497 11.479000091552734,1.437999963760376 C11.402999877929688,1.5130000114440918 11.312999725341797,1.5709999799728394 11.213000297546387,1.6089999675750732 C11.11299991607666,1.6460000276565552 11.005000114440918,1.6640000343322754 10.89799976348877,1.6579999923706055 C10.89799976348877,1.6579999923706055 -9.633000373840332,1.6579999923706055 -9.633000373840332,1.6579999923706055 C-9.633000373840332,1.6579999923706055 -9.633000373840332,14.984000205993652 -9.633000373840332,14.984000205993652 C-9.633000373840332,14.984000205993652 14.112000465393066,14.984000205993652 14.112000465393066,14.984000205993652 C14.218999862670898,14.97700023651123 14.326000213623047,14.994000434875488 14.427000045776367,15.031999588012695 C14.527000427246094,15.069000244140625 14.619000434875488,15.128000259399414 14.694999694824219,15.204000473022461 C14.770999908447266,15.279000282287598 14.831000328063965,15.369999885559082 14.869000434875488,15.470999717712402 C14.906999588012695,15.569999694824219 14.92300033569336,15.677000045776367 14.916000366210938,15.784000396728516">
</path>
</g>
</g>
</g>
</svg>
</a>
<ol class="chapter"><li class="chapter-item expanded affix "><a href="introduction/introduction.html">Introduction</a></li><li class="spacer"></li><li class="chapter-item expanded affix "><li class="part-title">Overview</li><li class="chapter-item expanded "><a href="dapps/0_xdappOverview.html"><strong aria-hidden="true">1.</strong> xDapps</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="dapps/1_defiBasics.html"><strong aria-hidden="true">1.1.</strong> Ecosystem Basics</a></li><li class="chapter-item expanded "><a href="dapps/2_crossChainInteroperability.html"><strong aria-hidden="true">1.2.</strong> Blockchain Interoperability</a></li><li class="chapter-item expanded "><a href="dapps/3_xdataxassets.html"><strong aria-hidden="true">1.3.</strong> xData &amp; xAssets</a></li><li class="chapter-item expanded "><a href="dapps/4_whatIsanXdapp.html"><strong aria-hidden="true">1.4.</strong> What is an xDapp?</a></li><li class="chapter-item expanded "><a href="dapps/5_advantages.html"><strong aria-hidden="true">1.5.</strong> Advantages of xDapps</a></li></ol></li><li class="chapter-item expanded "><a href="wormhole/0_wormholeOverview.html"><strong aria-hidden="true">2.</strong> Wormhole</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="wormhole/1_whatIsWormhole.html"><strong aria-hidden="true">2.1.</strong> What is Wormhole?</a></li><li class="chapter-item expanded "><a href="wormhole/2_architectureOverview.html"><strong aria-hidden="true">2.2.</strong> Architecture Overview</a></li><li class="chapter-item expanded "><a href="wormhole/3_coreLayerContracts.html"><strong aria-hidden="true">2.3.</strong> Core Layer Contracts</a></li><li class="chapter-item expanded "><a href="wormhole/4_vaa.html"><strong aria-hidden="true">2.4.</strong> VAA: Verified Action Approval</a></li><li class="chapter-item expanded "><a href="wormhole/5_guardianNetwork.html"><strong aria-hidden="true">2.5.</strong> Guardian Network</a></li><li class="chapter-item expanded "><a href="wormhole/6_relayers.html"><strong aria-hidden="true">2.6.</strong> Relayers</a></li><li class="chapter-item expanded "><a href="wormhole/7_xAssetBridge.html"><strong aria-hidden="true">2.7.</strong> xAsset Bridge</a></li><li class="chapter-item expanded "><a href="wormhole/8_wormchain.html"><strong aria-hidden="true">2.8.</strong> Wormchain</a></li></ol></li><li class="chapter-item expanded "><a href="wormhole/security.html"><strong aria-hidden="true">3.</strong> Security</a></li><li class="chapter-item expanded "><a href="dapps/architecture/0_dappDesign.html"><strong aria-hidden="true">4.</strong> xDapp Design</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="dapps/architecture/1_keyConsiderations.html"><strong aria-hidden="true">4.1.</strong> Key Considerations</a></li><li class="chapter-item expanded "><a href="dapps/architecture/2_ecosystems.html"><strong aria-hidden="true">4.2.</strong> Ecosystems</a></li><li class="chapter-item expanded "><a href="dapps/architecture/3_protocolDesign.html"><strong aria-hidden="true">4.3.</strong> Protocol Design</a></li><li class="chapter-item expanded "><a href="dapps/architecture/4_topology.html"><strong aria-hidden="true">4.4.</strong> Topology</a></li><li class="chapter-item expanded "><a href="dapps/architecture/5_relayers.html"><strong aria-hidden="true">4.5.</strong> Relayers</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><li class="part-title">Developing xDapps</li><li class="chapter-item expanded "><a href="technical/env/environments.html"><strong aria-hidden="true">5.</strong> Environment Setup</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="technical/env/tilt.html"><strong aria-hidden="true">5.1.</strong> Tilt (Devnet)</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="technical/env/troubleshooting.html"><strong aria-hidden="true">5.1.1.</strong> Troubleshooting</a></li></ol></li><li class="chapter-item expanded "><a href="technical/env/wlv.html"><strong aria-hidden="true">5.2.</strong> Wormhole Local Validator</a></li><li class="chapter-item expanded "><a href="technical/env/testnet.html"><strong aria-hidden="true">5.3.</strong> Testnet</a></li><li class="chapter-item expanded "><a href="technical/env/tooling.html"><strong aria-hidden="true">5.4.</strong> Tooling</a></li></ol></li><li class="chapter-item expanded "><a href="technical/overview.html"><strong aria-hidden="true">6.</strong> Contract Development</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="technical/evm/overview.html"><strong aria-hidden="true">6.1.</strong> EVM</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="technical/evm/coreLayer.html"><strong aria-hidden="true">6.1.1.</strong> Core Layer</a></li><li class="chapter-item expanded "><a href="technical/evm/tokenLayer.html"><strong aria-hidden="true">6.1.2.</strong> Token Bridge Module</a></li><li class="chapter-item expanded "><a href="technical/evm/nftLayer.html"><strong aria-hidden="true">6.1.3.</strong> NFT Bridge Module</a></li><li class="chapter-item expanded "><a href="technical/evm/relayer.html"><strong aria-hidden="true">6.1.4.</strong> Relayer Module</a></li><li class="chapter-item expanded "><a href="technical/evm/bestPractices.html"><strong aria-hidden="true">6.1.5.</strong> Best Practices</a></li><li class="chapter-item expanded "><a href="technical/evm/composableVerification.html"><strong aria-hidden="true">6.1.6.</strong> Composable Verification</a></li></ol></li><li class="chapter-item expanded "><a href="technical/solana/overview.html"><strong aria-hidden="true">6.2.</strong> Solana</a></li><li class="chapter-item expanded "><a href="technical/cosmos/overview.html"><strong aria-hidden="true">6.3.</strong> Cosmos</a></li><li class="chapter-item expanded "><a href="technical/algorand/overview.html"><strong aria-hidden="true">6.4.</strong> Algorand</a></li><li class="chapter-item expanded "><a href="technical/near/overview.html"><strong aria-hidden="true">6.5.</strong> Near</a></li><li class="chapter-item expanded "><a href="technical/aptos/overview.html"><strong aria-hidden="true">6.6.</strong> Aptos</a></li></ol></li><li class="chapter-item expanded "><a href="technical/relayer/overview.html"><strong aria-hidden="true">7.</strong> Relayers</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="technical/relayer/genericRelayer.html"><strong aria-hidden="true">7.1.</strong> Generic Relayers</a></li><li class="chapter-item expanded "><a href="technical/relayer/specializedRelayers.html"><strong aria-hidden="true">7.2.</strong> Specialized Relayers</a></li></ol></li><li class="chapter-item expanded "><a href="technical/typescript/overview.html"><strong aria-hidden="true">8.</strong> Wormhole Typescript SDK</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="technical/typescript/attestingToken.html"><strong aria-hidden="true">8.1.</strong> Token Registration</a></li><li class="chapter-item expanded "><a href="technical/typescript/tokenTransfer.html"><strong aria-hidden="true">8.2.</strong> Token Transfer Basics</a></li><li class="chapter-item expanded "><a href="technical/typescript/cross-ecosystem-transfer.html"><strong aria-hidden="true">8.3.</strong> Cross-Ecosystem Transfer</a></li><li class="chapter-item expanded "><a href="technical/typescript/using-relayer.html"><strong aria-hidden="true">8.4.</strong> Using Relayers</a></li><li class="spacer"></li></ol></li><li class="chapter-item expanded "><li class="part-title">Reference</li><li class="chapter-item expanded "><a href="reference/overview.html"><strong aria-hidden="true">9.</strong> Other Resources</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="reference/glossary.html"><strong aria-hidden="true">9.1.</strong> Glossary</a></li><li class="chapter-item expanded "><a href="reference/usefulLinks.html"><strong aria-hidden="true">9.2.</strong> Useful Links</a></li><li class="chapter-item expanded "><a href="reference/contracts.html"><strong aria-hidden="true">9.3.</strong> Deployed Contracts</a></li><li class="chapter-item expanded "><a href="reference/rpcnodes.html"><strong aria-hidden="true">9.4.</strong> RPC Nodes</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents"
aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)"
aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S"
aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Wormhole Development Book</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/wormhole-foundation/xdapp-book" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..."
aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function (link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<blockquote>
Warning: Some content may be outdated.
See <a href="https://docs.wormhole.com">docs.wormhole.com</a>
for up to date information
</blockquote>
<h1 id="introduction"><a class="header" href="#introduction">Introduction</a></h1>
<h2 id="welcome-to-the-wormhole-xdapp-book"><a class="header" href="#welcome-to-the-wormhole-xdapp-book">Welcome to the Wormhole xDapp Book!</a></h2>
<p>This guide aims to help you in your journey as a cross-chain developer by explaining the core concepts of Wormhole and xDapp development.</p>
<p>We'll start by outlining the most important aspects of Wormhole and the considerations which must be taken when developing an xDapp. After that, the second portion of the book helps you set up a development environment and get started writing actual code. By the end of this guide, you should be ready to build and deploy your first xDapp.</p>
<p>While this document doesn't assume you have experience with any particular technologies, a general understanding of blockchain development will help you get up to speed.</p>
<p>Ready to step <em>into the wormhole</em>?</p>
<hr />
<p>For additional resources, see the <strong>Reference</strong> section.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="an-introduction-to-xdapps"><a class="header" href="#an-introduction-to-xdapps">An Introduction to xDapps</a></h1>
<p>This chapter aims to give you a clear understanding of what xDapps are and why they're gaining traction in the blockchain development community.</p>
<p>To start, let's go over the current state of the decentralized finance (De-Fi) ecosystem, the emerging role of xDapps and the advantages they have over conventional Dapps.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="ecosystem-basics"><a class="header" href="#ecosystem-basics">Ecosystem Basics</a></h1>
<p>Since the launch of Bitcoin in 2009, the cryptocurrency and decentralized computing ecosystem has rapidly evolved and expanded. The ecosystem now includes hundreds of blockchains, often also referred to as <strong>Layer 1s</strong>.</p>
<p>Prior to 2015, blockchain transactions were limited in their capacities, typically being used to transfer funds from one user to another. This changed with the introduction of Ethereum and smart contracts. Smart contracts allowed developers to perform arbitrary on-chain computation as part of a blockchain transaction, opening the door for blockchain technology to become a global computing platform. These innovations laid the groundwork for the creation of <strong>Decentralized Applications</strong> or <strong>Dapps</strong>.</p>
<p>Now, a rich ecosystem of Dapps exists across an array of smart-contract-enabled blockchains. These Dapps provide a number of services across categories like:</p>
<ul>
<li>Decentralized Exchanges (DEXs)</li>
<li>Decentralized Autonomous Organizations (DAOs)</li>
<li>Borrow-Lend Platforms</li>
<li>Decentralized Games</li>
<li>NFT Protocols</li>
<li>Metaverse Platforms</li>
<li>and more</li>
</ul>
<p>However, Dapps are not without limitations, many of which are tied to their underlying blockchains.</p>
<p>Two notable limitations are that blockchains have no access to off-chain data and no mechanism to interact with other blockchains. These limitations have led to a fractured ecosystem, where each blockchain is closed off from the others by default. That means assets native to one chain are not accessible on another, and some services can't be leveraged on particular chains altogether.</p>
<p>Blockchain developers are now aiming to solve these interoperability problems to create a unified ecosystem. In this new cross-chain ecosystem, people can move beyond being users of individual blockchains and take advantage of Web3 on a broader scale.</p>
<hr />
<p>In the next section, we'll discuss the history and challenges of cross-chain interoperability, as well as introduce the role Wormhole plays in the future of this space.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="blockchain-interoperability"><a class="header" href="#blockchain-interoperability">Blockchain Interoperability</a></h1>
<p>Because blockchains are siloed by nature, individual cryptocurrencies being bound to their own chains has been a longtime limitation of blockchain technology. The first attempt at solving this problem was the creation of cryptocurrency exchanges like Coinbase and Binance. Today these are referred to as centralized exchanges (CEXs).</p>
<p>Centralized exchanges play an important role in cryptocurrency, but they are not a complete solution for blockchain interoperability for two primary reasons: (1) they're centralized, which is counterproductive to creating a decentralized platform, and (2) they deal only with tokens.</p>
<p>To solve the centralization problems with CEXs, decentralized exchanges (DEXs) were created. A DEX operates inside a smart contract runtime and can be as decentralized as the blockchain it runs on. Unfortunately, a DEX is only able to utilize the tokens on its native blockchain. In order to obtain a token which is not native to that chain, the DEX must be used in combination with a <strong>bridge</strong>.</p>
<p><strong>Bridges</strong> are complex and will be discussed at length in a later section. For now, we can categorize bridges as applications which 'lock' assets on one chain in exchange for <strong>wrapped assets</strong> on another chain. The wrapped assets can then be exchanged for the original 'backing' asset.</p>
<p>There are some other essential things you should know about bridges before going further:</p>
<ul>
<li>Bridges are capable of being decentralized in theory, but are often quite centralized in practice.</li>
<li>Bridges are currently the only way to hold a token on a chain other than its 'native' chain. If you're holding ETH on a chain other than Ethereum, it is, by definition, a wrapped token.</li>
<li>Bridges are all mutually incompatible with each other. Using multiple bridges just makes 'double wrapped' tokens.</li>
<li>If tokens have become double wrapped after traversing multiple bridges or blockchains, there can be a complex unwrapping process to get back to the original token.</li>
</ul>
<p>This explains how the ecosystem arrived at its current state -- CEXs are a solution to siloed blockchains, DEXs are a simple response to CEXs, and DEXs have created a demand for bridges. Each solution in this timeline is an ad-hoc patch to the previous problem, and the current landscape of fractured liquidity, double wrapped tokens, isolated user bases and wallet incompatibilities is the result.</p>
<p>Adding to this complexity are blockchains moving toward being general-purpose computing platforms. As such, interoperability will require data structures that are more complex than tokens and operations that are more sophisticated than transfers.</p>
<p>More ad-hoc solutions would only be short-term fixes for long-term problems, so it's critical to design new primitives and core infrastructure that will allow the next generation of decentralized applications to move beyond these lingering limitations.</p>
<p>This is why Wormhole exists. Wormhole proposes a new way of developing applications which leverages the strengths of each blockchain while mitigating the problems of the current ecosystem.</p>
<hr />
<p>Rethinking the next generation of decentralized applications means dethroning the token as the fundamental atomic unit of blockchains. We'll expand on this change in the next section.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="xdata-and-xassets"><a class="header" href="#xdata-and-xassets">xData and xAssets</a></h1>
<p>High on the wishlist of blockchain features is the ability to detach tokens from their native chains. It is a tremendous limitation that ETH only exists on Ethereum, MATIC only exists on Polygon and SOL only exists on Solana. It would be far more useful if those assets were able to move freely, independent of their native blockchains.</p>
<p>That thought underpins the idea of an <strong>xAsset</strong>, which could be considered a next-generation <em>wrapped token</em>. In a sense, xAssets exist on a layer <em>outside</em> of the blockchain ecosystem, and so are able to transact on a variety of blockchains. An xAsset is chain- and path- agnostic, so it retains fungibility regardless of where it travels. xAssets can also move fluidly around the blockchain ecosystem without ever becoming double-wrapped. </p>
<p>Now that we've established the idea of an xAsset, you might think they're an excellent atomic unit for solving interoperability challenges. However, xAssets are just one step short of the real solution. Let's take a step back: blockchains now process arbitrary data, and some of that data just happens to represent assets. The full solution then, is to create <strong>xData</strong>.</p>
<p>xData is akin to an xAsset in that it exists in its own layer independent of any blockchain, which makes xData accessible by <em>all</em> blockchains. The difference is that xData represents arbitrary data rather the token information represented by an xAsset.</p>
<p>Cross-chain interoperability then becomes a matter of creating, consuming and managing xData. Once blockchains have the ability to read and write data into a shared, global reservoir, application design can take on innovative new dimensions.</p>
<hr />
<p>Later in this document, we'll delve deeper into how Wormhole implements this <a href="dapps/../wormhole/3_coreLayerContracts.html">xData layer</a> (also referred to as the 'Core' layer of Wormhole), but for now let's talk about how xData can be used to create xDapps.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="what-is-an-xdapp"><a class="header" href="#what-is-an-xdapp">What is an xDapp?</a></h1>
<p>The term <strong>xDapp</strong> is short for &quot;Cross-Chain Decentralized Application&quot;. At first glance, this might give the impression that xDapps are simply Dapps that do cross-chain things. However, once you start building decentralized products designed to operate across a variety of blockchains and runtimes, it becomes clear that these applications are architected in a fundamentally different way than traditional Dapps.</p>
<p>xDapps have the capacity to perform all the operations of traditional Dapps, but they are also able to utilize xData. xData allows xDapp developers to build from a top-down, message-passing approach, rather than the bottom-up world of Dapp development. The Wormhole Core Layer implements xData, which acts as a shared repository of data across the entire Wormhole ecosystem.</p>
<p>Something we'll explore further in the upcoming xDapp Architecture chapter is the philosophy of <a href="dapps/./architecture/3_protocolDesign.html"><strong>Protocol-First Design</strong></a>. Protocol First Design is an approach to building decentralized applications where the first order of business is to lay out your application into a series of data structures, APIs and message payloads. Once you've laid out your application into a high-level protocol, the protocol acts as an agreement to which all components must adhere. From there, the smart contracts underlying the protocol can be considered an implementation detail.</p>
<p>If you're familiar with web2 development, you might notice that this philosophy is analogous to microservice architecture. This is no coincidence, as similar problems should expect to be solved by similar solutions, and the Wormhole Core Layer has a number of parallels to the OSI Network Model.</p>
<p>Thus, a more fitting depiction of xDapps might be to see them as <strong>Distributed Decentralized Applications</strong> with multiple, specialized components working in unison to deliver a smooth, unified user experience across a variety of layer 1 ecosystems.</p>
<hr />
<p>In the next section, we'll summarize the concrete advantages which xDapps built on Wormhole have over traditional Dapps today.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="advantages-of-xdapps"><a class="header" href="#advantages-of-xdapps">Advantages of xDapps</a></h1>
<p>Here are a few xDapp features that are making an impact across blockchain technologies:</p>
<ul>
<li>
<p><strong>Expanded User Base</strong> - Rather than being limited to the users of one blockchain, any user on any blockchain in the ecosystem can interact with an xDapp.</p>
</li>
<li>
<p><strong>Unified Liquidity</strong> - Liquidity fragmentation is a major problem in the current ecosystem. Unlike traditional tokens, xAssets can be pooled and moved anywhere.</p>
</li>
<li>
<p><strong>Decentralization</strong> - Cross-chain solutions today usually involve centralized exchanges or bridges. However, Wormhole has been designed to be decentralized from day one, and eventually totally trustless.</p>
</li>
<li>
<p><strong>Increased Performance</strong> - xDapps are able to utilize the strengths of each blockchain. With xDapps, expensive computations can be offloaded onto high-performance platforms, final settlement can take place on a preferred chain, and data can be stored wherever is cheapest.</p>
</li>
<li>
<p><strong>Broader Market Reach</strong> - Because xAssets move freely through the ecosystem, they can be listed on a variety of exchanges and custodied on any blockchain.</p>
</li>
<li>
<p><strong>Increased Extensibility and Composability</strong> - xDapps can utilize anything across the ecosystem, including other xDapps, expanding upon the composability and openness of smart contracts. </p>
</li>
<li>
<p><strong>Futureproofing</strong> - As new environments and protocols join the decentralized ecosystem, the connected nature of the Wormhole ecosystem allows existing protocols to expand and support them.</p>
</li>
</ul>
<p>Now that you have an understanding of what xDapps are and the advantages they offer, let's move on to the next chapter where we will delve into how Wormhole works and how it enables the creation of these next-generation protocols.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="wormhole"><a class="header" href="#wormhole">Wormhole</a></h1>
<p>In the previous chapter, we established concepts like xDapps, xData and xAssets. In this chapter, we'll focus on the inner workings of the Wormhole ecosystem and how they power these ideas.</p>
<p>By the end of this chapter, you'll have a clear understanding of what Wormhole is, what its key components are and how each component comes together to create a powerful, new cross-chain ecosystem.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="what-is-wormhole"><a class="header" href="#what-is-wormhole">What is Wormhole?</a></h1>
<p>Wormhole V1 was introduced in 2020 by Certus One and was initially conceived as a traditional token bridge between Ethereum and Solana. It served as the first bridge on Solana and was responsible for bootstrapping a large amount of the liquidity in the early Solana and Serum ecosystems.</p>
<p>However, despite its beginnings as a token bridge, Wormhole quickly grew beyond Solana and token transfers.</p>
<p>Wormhole v2 launched in August 2021 as a decentralized generic interoperability protocol for multiple blockchain ecosystems with initial support for Solana, Terra, Ethereum and Binance Smart Chain.</p>
<p>Over the past year, Wormhole has evolved to support an ever-growing list of blockchains across an unrivaled number of smart contract runtimes.</p>
<p>While Wormhole is a generic interoperability protocol, it is also an ecosystem and platform for developers to grow the decentralized computing space. Wormhole consists of multiple modular swap-in components that can be leveraged independently and supports an increasing number of composable applications built by numerous teams.</p>
<hr />
<p>In the next section, we'll go over the major components of the Wormhole ecosystem and how they fit together to enable the cross-chain functionality required to develop xDapps.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="architecture-overview"><a class="header" href="#architecture-overview">Architecture Overview</a></h1>
<p>Wormhole is a complex ecosystem with several noteworthy components. Before we go into each component in depth, let's talk about the names of the major pieces and how they fit together.</p>
<p><img src="wormhole/../diagrams/images/architecture.PNG" alt="Architecture Diagram" /></p>
<h3 id="on-chain-components"><a class="header" href="#on-chain-components">On-Chain Components</a></h3>
<ul>
<li>
<p><strong>xDapp Contracts</strong> - Contracts developed by xDapp developers. They receive transactions from the end user and then interact with other xDapp contracts and Wormhole Ecosystem Contracts in order to provide their service.</p>
</li>
<li>
<p><strong>Ecosystem Contracts</strong> - Contracts subject to Wormhole governance which live inside the Wormhole Ecosystem. Their job is to provide the feature suite of Wormhole to xDapp developers.</p>
<ul>
<li>
<p><strong>Core Contracts</strong> - Primary ecosystem contracts. These are the contracts which the Guardians observe and which fundamentally allow for cross-chain communication.</p>
</li>
<li>
<p><strong>xAsset Contracts</strong> - Contracts that allow normal tokens to be converted to xAssets and enable these xAssets to be bridged.</p>
</li>
<li>
<p><strong>Relay Contracts</strong> - <em>in development*</em> - Contracts that allow xDapps to send messages to a specific blockchain via the decentralized Generic Relayer network.</p>
</li>
<li>
<p><strong>Gas Oracle</strong> - <em>in development*</em> - Oracle for recommended fair gas prices across the ecosystem.</p>
</li>
<li>
<p><strong>Worm Router Contracts</strong> - <em>in development*</em> - Contracts that allow developers to make their Dapp an xDapp that users on any Wormhole supported chain can interact with purely through client-side code.</p>
</li>
</ul>
</li>
</ul>
<h3 id="off-chain-components"><a class="header" href="#off-chain-components">Off-Chain Components</a></h3>
<ul>
<li>
<p><strong>Guardian Network</strong> - Validators that exist in their own p2p network. Guardians observe the Core Contract on each supported chain and produce VAAs (signed messages) when those contracts receive an interaction.</p>
</li>
<li>
<p><strong>Guardian</strong> - One of 19 validators in the Guardian Network that contributes to the VAA multisig.</p>
</li>
<li>
<p><strong>Spy</strong> - Validators on the Guardian Network which are not part of the Guardian set. A spy can observe and forward network traffic, which helps scale up VAA distribution.</p>
</li>
<li>
<p><strong>VAAs</strong> - Verifiable Action Approvals (VAAs) are the key piece of data in the Wormhole ecosystem, containing the messages emitted by xDapps along with information such as what contract emitted the message. The VAAs are signed by the Guardians and need 13/19 signatures to be considered authentic.</p>
</li>
<li>
<p><strong>Specialized Relayers</strong> - Relayers that only handle VAAs for a specific protocol or xDapp. They can execute custom logic off-chain, which can reduce gas costs and increase cross-chain compatibility. Currently, xDapp developers are responsible for developing and hosting specialized relayers.</p>
</li>
<li>
<p><strong>Generic Relayers</strong> - <em>in development*</em> - A decentralized relayer network which delivers messages that are requested on-chain via the Wormhole Relay Contract.</p>
</li>
<li>
<p><strong>Wormchain</strong> - <em>in development*</em> - A purpose-built cosmos blockchain which aids the Guardian Network and allows for formal interaction with the Guardians.</p>
</li>
</ul>
<p>*Features listed as <em>in development</em> are not yet available.</p>
<hr />
<p>In the next section, we'll give an overview of how the Wormhole Guardian network creates VAAs along with a look at the key design considerations that underpin the network.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="core-contracts"><a class="header" href="#core-contracts">Core Contracts</a></h1>
<p>The Core Contracts are the mechanism by which all Wormhole messages are emitted. All xDapps either interact directly with the Core Contract or interact with another contract that does. There is one Core Contract on each blockchain in the ecosystem, and this is the contract which the Guardians are required to observe.</p>
<p>The Wormhole Core Contracts are one of the most pivotal pieces of the Wormhole ecosystem. They serve as a great place to start when learning about how data flows through the ecosystem.</p>
<p>In general, Core Contracts are simple and can be broken down to a <strong>sending</strong> and <strong>receiving</strong> side, which we'll define next.</p>
<h3 id="sending"><a class="header" href="#sending">Sending</a></h3>
<p>Below is the mechanism by which Wormhole messages (aka Verified Action Approval, VAA) are emitted:</p>
<pre><code>publishMessage(
int nonce,
byte[] payload,
int consistencyLevel
) returns int sequenceNumber
</code></pre>
<p>Let's break it down a bit:</p>
<ul>
<li>
<p><strong>payload</strong> - The content of the emitted message and an arbitrary byte array. It may be capped to a certain maximum length due to the constraints of individual blockchains.</p>
</li>
<li>
<p><strong>consistencyLevel</strong> - The level of finality to reach before emitting the Wormhole VAA. This is a defense against reorgs and rollbacks.</p>
</li>
<li>
<p><strong>nonce</strong> - An index number for the message that is used to produce Batch VAAs. How this is generated is elaborated in the <a href="wormhole/../technical/evm/coreLayer.html">CoreLayer</a> section.</p>
</li>
<li>
<p><strong>sequenceNumber</strong> - A unique index number for the message. When combined with the emitter contract address and emitter chain ID, the corresponding VAA can be retrieved from a guardian network node.</p>
</li>
</ul>
<p>The implementation strategy for publishMessage differs by chain, but the general strategy consists of the Core Contract posting the emitterAddress (the contract which called publishMessage), sequenceNumber, and consistencyLevel into the blockchain logs. Once the desired consistencyLevel has been reached and the message passes all of the Guardians' optional checks, the Guardian Network will produce the requested VAAs.</p>
<p>Currently there are no fees to publish a message (with the exception of publishing on Solana) but this is not guaranteed to always be the case in the future.</p>
<h3 id="receiving"><a class="header" href="#receiving">Receiving</a></h3>
<p>Below is the mechanism by which VAAs are received:</p>
<pre><code>parseAndVerifyVAA( byte[] VAA )
</code></pre>
<p>When passed a VAA, this function will either return the payload and associated metadata for the VAA or throw an exception. An exception should only ever throw if the VAA fails signature verification, indicating the VAA is invalid or inauthentic in some form.</p>
<hr />
<h2 id="consistency-levels"><a class="header" href="#consistency-levels">Consistency Levels</a></h2>
<p>The goal of Wormhole is to provide high confidence that, by default, only finalized messages are observed and attested. Different chains use different consensus mechanisms and so there are different finality assumptions with each one. Some advanced integrators may want to get messages <em>before</em> finality, which is where the <code>consistencyLevel</code> field offers chain-specific flexibility.</p>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Instant</th><th style="text-align: left">Safe</th><th style="text-align: left">Finalized</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left">0 (<code>confirmed</code>)</td><td style="text-align: left"></td><td style="text-align: left">1 (<code>finalized</code>)</td></tr>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left">200</td><td style="text-align: left">201 (<code>safe</code>)</td><td style="text-align: left">1 (<code>finalized</code>)</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">15 (recommended blocks)</td></tr>
<tr><td style="text-align: left">Polygon</td><td style="text-align: left">5</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (<a href="https://wiki.polygon.technology/docs/pos/heimdall/checkpoint/">checkpoint</a>)</td></tr>
<tr><td style="text-align: left">Avalanche (C-Chain)</td><td style="text-align: left">6</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (instant finality)</td></tr>
<tr><td style="text-align: left">Oasis (Emerald)</td><td style="text-align: left">7</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (instant finality)</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (instant finality)</td></tr>
<tr><td style="text-align: left">Karura</td><td style="text-align: left">11</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (safe mode)</td></tr>
<tr><td style="text-align: left">Acala</td><td style="text-align: left">12</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (safe mode)</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (instant finality)</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (instant finality)</td></tr>
<tr><td style="text-align: left">Moonbeam</td><td style="text-align: left">16</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (<a href="https://docs.moonbeam.network/builders/build/moonbeam-custom-api/#finality-rpc-endpoints"><code>moon_isBlockFinalized</code></a>)</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left">200</td><td style="text-align: left"></td><td style="text-align: left">1 (L1 block <code>finalized</code>)</td></tr>
<tr><td style="text-align: left">Optimism</td><td style="text-align: left">24</td><td style="text-align: left">200</td><td style="text-align: left">201 (<a href="https://community.optimism.io/docs/developers/bedrock/differences/#chain-reorganizations"><code>safe</code></a>)</td><td style="text-align: left">1 (L1 block <code>finalized</code>)</td></tr>
</tbody></table>
<hr />
<h2 id="multicasting"><a class="header" href="#multicasting">Multicasting</a></h2>
<p>Let's take a moment to point out that there is no destination address or chain in these functions.</p>
<p>VAAs simply attest that &quot;this contract on this chain said this thing.&quot; Therefore, VAAs are multicast by default and will be verified as authentic on any chain they are brought to.</p>
<p>This multicast-by-default model is integral to the design. Having this multicast capacity makes it easy to synchronize state across the entire ecosystem, because a single blockchain can make its data available to every chain in a single action with low latency. This reduces the complexity of the n^2 problems encountered by routing data to a large number of blockchains.</p>
<p>Use cases where the message has an intended recipient or is only meant to be consumed a single time must be handled in logic outside the Core Contract. There are standard practices for accomplishing these features later on in the code examples, and some ecosystem contracts (namely Token Bridge &amp; the Relaying contract) handle this on behalf of downstream consumers.</p>
<p>Lastly, because the VAA creation is separate from relaying, there is <em>no additional cost</em> to the multicast model when a single chain is being targeted. If the data isn't needed on a certain blockchain, don't relay it there, and it won't cost anything.</p>
<hr />
<p>In our next section, we'll dive into the technical specifications of the VAA.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="vaas-verified-action-approvals"><a class="header" href="#vaas-verified-action-approvals">VAAs (Verified Action Approvals)</a></h1>
<p>VAAs are the core messaging primitive in Wormhole. You can think of them as packets of xData that are emitted any time an xDapp contract interacts with the Core Contract.</p>
<p>The basic VAA has two components--a Header and a Body.</p>
<h2 id="header"><a class="header" href="#header">Header</a></h2>
<pre><code>byte version (VAA Version)
u32 guardian_set_index (Indicates which guardian set is signing)
u8 len_signatures (Number of signatures stored)
[][66]byte signatures (Collection of ecdsa signatures)
</code></pre>
<p>The Header is used by the Core Contract to determine the authenticity of the VAA, but can generally be ignored by other consumers.</p>
<h2 id="body"><a class="header" href="#body">Body</a></h2>
<pre><code>u32 timestamp (Timestamp of the block where the source transaction occurred)
u32 nonce (A grouping number)
u16 emitter_chain (Wormhole ChainId of emitter contract)
[32]byte emitter_address (Emitter contract address, in Wormhole format)
u64 sequence (Strictly increasing sequence, tied to emitter address &amp; chain)
u8 consistency_level (What finality level was reached before emitting this message)
[]byte payload (VAA message content)
</code></pre>
<p>The Body is the relevant information for consumers and is handed back from parseAndVerifyVAA. Because the emitterAddress is included as part of the Body, the developer is able to tell if this VAA originated from a trusted contract.</p>
<p>VAAs are uniquely indexed by their emitterChain, emittedAddress and sequence. They can be obtained by querying a node in the Guardian Network with this information.</p>
<p>Because baseline VAAs have no destination, they are effectively multicast. They will be verified as authentic by any Core Contract on any chain in the network. If a VAA has a specific destination, it is entirely the responsibility of relayers to complete that delivery appropriately.</p>
<h2 id="batch-vaas"><a class="header" href="#batch-vaas">Batch VAAs</a></h2>
<p>Certain blockchains support version 2 VAAs, also referred to as <strong>Batch VAAs</strong> which are designed to provide an easier paradigm for composability and better gas efficiency when multiple cross-chain actions are involved in a single transaction.</p>
<p>Batch VAAs are designed to be automatically generated for all messages that come from a single transaction.</p>
<p>In an extreme composability scenario or advanced integration, there may be some messages in a transaction that may not be relevant to one another. To control the create of additional batches, some messages can be created with the same <code>nonce</code> to <em>additionally</em> group them.</p>
<p>It is of note that Single VAAs will always be emitted for each message generated, regardless of it they are contained in a Batch VAA or not.</p>
<p>Go <a href="wormhole/../technical/evm/coreLayer.html">here</a> for a more detailed description of how Batch VAAs are generated.</p>
<p><em>Note: Batch VAAs are not currently live on mainnet, but will have initial support on all EVM chains when they launch.</em></p>
<blockquote>
<p>How to leverage Batch VAAs</p>
<p>Imagine a transaction generates three messages (A, B, C) that a consuming contract needs to know about.</p>
<p>If each message is independent of each other, the consuming contract can handle and validate each of these VAAs individually like [A], [B], [C].</p>
<p>If all of the messages are related to each other, the consuming contract can handle and validate the Batch VAA of the entire transaction that is automatically generated like [A, B, C].</p>
<p>If only two of the messages are related to each other, say A and C, the same <code>nonce</code> can be used for those two messages to generate an additional Batch VAA and the consuming contract can then handle and validate two sets of VAAs like [A, C] and [B].</p>
</blockquote>
<hr />
<p>In the next section, we'll give an overview of how the Wormhole Guardian network creates VAAs along with a look at the key design considerations that underpin the network.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="guardian-network"><a class="header" href="#guardian-network">Guardian Network</a></h1>
<p>The Guardian Network is designed to serve as Wormhole's oracle component, and the entire Wormhole ecosystem is founded on its technical underpinnings. It is the most critical element of the Wormhole ecosystem, and represents the single most important component to learn about if you want a deep understanding of Wormhole.</p>
<p>To understand not just <em>how</em> the Guardian Network works, but <em>why</em> it works the way it does, let's first take a step back and go over the key design considerations. To become the best-in-class interoperability platform, there were five critical features Wormhole needed to have:</p>
<ol>
<li><strong>Decentralization</strong> - Control of the network needs to be distributed amongst many parties.</li>
<li><strong>Modularity</strong> - Disparate parts of the ecosystem such as the oracle, relayer, applications, etc, should be kept as separate and modular as possible so they can be designed, modified and upgraded independently.</li>
<li><strong>Chain Agnosticism</strong> - Wormhole should be able to support not only EVM, but also chains like Solana, Algorand, Cosmos, and even platforms that haven't been created yet. It also should not have any one chain as a single point of failure.</li>
<li><strong>Scalability</strong> - Wormhole should be able to secure a large amount of value immediately and be able to handle the large transaction volume.</li>
<li><strong>Upgradeability</strong> - As the decentralized computing ecosystem evolves, Wormhole will need to be able to change the implementation of its existing modules without breaking integrators.</li>
</ol>
<p>Next, let's go into how Wormhole achieves these one at a time.</p>
<h2 id="decentralization"><a class="header" href="#decentralization">Decentralization</a></h2>
<p>Decentralization is the biggest concern. Previous interoperability solutions have largely been entirely centralized, and even newer solutions utilizing things like adversarial relayers still tend to have single points of failure or collusion thresholds as low as 1 or 2.</p>
<p>When designing a decentralized oracle network, the first option to consider is likely a Proof-of-Stake (PoS) system-but this turns out to be a suboptimal solution. PoS is designed for blockchain consensus in smart-contract enabled environments, so it's less suitable when the network is verifying the output of many blockchains and not supporting its own smart contracts. While it looks appealing from a decentralization perspective, the network security remains unclear, and it can make some other outlined goals more difficult to achieve. Let's explore other options.</p>
<p>The next option would be to rush straight for the finish line and use zero-knowledge proofs to secure the network. This would be a good solution from a decentralization perspective, as it's literally trustless. However, zero-knowledge proofs are still a nascent technology and it's not really feasible to verify them on-chain, especially on chains with limited computational environments. That means a form of multisig will be needed to secure the network.</p>
<p>If we step back and look at the current De-Fi landscape, most of the top blockchains are secured by the same handful of validator companies. Currently, there are a limited number of companies in the world with the skills and capital to run top-notch validator companies.</p>
<p>If a protocol could unite a large number of those validator companies into a purpose-built consensus mechanism that's optimized for chain interoperability, that design would likely be more performant and secure than a network bootstrapped by a tokenomics model. Assuming the validators would be on board, how many could Wormhole realistically utilize?</p>
<p>If Wormhole were to use threshold signatures, the answer would basically be 'as many as are willing to participate.' However, threshold signatures have spotty support across the blockchain world, meaning it would be difficult and expensive to verify the signatures, ultimately limiting scalability and chain agnosticism. Thus, a t-schnorr multisig presents itself as the best option: cheap and well supported, despite the fact that its verification costs increases linearly with the number of signatures included.</p>
<p>All these things considered, 19 seems to be the maximum number and a good tradeoff. If 2/3 of the signatures are needed for consensus, then 13 signatures need to be verified on-chain, which remains reasonable from a gas-cost perspective.</p>
<p>Rather than securing the network with tokenomics, it is better to initially secure the network by involving robust companies which are heavily invested in the success of De-Fi as a whole. The 19 Guardians are not anonymous or small--they are many of the largest and most widely-known validator companies in cryptocurrency. The current list of Guardians can be viewed <a href="https://wormhole.com/network/">here</a></p>
<p>That's how we end up with the network of 19 Guardians, each with an equal stake and joined in a purpose-built Proof of Authority consensus mechanism. As threshold signatures become better supported, the Guardian set can expand, and once ZKPs are ubiquitous, the Guardian Network will become fully trustless.</p>
<p>With our perspective on Decentralization laid out, the remaining elements fall into place.</p>
<h2 id="modularity"><a class="header" href="#modularity">Modularity</a></h2>
<p>The Guardian Network is robust and trustworthy by itself, so there's no need for components like the relayer to contribute to the security model. That makes Wormhole able to have simple components that are very good at the one thing they do. That way, Guardians only need to verify on-chain activity and produce VAAs while Relayers only need to interact with blockchains and deliver messages.</p>
<p>The signing scheme of the VAAs can be changed without affecting downstream users, and multiple relay mechanisms can exist independently. xAssets can be implemented purely at the application layer and xDapps can utilize whatever components suits them.</p>
<h2 id="chain-agnosticism"><a class="header" href="#chain-agnosticism">Chain Agnosticism</a></h2>
<p>Today, Wormhole supports a wider range of ecosystems than any other interoperability protocol because it uses simple tech (t-schnorr signatures), an adaptable, heterogeneous relayer model, and a robust validator network.</p>
<p>Wormhole can expand to new ecosystems as quickly as a Core Contract can be developed for the smart contract runtime. Relayers don't need to be factored into the security model--they just need to be able to upload messages to the blockchain. The Guardians are able to observe every transaction on every chain, without taking shortcuts.</p>
<h2 id="scalability"><a class="header" href="#scalability">Scalability</a></h2>
<p>Wormhole scales well, as demonstrated by its ability to handle huge TVL and transaction volume--even during tumultuous events.</p>
<p>The requirements for running a Guardian are relatively heavy, as they need to run a full node for every single blockchain in the ecosystem. This is another reason why a limited number of robust validator companies are beneficial for this design.</p>
<p>However, once all the full nodes are running, the actual computation and network overheads of the Guardian Network become lightweight. The performance of the blockchains themselves tends to be the bottleneck in Wormhole, rather than anything happening inside the Guardian Network.</p>
<h2 id="upgradability"><a class="header" href="#upgradability">Upgradability</a></h2>
<p>Over time, the Guardian Set can be expanded beyond 19 with the use of threshold signatures. A variety of relaying models will emerge, each with their own strengths and weaknesses. ZKPs can be used on chains where they are well supported. The xDapp ecosystem will grow, and xDapps will become increasingly intermingled with each other. There are very few APIs in Wormhole, and most items are implementation details from the perspective of an integrator. This creates a clear pathway towards a fully trustlessness interoperability layer which spans the entirety of decentralized computing.</p>
<hr />
<p>In the next section, we will talk about the role and responsibilities of relayers in the Wormhole ecosystem.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="relayers"><a class="header" href="#relayers">Relayers</a></h1>
<p>All simple cross-chain processes on Wormhole essentially boil down to a three-step process:</p>
<ol>
<li>Perform an action on chain A.</li>
<li>Retrieve the resulting VAA from the Guardian Network.</li>
<li>Perform an action on chain B using the VAA.</li>
</ol>
<p>Relayers play a key role in the final step of the process -- they can be thought of as the 'write' portion of interoperability, complementing the 'read' portion that Guardians provide.</p>
<p>The definition of a <em>Relayer</em> in the context of Wormhole is: Any process which delivers VAAs to a destination.</p>
<p>Unlike other interoperability protocols, Wormhole <strong>does not have a required relaying methodology</strong>.</p>
<p>In most designs there is a dedicated relaying mechanism which operates inside the protocol's trust boundaries. This means that the relayer either has an adversarial relationship to the oracle, or the relayer has trust assumptions and contributes to the protocol's security model. Relayers are usually a trusted party, are often also privileged, and developers are typically forced to use the relayer model built into the protocol.</p>
<p>In Wormhole, relayers are neither trusted nor privileged. This means relayers <strong>cannot jeopardize security, only liveness</strong>. Because Wormhole is designed to have a firm trust boundary at the level of the VAA, relayers have exactly the same capabilities as any regular, untrusted blockchain user.</p>
<p>From this perspective, relayers are just delivery trucks that deliver VAAs to their destination, and have no capacity to tamper with the delivery outcome. VAAs either get delivered or don't, which makes relayers analogous to the off-chain 'crank turners' of traditional Dapps.</p>
<p>As a result, Wormhole is able to facilitate a variety of heterogeneous relaying mechanisms, and the developer is able to choose whatever best suits their needs.</p>
<p>Next, we'll go over a few of the most common relaying strategies.</p>
<h2 id="client-side-relaying"><a class="header" href="#client-side-relaying">Client-side Relaying</a></h2>
<p>Client-side relaying relies on the user-facing frontend, like a webpage or a wallet, to perform all three steps of the cross-chain process.</p>
<p>There are two major benefits of this approach:</p>
<ul>
<li>Low cost. Users pay exactly the transaction fee for the second transaction.</li>
<li>No backend relaying infrastructure.</li>
</ul>
<p>However, client-side relaying also has two notable drawbacks:</p>
<ul>
<li>Users must sign all transactions required with their own wallet.</li>
<li>Users must have funds to pay the transaction fees on every chain involved.</li>
</ul>
<p>Overall, client-side relaying is a simple solution, but can make the user experience cumbersome. It's generally not recommended if your goal is a highly-polished user experience but can be useful to getting an MVP up and running.</p>
<h2 id="specialized-relayers"><a class="header" href="#specialized-relayers">Specialized Relayers</a></h2>
<p>Specialized relayers solve the UX problems of client-side relayers by adding a backend component which can handle steps 2 and 3 on behalf of the user.</p>
<p>In this model, relayers either listen directly to the Guardian Network via a spy (called <strong>Spy Relaying</strong>), or will simply provide a REST endpoint to accept a VAA which should be relayed (called <strong>REST Relaying</strong>). Once a relayer has the VAA, it simply performs any necessary off-chain calculations and submits the VAA to the required destination.</p>
<p>An important consideration when developing a specialized relayer is that the relayer is still considered untrusted. VAAs are public and can be submitted by anyone, so developers should not rely on off-chain relayers to perform any computation which is considered &quot;trusted.&quot; However, things that do not impact security like deterministic data transforms, waiting for gas prices to drop, or various forms of 'batching' can be very useful cost-reduction strategies.</p>
<p>Specialized Relayers have the following advantages:</p>
<pre><code>- They simplify user experience
- They allow off-chain calculations to be performed in the relayer, reducing gas costs
- They are generally easy to develop
</code></pre>
<p>However, they also have a couple notable downsides</p>
<pre><code>- They add a backend relaying component which is responsible for liveness
- They can complicate fee-modeling, as relayers are responsible for paying target chain fees.
</code></pre>
<p>Due to specialized relayers being such a common solution, an extensible relayer (called the plugin relayer) has been provided in the main Wormhole repository. The plugin relayer stands up most of the requisite infrastructure for relaying, so that you only need to implement the logic which is specific to your application.</p>
<p>If you plan to develop a specialized relayer, consider starting from the plugin relayer <a href="https://github.com/wormhole-foundation/wormhole/tree/main/relayer">found here</a>.</p>
<!--
TODO link to plugin relayer once it has been merged down
-->
<p>Because relayers are responsible for liveness, they become another dependency component (similar to the frontend, blockchain nodes, blockchains, third party APIs, etc.) for the xDapp. If the relayers are all down, your application has an outage.</p>
<p>To mitigate this, multiple relayers can be run in order to provide redundancy either by (1) the xDapp team or (2) a decentralized network based off economic incentives. <em>However, creating a robust model for decentralized relaying is generally application-specific and complex.</em></p>
<p>Overall, Specialized Relayers add a backend component that is responsible for liveness, but can simplify the user experience. It's generally recommended if your goal is a highly-polished user experience, and you want to have better control over message delivery.</p>
<h1 id="generic-relayers"><a class="header" href="#generic-relayers">Generic Relayers</a></h1>
<p><em>Note: this feature is not yet available in mainnet</em></p>
<p>Because relaying is such an integral component to xDapps, Wormhole has built a protocol which allows developers to utilize a decentralized network of untrusted relayers to deliver their messages, removing the specialized relayer as an infrastructure responsibility.</p>
<p>In order to utilize the generic relayer network, developers must request delivery from the Wormhole Relay Ecosystem Contract and must also implement a &quot;receiveRelay&quot; function in their contracts, which will be called by the relayer. Once a delivery has been requested, the VAA is guaranteed to be delivered within a certain time frame. The specifics of this vary by blockchain and smart contract runtime.</p>
<p>Generic relayers have the following benefits:</p>
<pre><code>- They feature simplified UX
- There are no relayer infrastructure requirements for the developer
</code></pre>
<p>And potential downsides:</p>
<pre><code>- They require all calculations to be done on-chain
- They sometimes have less gas efficiency
- They may not be supported on all chains
</code></pre>
<p>Overall, Generic Relayers simplify both the developer and user experience. They're a great choice if they cover all your use cases.</p>
<hr />
<p>In the next section, we'll discuss the xAsset module, which allows xAssets to be created and moved freely around the ecosystem.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="xasset-layer"><a class="header" href="#xasset-layer">xAsset Layer</a></h1>
<p>There is a set of ecosystem contracts that provision Wormhole's xAsset layer which allow tokens to be bridged around the Wormhole Ecosystem in a <strong>path-independent</strong> fashion, and are easily composable with other functions in the Wormhole ecosystem.</p>
<p>This section provides a high-level overview of how to interact with two smart contract modules that implement xAssets: (1) Token Bridge module and (2) NFT Bridge Module.</p>
<p>If you're looking to interact with the Token Bridge directly from a typescript client or backend, you should start with the <a href="https://www.npmjs.com/package/@certusone/wormhole-sdk">Wormhole Typescript SDK</a>.</p>
<h2 id="creating-xassets"><a class="header" href="#creating-xassets">Creating xAssets</a></h2>
<p>xAssets always have an <strong>origin chain</strong>. This is where the token is initially minted via the standard of that chain (ERC-20, SPL, etc for tokens; ERC-721, Metaplex, etc for NFTs).</p>
<p>xAssets are all <strong>fungible</strong> with each other. This means the Wormhole-wrapped asset can be exchanged for the original asset or a wrapped asset from other chains.</p>
<p><strong>Tokens</strong></p>
<p>To convert tokens into an xAsset, an <strong>attestation</strong> must first be created. To create an attestation, simply call the <strong>attest</strong> function on the token bridge contract of the origin chain.</p>
<pre><code>function attestToken(
address tokenAddress,
uint32 nonce)
returns (uint64 sequence)
</code></pre>
<p>The Guardian Network will then produce an <strong>attestation VAA</strong>, which can be retrieved using the sequence number returned by the attestToken function.</p>
<p>The attestation VAA must then be submitted to the <strong>createWrapped</strong> function of every other chain, referred to as <strong>foreign chains</strong> for this token.</p>
<pre><code>function createWrapped(
bytes memory encodedVm)
returns (address token)
</code></pre>
<p>Calling this function will deploy a new contract for the token on the foreign chain, creating a <strong>Wormhole-Wrapped Token</strong>. The wrapped token will use the same symbol as the origin asset, and will append (Wormhole) to the end of the name.</p>
<p><strong>NFTs</strong></p>
<p>NFTs do not need to be attested before they can be created into a xAsset.</p>
<h2 id="transferring-xassets"><a class="header" href="#transferring-xassets">Transferring xAssets</a></h2>
<p>Initiating xAsset transfers is a straightforward affair. Once the transfer is initiated, the Guardians will produce a transfer VAA when finality has been reached on the <strong>source chain</strong>. The VAA must then be relayed to the <strong>target chain</strong>.</p>
<p>All tokens managed by the Token Bridge are backed by the origin asset, allowing assets to be transferred in a path-independent fashion. Regardless of what chain the assets are passed to, a 'double-wrapped' asset will never be created for a single backing asset. Additionally, there are no liquidity limitations.</p>
<p><strong>Tokens</strong></p>
<pre><code> function transferTokens(
address token,
uint256 amount,
uint16 recipientChain,
bytes32 recipient,
uint256 arbiterFee,
uint32 nonce) returns (uint64 sequence)
</code></pre>
<p><strong>NFTs</strong></p>
<pre><code>function transferNFT(
address token,
uint256 tokenID,
uint16 recipientChain,
bytes32 recipient,
uint32 nonce) returns (uint64 sequence)
)
</code></pre>
<h2 id="contract-controlled-transfers"><a class="header" href="#contract-controlled-transfers">Contract-Controlled Transfers</a></h2>
<p>Basic transfers are intended to transfer xAssets from one wallet to another, whereas Contract Controlled Transfers (CCTs) are meant to transfer xAssets from one smart contract to another. If you're writing an xDapp, CCTs will likely be a large component.</p>
<p>CCTs allow xDapp contracts to easily perform simple xAsset transfers, but have two additional features:</p>
<ul>
<li>An arbitrary byte array can be appended to the transfer and can be used to easily pass additional information to the recipient contract.</li>
<li>The CCT VAA redeem can only be performed by the recipient contract, as opposed to basic transfers, which can be performed by any caller. This ensures that any additional operations which the contract wants to perform as part of the redeem transaction must be executed.</li>
</ul>
<hr />
<p>In the next section, we'll discuss Wormchain and some upcoming features it will enable.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="wormchain"><a class="header" href="#wormchain">Wormchain</a></h1>
<p>Wormchain is a purpose-built cosmos blockchain for the Wormhole ecosystem. It has two primary functions:</p>
<ol>
<li>Provide a public and auditable mechanism for users to interact with the Guardian Network.</li>
<li>Create a robust platform for on-chain infrastructure which would be infeasible to build elsewhere.</li>
</ol>
<p>Wormchain is built to provide things like:</p>
<pre><code>- Redundant security checks
- Governance
- Interactions with 'legacy' chains like Bitcoin
</code></pre>
<p>Wormchain is less relevant to xDapp developers than some other parts of the ecosystem, but it will become an increasingly important component as Wormhole matures and features are added. </p>
<hr />
<p>In the next section, we'll get into the key concepts that underpin xDapp design.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="security"><a class="header" href="#security">Security</a></h1>
<p>Let's take a moment to pause and spell out the most important security elements of Wormhole before proceeding.</p>
</br>
</br>
<p><strong>What are the Core Security Assumptions of Wormhole?</strong></p>
<ul>
<li>
<p>Wormhole's core security primitive is its signed messages (signed VAAs).</p>
</li>
<li>
<p>The Guardian network is currently secured by a collection of 19 of the world's top validator companies, <a href="https://wormhole.com/network/">listed here</a>.</p>
</li>
<li>
<p>Guardians produce signed state attestations (signed VAAs), when requested by a Core Contract integrator.</p>
</li>
<li>
<p>Every Guardian runs full nodes (rather than light nodes) of every blockchain in the Wormhole network. This means that if a blockchain suffers a consensus attack or hard fork, the blockchain will disconnect from the network, rather than potentially produce invalid signed VAAs.</p>
</li>
<li>
<p>Any Signed VAA can be verified as authentic by the Core Contract of any other chain.</p>
</li>
<li>
<p>Relayers are considered untrusted in the Wormhole ecosystem.</p>
</li>
</ul>
</br>
<blockquote>
<p>In summary:</p>
<ul>
<li>
<p><strong>Core integrators aren't exposed to risk from chains and contracts they don't integrate with</strong>.</p>
</li>
<li>
<p>By default, you only trust Wormhole's signing process and the Core Contracts of the chains you're on.</p>
</li>
<li>
<p>You can expand your contract and chain dependencies as you see fit.</p>
</li>
</ul>
</blockquote>
<hr />
</br>
<p>Core assumptions aside, there are many other factors which impact the real-world security of decentralized platforms. Here is more information on additional measures which have been put in place to ensure the security of Wormhole.</p>
</br>
<h2 id="audits"><a class="header" href="#audits">Audits</a></h2>
<p>Wormhole has been heavily audited, with <strong>16 third-party audits completed</strong> and a total of <strong>25+ started</strong>.</p>
<p>Wormhole has had audits performed by the following firms, and continues to seek more:</p>
<ul>
<li>Trail of Bits</li>
<li>Neodyme</li>
<li>Kudelski</li>
<li>OtterSec</li>
<li>Certik</li>
<li>Hacken</li>
<li>Zellic</li>
<li>Coinspect</li>
<li>Halborn</li>
</ul>
<p>The most up-to-date list of audits, as well as the final reports can be found <a href="https://github.com/wormhole-foundation/wormhole/blob/main/SECURITY.md#3rd-party-security-audits">here</a></p>
</br>
<h2 id="bug-bounties"><a class="header" href="#bug-bounties">Bug Bounties</a></h2>
<p>Wormhole has one of the largest bug bounty programs in all of software development, and has repeatedly shown commitment to engaging with the white hat community.</p>
<p>Wormhole hosts two bug bounty programs:</p>
<ul>
<li>An <a href="https://immunefi.com/">Immunefi</a> program,</li>
<li>As well as a <a href="https://wormhole.com/bounty/">self-hosted program</a></li>
</ul>
<p>Both platforms have a top payout of <strong>2.5 million dollars</strong>.</p>
<p>If you are interested in helping contribute to Wormhole security, please look at this section for <a href="https://github.com/wormhole-foundation/wormhole/blob/main/SECURITY.md#white-hat-hacking">Getting started as a White Hat</a>, and be sure to follow the <a href="https://github.com/wormhole-foundation/wormhole/blob/main/CONTRIBUTING.md">Wormhole Contributor Guidelines</a>.</p>
<p>For more information about submitting to the bug bounty programs, look <a href="https://wormhole.com/bounty/">here</a></p>
</br>
<h2 id="guardian-network-1"><a class="header" href="#guardian-network-1">Guardian Network</a></h2>
<p>Wormhole is an evolving platform. While the Guardian set currently comprises 19 validators, this is mostly a limitation of current blockchain technology. The aim of Wormhole is that the the Guardian Network will expand overtime, and that <strong>eventually Guardian signatures will be replaced entirely by state proofs</strong>. <a href="wormhole/./5_guardianNetwork.html">More info in this previous section</a>.</p>
</br>
<h2 id="governance"><a class="header" href="#governance">Governance</a></h2>
<p>Since the launch of Wormhole v2, all Wormhole governance actions and contract upgrades have been managed via Wormhole's <strong>on-chain governance system</strong>.</p>
<p>Guardians manually vote on governance proposals which originate inside the Guardian Network and are then submitted to ecosystem contracts. This means that <strong>governance actions are held to the same security standard</strong> as the rest of the system. A 2/3 supermajority of the Guardians are required to pass any Governance action.</p>
<p>Via governance, the Guardians are able to:</p>
<ul>
<li>Change the current Guardian set</li>
<li>Expand the Guardian set</li>
<li>Upgrade ecosystem contract implementations</li>
</ul>
<p>The Governance system is fully open source in the core repository. Here are some relevant contracts:</p>
<ul>
<li><a href="https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/bridge/BridgeGovernance.sol">Ethereum Core Governance</a></li>
<li><a href="https://github.com/wormhole-foundation/wormhole/blob/main/solana/bridge/program/src/api/governance.rs">Solana Core Governance API</a></li>
</ul>
</br>
<h2 id="monitoring"><a class="header" href="#monitoring">Monitoring</a></h2>
<p>A key element of Wormhole's defense-in-depth strategy is that each Guardian is a highly-competent validator company with their own in-house processes for running, monitoring, and securing blockchain operations. This heterogeneous approach to monitoring increases the likelihood that fraudulent activity is detected and reduces the number of single failure points in the system.</p>
<p>Guardians are not just running Wormhole validators, they're running validators for <strong>every blockchain inside of Wormhole as well</strong>, which allows them to perform monitoring <strong>holistically across decentralized computing</strong>, rather than just at a few single points.</p>
<p>Guardians Monitor:</p>
<ul>
<li>
<p>Block Production &amp; Consensus of each blockchain. If a blockchain's consensus is violated it disconnects from the network until the Guardians resolve the issue.</p>
</li>
<li>
<p>Smart Contract level data. Via processes like the Governor, Guardians constantly monitor the circulating supply and token movements across all supported blockchains.</p>
</li>
<li>
<p>Guardian Level activity. The Guardian Network functions as an autonomous decentralized computing network, complete with its own blockchain (Wormchain).</p>
</li>
</ul>
</br>
<h2 id="wormchain--asset-layer-protections"><a class="header" href="#wormchain--asset-layer-protections">Wormchain &amp; Asset Layer Protections</a></h2>
<p>One of the most powerful aspects of the Wormhole ecosystem is that Guardians effectively have <strong>the entire state DeFi available to them</strong>.</p>
<p>Wormchain is a Cosmos based blockchain which runs internally to the Guardian network, whereby the Guardians can effectively execute smart contracts against the current state of all blockchains, rather than just one blockchain.</p>
<p>This enables two additional protections for the Wormhole Asset Layer in addition to the core assumptions:</p>
<ul>
<li><strong>Governor:</strong> The Governor tracks inflows and outflows of all blockchains and delays suspicious transfers which may be indicative of a exploit. <a href="https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0007_governor.md">More Info</a></li>
<li><strong>Global Accountant:</strong> The accountant tracks the total circulating supply of all Wormhole assets across all chains and prevents any blockchain from bridging assets which would violate the supply invariant.</li>
</ul>
</br>
<h2 id="always-open-source"><a class="header" href="#always-open-source">Always Open Source</a></h2>
<p>Lastly, Wormhole builds in the open and is always open source.</p>
<ul>
<li>
<p><a href="https://github.com/wormhole-foundation/wormhole">Wormhole Core Repository</a></p>
</li>
<li>
<p><a href="https://github.com/wormhole-foundation">Wormhole Foundation Github Organization</a></p>
</li>
<li>
<p><a href="wormhole/../reference/contracts.html">Wormhole Contract Deployments</a></p>
</li>
</ul>
</br>
<h2 id="learn-more"><a class="header" href="#learn-more">Learn More</a></h2>
<ul>
<li>The <a href="https://github.com/wormhole-foundation/wormhole/blob/main/SECURITY.md">SECURITY.md</a> from the official repository has the latest security policies and updates.</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="xdapp-design"><a class="header" href="#xdapp-design">xDapp Design</a></h1>
<p>Now that we've established the major concepts and components underlying xDapps, let's dive into the process of designing one. This chapter will guide you through the considerations you should make before developing an xDapp, including topics like network topology, protocol design and more.</p>
<p>By the end of this chapter, you will have all the tools you need to lay out a design for your xDapp and start building.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="key-considerations"><a class="header" href="#key-considerations">Key Considerations</a></h1>
<p>Before we get started, we should outline the key considerations that will shape your xDapp. Below, we'll show how each of the decisions you make about these key considerations can impact the structure of your application as a whole.</p>
<h3 id="why"><a class="header" href="#why">Why?</a></h3>
<p>The reason you're building an xDapp is the foremost consideration. Think about the <a href="dapps/architecture/../5_advantages.html">advantages</a> of cross-chain development -- which of these are most important to you? Are you building a brand new application and you want the widest reach? Are you trying to increase the performance of an existing Dapp? Are you interested in composing on top of protocols that only exist in certain ecosystems? Determining your key priorities will help you make better technical decisions and tradeoffs when designing your xDapp.</p>
<h3 id="target-ecosystems--languages"><a class="header" href="#target-ecosystems--languages">Target Ecosystems &amp; Languages</a></h3>
<p>Which blockchains do you intend to support? Because different blockchains utilize different virtual machines, supporting more blockchains often requires writing smart contracts in more than one language.</p>
<h3 id="data-flows"><a class="header" href="#data-flows">Data Flows</a></h3>
<p>Think about where your data originates and where it needs to go. Does all your data come from user-initiated transactions? Do you have governance messages that need to be emitted from a central governance contract? Do you have automated actions which need to happen periodically to synchronize your data? </p>
<h3 id="liquidity--tokens"><a class="header" href="#liquidity--tokens">Liquidity &amp; Tokens</a></h3>
<p>Not all xDapps deal with tokens, but many do. If your app is centered around tokens, you'll have to decide which tokens will be utilized, where liquidity is aggregated (or fractured), and how this liquidity can be best utilized across your application.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="ecosystems"><a class="header" href="#ecosystems">Ecosystems</a></h1>
<p>At present, there are 6 ecosystems supported by Wormhole, though the number of supported ecosystems is always growing.</p>
<h3 id="evm"><a class="header" href="#evm">EVM</a></h3>
<p>EVM is the most popular ecosystem, and most xDapps will have some support for this platform. These contracts are written in Solidity -- a 'jack of all trades' style of computation environment. A common strategy for xDapps is to develop one single contract in Solidity, and then deploy that contract to all the supported EVM blockchains.</p>
<p>Example chains:</p>
<pre><code>- Ethereum
- Polygon
- BNB Chain
- Avalanche (C Chain)
- Aurora (Near Network)
- Karura (Polkadot Network)
- Acala (Polkadot Network)
- Celo
- Fantom
- Oasis (Emerald)
</code></pre>
<h3 id="solana"><a class="header" href="#solana">Solana</a></h3>
<p>Solana is characterized by its high transaction throughput, increased computation power and cheap data storage when compared to EVM environments. These contracts are written in Rust.</p>
<h3 id="cosmos"><a class="header" href="#cosmos">Cosmos</a></h3>
<p>Cosmos is a network of blockchains that share a common ecosystem. Cosmos is a general purpose environment, but excels in certain areas like application-specific blockchains and the use of Cosmos-wide standards via its sdk 'modules.' It uses CosmWasm for its smart contract runtime, which is based in Rust.</p>
<h3 id="algorand"><a class="header" href="#algorand">Algorand</a></h3>
<p>Algorand is a leading blockchain on the state proof front and repesents the bleeding edge of trustlessness. These contracts are written in Python.</p>
<h3 id="aptos"><a class="header" href="#aptos">Aptos</a></h3>
<p>Aptos is characterized by its optimisitic approach to computation parallelization to bring increased performance. These contracts are written in Move.</p>
<h3 id="near"><a class="header" href="#near">NEAR</a></h3>
<p>NEAR is characterized by its sharding technology that may allow for greater transaction capacity and security. These contracts are written in Rust.</p>
<h3 id="read-only-chains"><a class="header" href="#read-only-chains">Read-Only Chains</a></h3>
<p>Some chains in the Wormhole ecosystem are 'Read-Only.' These chains are able to verify messages emitted from other chains in the network, but are not able to emit messages themselves. For information about these chains, check the <a href="dapps/architecture/../../reference/contracts.html">contracts page</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="protocol-design"><a class="header" href="#protocol-design">Protocol Design</a></h1>
<p>The key feature of Wormhole is bringing message passing to the world of blockchain, so it's worthwhile to take some inspiration from other areas of software development that are based on similar principles.</p>
<p>Much of the traditional web stack is based on distributed systems that rely on message passing to create interfaces and boundaries for disparate systems to work together. We can think of xDapps as web3 distributed systems founded on similar paradigms.</p>
<h2 id="protocol-first-design"><a class="header" href="#protocol-first-design">Protocol First Design</a></h2>
<p>Protocol first design is a design philosophy where you initially lay out your data types, message formats and supported operations into a well-defined protocol. This creates a solid protocol layer which can serve as the foundation for your application. This way, the code instantiating that protocol can be treated as an implementation detail when reasoning about the protocol itself.</p>
<p>At this stage in the design, you should also consider the incentive structures surrounding your protocol. What is the incentive for each party to engage? Are there economic attack vectors in your application which might jeopardize its security? Do certain market conditions result in perverse outcomes? </p>
<p>Depending on your goals, this stage of the process can be as simple as stating &quot;people will want my NFT&quot; or as difficult as designing an entire ecosystem with multiple competing interested parties. </p>
<p>Once you have a clear idea of your core product, incentives and users, you can begin to lay out your data model. From there, you can define your message types and operations.</p>
<h2 id="common-strategies-and-conventions"><a class="header" href="#common-strategies-and-conventions">Common Strategies and Conventions</a></h2>
<h3 id="address-space"><a class="header" href="#address-space">Address Space</a></h3>
<p>Because there are many different formats for addresses across the different supported blockchains, a compatibility format is necessary. Wormhole uses its own address format (generally referred to as Wormhole format) in order to solve this issue. These addresses correspond 1 to 1 with native addresses on each chain.</p>
<p>A Wormhole address is a tuple containing the 2 byte Wormhole chain ID and a 32 byte shim address, totalling 34 bytes.</p>
<p>Because EVM addresses are only 20 bytes in length, to convert this to a Wormhole address, the address is left-padded with zeros until it's length 32. To transmit as this as a single item, the Wormhole chain ID is usually appended to the front, resulting in a 34 byte address.</p>
<p>When dealing with addresses inside your messages, it's recommended to always convert to Wormhole format and transmit in that format. You will regularly encounter addresses in the Wormhole format when interacting with other parts of the ecosystem, and adopting the format in your protocol will enhance your forward compatibility as you add more chains.</p>
<h3 id="trusted-contract-network"><a class="header" href="#trusted-contract-network">Trusted Contract Network</a></h3>
<p>Typically, the first check performed when receiving a message is to validate that it originates from a trusted source. If your protocol has smart contracts deployed to multiple chains, it will be important for your contracts to know which other contracts are 'in network' for your protocol. </p>
<p>Generally, this list of trusted contracts is stored in the state of each contract individually. Updating the trusted contracts is tied into the governance mechanism of the protocol.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="topology"><a class="header" href="#topology">Topology</a></h1>
<p>Topology describes how data flows through your application and defines the responsibilities of each component. In terms of overall xDapp topology, the primary decision is determining where your smart contracts will live and the responsibilities each contract will hold.</p>
<h2 id="ultra-light-clients"><a class="header" href="#ultra-light-clients">Ultra-light Clients</a></h2>
<p><img src="dapps/architecture/../../diagrams/images/ultralight_2.png" alt="Ultra-light client" title="Ultra Light Clients" /></p>
<p>Ultra-light Clients are often the best option when designing an MVP for your xDapp. The defining feature of an Ultra-light Client is that you are able to support users from every chain in the Wormhole ecosystem while <strong>only having smart contracts on a single chain (!!!)</strong>.</p>
<p>xDapps with this structure work by having a hub chain that all application contract logic is deployed to and entrypoints which receive and send Wormhole messages to remote chains. The network of Wormhole contracts deployed on other chains across the ecosystem provide the rest of the heavy lifting to send messages across chains.</p>
<p>You can think of the data flow across a xDapp with a Ultra-light Client as follows:</p>
<ol>
<li>The end user's wallet interacts with Wormhole contracts on remote chain.</li>
<li>The Wormhole contracts on remote chain generate a VAA which is received by your xDapp contract on the hub chain.</li>
<li>Your xDapp contract on the hub chain performs all necessary operations.</li>
<li>Your xDapp contract interacts with Wormhole contracts on the hub chain.</li>
<li>The Wormhole contracts on hub chain generate a VAA which is sent back to the end user's wallet on the remote chain.</li>
</ol>
<p><strong><em>Advantages:</em></strong></p>
<ul>
<li>Very little added smart contract risk.</li>
<li>Simple to develop.</li>
<li>Easiest way to get heterogeneous ecosystem support.</li>
</ul>
<p><strong><em>Disadvantages:</em></strong></p>
<ul>
<li>Latency: Transactions incur latencies associated with bridging into and out of both the remote and hub chain.</li>
<li>Transaction Fees: There are always a grand total of three transactions. Two on the remote chain, and one on the hub chain.</li>
<li>Use cases: There is no place to perform trusted computation on the remote chain, so some use cases are more difficult to implement (or potentially not possible).</li>
</ul>
<h2 id="hub-and-spoke"><a class="header" href="#hub-and-spoke">Hub-and-Spoke</a></h2>
<p><img src="dapps/architecture/../../diagrams/images/hub_and_spoke.PNG" alt="Hub and Spoke" title="Hub and Spoke" /></p>
<p>Hub and Spoke models can somewhat be thought of as the natural evolution of the ultra-light client. There is still a hub contract which handles all transactions, but there is now also a contract deployed to all the remote chains that is capable of performing some trusted computation.</p>
<p>You can think of the data flow across a Hub-and-Spoke system as follows:</p>
<ol>
<li>The end user's wallet interacts with your (lightweight) remote contracts.</li>
<li>The remote contracts perform any necessary trusted computation.</li>
<li>The remote contracts use Wormhole to generate a VAA, which is consumed by the hub contract.</li>
<li>The hub contract performs all necessary operations.</li>
<li>The hub contract uses Wormhole to send a message back to the original remote contract.</li>
<li>The remote contract takes whatever action is needed to finish the process.</li>
</ol>
<p><strong><em>Advantages:</em></strong></p>
<ul>
<li>Remote contracts are lightweight and don't carry large amounts of risk.</li>
<li>Can perform trusted checks on the remote chain. (Such as validating wallet balance, or any other piece of blockchain state)</li>
</ul>
<p><strong><em>Disadvantages:</em></strong></p>
<ul>
<li>Latency (same as ultra-light clients)</li>
<li>Transaction Fees</li>
<li>Managing multiple contracts</li>
</ul>
<h2 id="mesh"><a class="header" href="#mesh">Mesh</a></h2>
<p><img src="dapps/architecture/../../diagrams/images/mesh.PNG" alt="Mesh" title="Mesh" /></p>
<p>Mesh topologies can somewhat be thought of as the next evolution of the Hub and Spoke model. There are now contracts capable of handling all transactions for an application are deployed on all supported chains. Each contract can be thought of as a peer of other contracts in the trusted network and can act autonomously.</p>
<p>This is historically the most popular methodology for going cross-chain. It's very attractive in ecosystems like EVM or Cosmos, where a single smart contract can simply be deployed across many different blockchains.</p>
<p><strong><em>Advantages:</em></strong></p>
<ul>
<li>Latency: Users can often perform their operation without waiting for other chains.</li>
<li>Transaction Fees: Does not stack the transaction fees of multiple chains.</li>
</ul>
<p><strong><em>Disadvantages:</em></strong></p>
<ul>
<li>Complexity: there are now quite a few contracts to manage, especially if they are implemented multiple times across different VMs.</li>
<li>Data desync: because each blockchain acts independently, each chain will have independent state. This can open up unwanted arbitrage opportunities and other discrepancies.</li>
<li>Race conditions: In cases where an event is supposed to propagate through the entire system at a fixed time (for example, when closing a governance vote), it can be difficult to synchronize all the blockchains.</li>
</ul>
<h2 id="distributed"><a class="header" href="#distributed">Distributed</a></h2>
<p><img src="dapps/architecture/../../diagrams/images/distributed.PNG" alt="Distributed" title="Distributed" /></p>
<p>Distributed topologies can somewhat be thought of as the next evolution of the Mesh model. Instead of contracts that are capable of handling all transactions for an application on all supported chain, applications are broken up into separate responsibilities (i.e. data storage, user interaction, asset custody, governance) and deployed to different blockchains.</p>
<p>Advantages:</p>
<ul>
<li>Power: utilize each blockchain for whatever is most optimal.</li>
</ul>
<p>Disadvantages:</p>
<ul>
<li>Complexity: requires multiple specialized smart contracts, and potentially additional on-chain processes.</li>
</ul>
<h2 id="mix--match"><a class="header" href="#mix--match">Mix &amp; Match</a></h2>
<p>Different use cases have different optimal topologies, and it's possible to use different topologies for different workflows in your application. This means you should not feel 'locked in' to a single topology, and should instead consider designing each workflow independently. For example, governance workflows are generally best implemented using a Hub and Spoke topology, even if the rest of the application uses a Mesh architecture. As such, your contracts will likely evolve over time as your xDapp evolves and adds additional workflows.</p>
<p>You can also progress through different topologies. A common strategy is to start off with an ultra-light client, move to a hub and spoke configuration, and then add optimizations and specialties to contracts as the need arises.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="relayers-1"><a class="header" href="#relayers-1">Relayers</a></h1>
<p>In Chapter 2, we discussed the <a href="dapps/architecture/../../wormhole/6_relayers.html">general concepts associated with relayers in the Wormhole ecosystem</a>. In this section, we'll elaborate on the considerations that should be accounted for when using relayers in your xDapp.</p>
<h2 id="fundamentals"><a class="header" href="#fundamentals">Fundamentals</a></h2>
<p>It's important to remember that relayers are untrusted. This means you don't have to trust them--but it also means you can't trust them. This is true of both generic and specialized relayers.</p>
<p>Let's dive into a little more detail about why relayers are untrusted and what this means for you.</p>
<p>A few key properties of VAAs:</p>
<ul>
<li>They are publicly emitted from the Guardian Network.</li>
<li>They need to be signed by the Guardian Network to be considered authentic.</li>
<li>They can be verified as authentic by anyone and any Wormhole Core Contract.</li>
</ul>
<p>Relayers are untrusted as an inherent consequence of these properties. Anyone can pick up a VAA and deliver it anywhere, but no one can alter the content of the VAA without invalidating the signatures.</p>
<p>So, when writing your contracts, it's incredibly important to only trust information which is either inside your contract or inside a VAA. If you trust information provided by a relayer, you are opening yourself up to untrusted input attacks.</p>
<p>The easiest and most secure way to interact with relayers then is to only accept the VAA as input. If the relayer can't provide any additional args, then there's no way for them to provide untrusted input.</p>
<p>More advanced strategies involve having the relayer perform untrusted off-chain computation which is passed into the destination contract. These strategies can optimize gas costs, but can also create attack vectors if not used correctly.</p>
<p>With this in mind, relayer design becomes a matter of structuring the messages in your protocol such that there is a single, deterministic way that they can be processed. In a well designed protocol, relayers have a 'correct' implementation.</p>
<p>Relayers are conceptually quite similar to &quot;crank turner&quot; processes used elsewhere in blockchain, in that there is only a single action which can be taken (pulling the crank), and their sole responsibility is to initiate this action and pay for the costs.</p>
<hr />
<h2 id="generic-relayers-1"><a class="header" href="#generic-relayers-1">Generic Relayers</a></h2>
<p>Generic relayers are a decentralized relayer network which can deliver arbitrary VAAs as long as the recipient contract conforms with the generic relayer API.</p>
<p><strong><em>Advantages:</em></strong></p>
<ul>
<li>Generic relayers are done purely on-chain, so there's no need to develop, host or maintain relayers.</li>
</ul>
<p><strong><em>Disadvantages:</em></strong></p>
<ul>
<li>There's less room for optimization via features like conditional delivery, batching, off-chain calculations, etc.</li>
</ul>
<hr />
<h2 id="specialized-relayers-1"><a class="header" href="#specialized-relayers-1">Specialized Relayers</a></h2>
<p>Specialized Relayers are relayers that are purpose-built to relay messages for a certain application. In the future, there may be ways to customize generic relayers such that they will gain the advantages of today's specialized relayers.</p>
<p><strong><em>Advantages:</em></strong></p>
<ul>
<li>Specialized relayers can perform off-chain untrusted computation.</li>
<li>They are highly customizeable and can perform batching, conditional delivery, multi-chain deliveries, etc.</li>
<li>Can home-roll an incentive structure.</li>
</ul>
<p><strong><em>Disadvantages</em></strong></p>
<ul>
<li>Requires development work and relayer hosting.</li>
</ul>
<hr />
<h3 id="relayer-incentives"><a class="header" href="#relayer-incentives">Relayer Incentives</a></h3>
<p>Relayers have to cover the costs of executing the downstream transactions resulting from the original 'source' transaction. Unless the relayers are running at a loss, there must be a mechanism for reimbursing the relayer in exchange for message delivery.</p>
<p>There are many strategies for reimbursement, and the 'best' strategy for an application is often dependent on the specifics of that application. However, a few of the most common strategies are:</p>
<ul>
<li>Pay the relayer with a potion of the tokens being sent cross-chain.</li>
<li>Collect a safe amount of gas money from the end user prior to performing any actions.</li>
<li>'Lazy' relaying, where relaying might only be profitable in certain, potentially rare, market conditions.</li>
</ul>
<p>Generic relayers have an incentive model built in to the network, so you do not need to design an incentive structure when using them.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="environment-setup"><a class="header" href="#environment-setup">Environment Setup</a></h1>
<p>The bare minimum Wormhole environment is just a blockchain linked up to a Guardian node. There are quite a few ways of accomplishing this, and if you're just looking to get your feet wet, you should try whichever sounds easiest.</p>
<p>However, as you get deeper into cross-chain development, you'll likely find yourself with a growing number of components, dependencies, and teammates in the picture making your development environment more complex. Here are some of the considerations you should have in mind when choosing a development environment which will be well suited for your xDapp.</p>
<h3 id="what-components-do-i-need-now-what-components-will-i-need-in-the-future"><a class="header" href="#what-components-do-i-need-now-what-components-will-i-need-in-the-future">What components do I need now? What components will I need in the future?</a></h3>
<ul>
<li>You may be able to get a lot done with just an EVM chain and a Guardian. However, as your application gets more sophisticated, components like relayers, frontends, automated tests, databases, explorers, and other blockchains are likely to get added into the environment.</li>
</ul>
<h3 id="what-are-my-dependencies"><a class="header" href="#what-are-my-dependencies">What are my dependencies?</a></h3>
<ul>
<li>
<p>If your smart contracts have no dependencies, it may be possible for you to develop in a vacuum.</p>
</li>
<li>
<p>If your smart contracts does have dependencies, there are several options that range from deploying your dependencies in <a href="technical/env/./tilt.html">Tilt</a> to simulating an existing testnet/mainnet in <a href="https://github.com/foundry-rs/foundry">Foundry</a> to working directly in <a href="technical/env/./testnet.html">testnet</a> alongside other teams.</p>
</li>
</ul>
<h3 id="how-am-i-going-to-collaborate"><a class="header" href="#how-am-i-going-to-collaborate">How am I going to collaborate?</a></h3>
<ul>
<li>You should consider how your teammates or collaborators are going to work in this environment from the start. There are some basic considerations like &quot;how will they access it&quot;, but also some subtler points such as ensuring that contracts will deploy deterministically and that automated tests can be trusted to run reliably. The two paths to accomplish this are to use a public environment (testnet), or to ensure the local environment is well controlled (like tilt).</li>
</ul>
<hr />
<p>In the following sections we'll describe three different development environments that are available -- Tilt, Wormhole Local Validator, and Testnet -- in more details as well as some additional tools that should be helpful in your xDapp development.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="tilt-devnet"><a class="header" href="#tilt-devnet">Tilt (Devnet)</a></h1>
<h2 id="what-is-tilt"><a class="header" href="#what-is-tilt">What is Tilt?</a></h2>
<p><a href="https://tilt.dev/">Tilt</a> is part of the official Docker ecosystem. It's a tool which allows developers to easily configure a Kubernetes environment for development.</p>
<p>However, in the context of Wormhole, 'Tilt' refers to the development environment used by the <a href="https://github.com/wormhole-foundation/wormhole">Wormhole Core Repository</a>. This environment stands up docker images for all the tools necessary to build across multiple blockchains, including:</p>
<ul>
<li>All the Wormhole supported blockchains / ecosystems</li>
<li>A Guardian Node</li>
<li>Relayers</li>
<li>Databases, Redis</li>
<li>Utility frontends</li>
</ul>
<p>The 'Tilt' environment is meant to provide an entire cross-chain development stack right out of the box.</p>
<p><em>Note: Tilt is often referred to as 'Devnet' in the Wormhole ecosystem so any information that is labelled as 'devnet' also applies to Tilt.</em></p>
<h3 id="is-tilt-right-for-you"><a class="header" href="#is-tilt-right-for-you">Is Tilt Right for You?</a></h3>
<p>Tilt is generally a good starting point for most developers. Here's a succinct list of the pros and cons of the environment, so you can decide if it's the right fit for you.</p>
<p><strong>Pros</strong></p>
<ul>
<li>Out-of-the-box support for the many components needed to develop across the heterogeneous blockchain spaces.</li>
<li>Consistent development environment, where contracts deploy deterministically and everything is already linked up.</li>
<li>Ability to easily enable/disable components as needed.</li>
<li>Regularly updated as new components join the Wormhole ecosystem.</li>
</ul>
<p><strong>Cons</strong></p>
<ul>
<li>Relatively high system requirements but this can be mitigated by disabling components.</li>
<li>Most blockchains are 'fresh' and have no contracts by default. Thus, if your contracts have any dependencies, you may have to deploy them yourself or alter the default tilt configuration.</li>
<li>Spin-up and rebuild times can be slow which can result in a slow workflow.</li>
</ul>
<h2 id="setting-up-tilt"><a class="header" href="#setting-up-tilt">Setting up Tilt</a></h2>
<p>Tilt functions best in a UNIX-style environment. For Windows users, a WSL environment is recommended.</p>
<p>In order to run the Tilt environment, make sure you have <a href="https://docs.tilt.dev/install.html">Tilt</a> and <a href="https://go.dev/doc/install">Go</a> installed.</p>
<p>Once you've installed these two dependencies, just clone the Wormhole Core Repository and start Tilt.</p>
<pre><code>git clone --branch main https://github.com/wormhole-foundation/wormhole.git
cd wormhole
tilt up
</code></pre>
<p>Be sure to check out the <a href="https://github.com/wormhole-foundation/wormhole/blob/main/Tiltfile"><strong>Tiltfile</strong></a>, which has much of the configuration and arguments for the development environment. It's relatively straightforward to enable and disable components. </p>
<p>For example, you can disable blockchains by setting them to false at startup</p>
<pre><code>tilt up -- --algorand=false --near=false --solana=false terra_classic=false terra2=false
</code></pre>
<h2 id="using-tilt"><a class="header" href="#using-tilt">Using Tilt</a></h2>
<p>Tilt can pretty much be treated as an external environment / testnet that you can easily spin up and tear down.</p>
<p>If you've followed the standard setup, all your resources will be bound to various ports on localhost. To see all the endpoints which are being hosted in your Tilt environment, you should check the Tilt dashboard, located at <a href="http://localhost:10350/overview">http://localhost:10350/overview</a>.</p>
<p>All the deployed contract addresses can be found under the 'Devnet / Tilt' section of <a href="technical/env/../../reference/contracts.html">contracts</a>.</p>
<p>Useful information pertaining to funded wallets &amp; private keys can also be found in the <a href="https://github.com/wormhole-foundation/wormhole/blob/main/docs/devnet.md">devnet.md</a> file of the docs.</p>
<p>Additional helpful resources can be found in the <a href="technical/env/./tooling.html">Tooling</a> page.</p>
<h2 id="shutting-down-tilt"><a class="header" href="#shutting-down-tilt">Shutting down Tilt</a></h2>
<p>In order to shut down Tilt, simply run</p>
<pre><code>tilt down
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h1>
<p>Tilt, Kubernetes, and Docker may be new tools for developers entering the Wormhole ecosystem. This section is meant to provide some additional support when setting up the Wormhole Tilt environment.</p>
<h1 id="macos-install"><a class="header" href="#macos-install">macOS Install</a></h1>
<h2 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h2>
<p>Install <a href="https://brew.sh">Homebrew</a> if you don't already have it.</p>
<p>You can grab it with:</p>
<pre><code class="language-sh">/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;
</code></pre>
<p>From there, all the other installs are one-liners.</p>
<h3 id="1-install-go"><a class="header" href="#1-install-go">1. Install Go</a></h3>
<pre><code class="language-sh">brew install go
</code></pre>
<h3 id="2-install-docker"><a class="header" href="#2-install-docker">2. Install Docker</a></h3>
<pre><code class="language-sh">brew install docker
</code></pre>
<p>After installation, go into Docker settings and switch ON <code>kubernetes</code>. Also configure Docker to have 4 CPUs and ~16GB of RAM.</p>
<h3 id="3-install-tilt"><a class="header" href="#3-install-tilt">3. Install Tilt</a></h3>
<pre><code class="language-sh">brew install tilt
</code></pre>
<h3 id="4-clone-wormhole-repo-and-start-tilt"><a class="header" href="#4-clone-wormhole-repo-and-start-tilt">4. Clone Wormhole Repo and Start Tilt</a></h3>
<pre><code class="language-sh">git clone --branch main https://github.com/wormhole-foundation/wormhole.git
cd wormhole/
tilt up
</code></pre>
<p>You'll be able to access the Tilt UI at
<code>localhost:10350</code></p>
<h1 id="linux--wsl-install"><a class="header" href="#linux--wsl-install">Linux &amp; WSL Install</a></h1>
<h2 id="script-setup"><a class="header" href="#script-setup">Script Setup</a></h2>
<p>If you're using a Debian distro, you should run the <a href="https://github.com/wormhole-foundation/wormhole/blob/main/scripts/dev-setup.sh">dev-setup.sh</a> script. Even if you're not using Debian, this script still contains the main steps for setup.</p>
<h2 id="regular-setup"><a class="header" href="#regular-setup">Regular Setup</a></h2>
<h3 id="1-install-go-1"><a class="header" href="#1-install-go-1">1. Install Go</a></h3>
<pre><code class="language-sh">wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz
rm -rf /usr/local/go &amp;&amp; tar -C /usr/local -xzf go1.18.1.linux-amd64.tar.gz
</code></pre>
<h3 id="2-install-docker-1"><a class="header" href="#2-install-docker-1">2. Install Docker</a></h3>
<p>If you're using Linux with a window manager, consider getting Docker Desktop instead of the following command. It comes with Kubernetes built in and you won't need to download <code>minikube</code>. It's recommended to have at least 4 CPUs and 16GB RAM dedicated to Docker.</p>
<p>Also, make absolutely sure that you set up Docker as a non-root user.</p>
<p><a href="https://docs.docker.com/engine/install/ubuntu/#installation-methods">https://docs.docker.com/engine/install/ubuntu/#installation-methods</a></p>
<h3 id="3a-docker-desktop-install"><a class="header" href="#3a-docker-desktop-install">3a. (Docker Desktop Install)</a></h3>
<p>Enable Kubernetes by going into Settings &gt; Kubernetes</p>
<h3 id="3b-non-docker-desktop"><a class="header" href="#3b-non-docker-desktop">3b. (Non Docker Desktop)</a></h3>
<p>Install <a href="https://minikube.sigs.k8s.io/docs/start/"><code>minikube</code></a>.</p>
<p>Configure minikube:</p>
<pre><code>minikube start --driver=docker --kubernetes-version=v1.23.3 --cpus=4 --memory=14G --disk-size=10G --namespace=wormhole
</code></pre>
<p>Minikube needs to be running for tilt to work, so always make sure to run <code>minikube start</code> before you bring up tilt.</p>
<h3 id="4-install-tilt"><a class="header" href="#4-install-tilt">4. Install Tilt</a></h3>
<p>Install Tilt by copy pasting this into the Terminal:</p>
<pre><code class="language-sh">curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
</code></pre>
<h3 id="5-clone-the-wormhole-repo-and-start-tilt"><a class="header" href="#5-clone-the-wormhole-repo-and-start-tilt">5. Clone the Wormhole Repo and start Tilt</a></h3>
<pre><code class="language-sh">git clone --branch main https://github.com/wormhole-foundation/wormhole.git
</code></pre>
<p>If you're running Tilt on your machine:</p>
<pre><code class="language-sh">cd wormhole/
tilt up
</code></pre>
<p>If you're running Tilt in a VM, you'll need to pass in some extra flags to enable Tilt to listen to incoming traffic from external addresses:</p>
<pre><code class="language-sh">cd wormhole
tilt up --host=0.0.0.0 -- --webHost=0.0.0.0
</code></pre>
<p>You can now access the Tilt UI at either <code>localhost:10350</code> or <code>vm_external_ip:10350</code>.</p>
<p>If the VM's external IP doesn't work, check firewall and port settings to make sure your VM allows incoming traffic.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="wormhole-local-validator"><a class="header" href="#wormhole-local-validator">Wormhole Local Validator</a></h1>
<h2 id="what-is-wormhole-local-validator-wlv"><a class="header" href="#what-is-wormhole-local-validator-wlv">What is Wormhole Local Validator (WLV)?</a></h2>
<p><a href="https://github.com/wormhole-foundation/xdapp-book/tree/main/projects/wormhole-local-validator">Wormhole Local Validator</a> is meant to be the simplest custom environment. It consists only of a dockerized Guardian image, and some utility tooling to aid with contract management. This allows you to set it up with any blockchain you'd like.</p>
<h3 id="is-wlv-right-for-you"><a class="header" href="#is-wlv-right-for-you">Is WLV Right for You?</a></h3>
<p>Here's a succinct list of the pros and cons of the environment, so you can decide if it's the right fit for you.</p>
<p><strong>Pros</strong></p>
<ul>
<li>Lightweight, low system resource demand.</li>
<li>Fast iteration times.</li>
<li>Can be added into an existing blockchain development setup.</li>
</ul>
<p><strong>Cons</strong></p>
<ul>
<li>You may end up reinventing the tilt/testnet environment as you add more components.</li>
</ul>
<h2 id="setting-up-wormhole-local-validator"><a class="header" href="#setting-up-wormhole-local-validator">Setting up Wormhole Local Validator</a></h2>
<p>You will need Docker running in order to set up WLV. If you're on desktop, <a href="https://docs.docker.com/get-docker/">Docker Desktop</a> is generally the best choice, though <a href="https://docs.docker.com/engine/">Docker Engine</a> works fine too.</p>
<p>From there, you just need nodes for the blockchains you're interested in developing on. There is code to spin up EVM and Solana local validators included in the WLV project repo, as well as scripts to deploy the necessary Wormhole contracts to your local instances.</p>
<p>Further information can be found in the project's <a href="https://github.com/wormhole-foundation/xdapp-book/blob/main/projects/wormhole-local-validator/README.md">README</a>.</p>
<h2 id="troubleshooting-1"><a class="header" href="#troubleshooting-1">Troubleshooting</a></h2>
<p>Q: Anvil isn't working</p>
<ul>
<li>While Foundry's 'forge' tool is the generally recommended tool for EVM contract compilation, Anvil isn't currently compatible with guardiand. Anvil reports block headers in a way which is non-compliant with go-ethereum, which means the guardian node can't correctly read anvil headers.</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="testnet"><a class="header" href="#testnet">Testnet</a></h1>
<p>Wormhole testnet is spread across many of the most popular testnet blockchains.</p>
<h3 id="is-testnet-right-for-you"><a class="header" href="#is-testnet-right-for-you">Is Testnet right for you?</a></h3>
<p>The primary reason to use Wormhole testnet is to simplify the management of your dependent contracts. This will vary from blockchain to blockchain.</p>
<p>For example, some blockchain ecosystems have the standard that their contracts are closed-source, and there are no tools to fork mainnet. Other ecosystems, such as EVM, have tools like <a href="https://github.com/foundry-rs/foundry">foundry</a>, which allow you to hardfork the mainnet ecosystem into a local development node.</p>
<p>In short, testnet tends to be the correct choice only when you have contract dependencies, and those dependencies are difficult to get working in a local environment. In most other cases, testnet tends to be more work than it's worth.</p>
<p>Here's a succinct list of the pros and cons of the environment, so you can decide if it's the right fit for you.</p>
<p><strong>Pros</strong></p>
<ul>
<li>Many other projects deploy their contracts to testnet.</li>
<li>In ecosystems without extensive local tooling, this may be the preferred development environment.</li>
</ul>
<p><strong>Cons</strong></p>
<ul>
<li>Many testnets are somewhat unstable and have outages or partitioning events.</li>
<li>Wormhole Testnet sometimes misses VAAs due to testnet instabilities.</li>
<li>Testnet tokens are often difficult to acquire.</li>
</ul>
<h2 id="using-testnet"><a class="header" href="#using-testnet">Using Testnet</a></h2>
<p>If you elect to use testnet, the Wormhole contracts addresses can be found in the <a href="technical/env/../../reference/contracts.html">Contracts</a> page.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="tooling"><a class="header" href="#tooling">Tooling</a></h1>
<p>Regardless of the development environment that you use, there are a few wormhole-specific tools you should know about.</p>
<h3 id="wormhole-core-repository"><a class="header" href="#wormhole-core-repository"><a href="https://github.com/wormhole-foundation/wormhole/tree/main/">Wormhole Core Repository</a></a></h3>
<ul>
<li>Most developers find it useful to clone the Wormhole Core repository. This repository provides the Devnet Tilt environment, plenty of useful code examples and tests, along with some utilities which do not have an official release package.</li>
</ul>
<h3 id="worm-cli-tool"><a class="header" href="#worm-cli-tool"><a href="https://github.com/wormhole-foundation/wormhole/tree/main/clients/js">Worm CLI tool</a></a></h3>
<ul>
<li>Swiss-Army Knife Utility CLI tool. Excellent for creating one-off VAAs, parsing VAAs, reading Wormhole contract configurations, and more.</li>
</ul>
<h3 id="orchestrator"><a class="header" href="#orchestrator"><a href="https://github.com/wormhole-foundation/xdapp-book/blob/main/projects/evm-messenger/orchestrator.js">Orchestrator</a></a></h3>
<ul>
<li>Small WIP tool which comes as part of the <a href="technical/env/./wlv.html">Wormhole Local Validator</a> environment. Used to quickly deploy and redeploy multiple smart contracts. Will eventually become part of a larger deployment management tool.</li>
</ul>
<h3 id="wormhole-sdks"><a class="header" href="#wormhole-sdks"><a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk">Wormhole SDKs</a></a></h3>
<ul>
<li>Libraries in various languages to help with interacting with Wormhole contracts.</li>
</ul>
<h3 id="wormhole-typescript-sdk"><a class="header" href="#wormhole-typescript-sdk"><a href="https://www.npmjs.com/package/@certusone/wormhole-sdk">Wormhole Typescript SDK</a></a></h3>
<ul>
<li>Typescript SDK distributed on npm. It can greatly aid in writing frontend code for xDapps and utilizing the Wormhole Token Bridge directly.</li>
</ul>
<h3 id="wormhole-spy-sdk"><a class="header" href="#wormhole-spy-sdk"><a href="https://github.com/wormhole-foundation/wormhole/tree/main/spydk/js">Wormhole Spy SDK</a></a></h3>
<ul>
<li>The Wormhole Spy SDK allows you to listen to all of the activity on the Guardian Network.</li>
</ul>
<h3 id="reference-bridge-ui"><a class="header" href="#reference-bridge-ui"><a href="https://github.com/wormhole-foundation/example-token-bridge-ui">Reference Bridge UI</a></a></h3>
<ul>
<li>An example GUI which can be used to perform token transfers around the ecosystem.</li>
</ul>
<h3 id="explorer"><a class="header" href="#explorer"><a href="https://wormhole.com/explorer/">Explorer</a></a></h3>
<ul>
<li>Resource for looking at individual transfers statuses on mainnet and testnet.</li>
</ul>
<!--
how to use on tilt?
-->
<!--
Example projects
-->
<h2 id="example-projects"><a class="header" href="#example-projects">Example Projects</a></h2>
<h3 id="basic-examples"><a class="header" href="#basic-examples"><a href="https://github.com/wormhole-foundation/xdapp-book/tree/main/projects">Basic Examples</a></a></h3>
<ul>
<li>Several example projects are bundled here. They show minimum-code examples for how to send messages, tokens, and other common functions.</li>
</ul>
<h3 id="icco"><a class="header" href="#icco"><a href="https://github.com/certusone/wormhole-icco">ICCO</a></a></h3>
<ul>
<li>Productionized, audited xDapp which does cross-chain token launches. Great example of what a robust xDapp, written across multiple ecosystems looks like.</li>
</ul>
<h3 id="native-swap"><a class="header" href="#native-swap"><a href="https://github.com/certusone/wormhole-nativeswap-example">Native Swap</a></a></h3>
<ul>
<li>Example cross-chain dex, utilizing the stablecoin highway model.</li>
</ul>
<h3 id="wormhole-examples"><a class="header" href="#wormhole-examples"><a href="https://github.com/certusone/wormhole-examples">Wormhole Examples</a></a></h3>
<ul>
<li>More example components. Has a mix of relayers, xDapps, NFT projects, and more.</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="contract-development"><a class="header" href="#contract-development">Contract Development</a></h1>
<p>This section should help you get off the ground with contract development in the Wormhole ecosystem.</p>
<p>It is written in a fashion which assumes familiarity with smart contract development in each ecosystem. As such, it doesn't provide information on smart contract basics, and instead focuses on how to properly interact with the provided Wormhole functions.</p>
<p>While the syntax for each programming environment differs, the general structure of the code and best practices tend to be quite similar. You should consider referencing the sections for environments other than the one you're working in, as many of the concepts outlined here are universal to cross-chain development.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="evm-1"><a class="header" href="#evm-1">EVM</a></h1>
<p><strong>Disclaimer</strong>: This section is written as a guide for how to use Wormhole for experienced EVM developers. If you are new to using the EVM ecosystem, it's recommended for you to get started with a tutorial like <a href="https://ethereum.org/en/developers/docs/intro-to-ethereum/">this</a>.</p>
<p>Within the Wormhole ecosystem, EVM refers to any blockchain that utilizes EVM contracts of Wormhole -- this includes blockchains beyond Ethereum such as Polygon or Avalanche, as well as EVM+ environments such as Acala.</p>
<p>At certain points, it may be easiest to integrate simply by referencing the implementation of the Wormhole contracts. The official implementation for the Wormhole contracts can be found <a href="https://github.com/wormhole-foundation/wormhole/tree/main/ethereum">here</a>.</p>
<h3 id="recommended-tooling-for-evm"><a class="header" href="#recommended-tooling-for-evm">Recommended Tooling for EVM</a></h3>
<p><strong>Frontend Development</strong></p>
<ul>
<li><a href="https://docs.ethers.io/v5/">Ethers</a> an excellent, widely used library for using web-based wallets to interact with EVM blockchains.</li>
</ul>
<p><strong>Contract Development and Testing</strong></p>
<ul>
<li><a href="https://github.com/foundry-rs/foundry">Foundry</a> is the preferred library for the Core Repository. It has tooling for development, testing, compilation, and even the ability to duplicate mainnet environments for development.</li>
<li><a href="https://trufflesuite.com/">Truffle</a> and <a href="https://hardhat.org/">Hardhat</a> are also viable alternatives.</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="core-message-layer"><a class="header" href="#core-message-layer">Core Message Layer</a></h1>
<p>This section will explain how to properly interact with the Wormhole Core Message Layer in an EVM ecosystem.</p>
<p>Messages in Wormhole take the form of a Verified Action Approval (VAA) and both terms can be used interchangeably. The rest of this section will only use the term VAA.</p>
<h2 id="configuring-the-interface"><a class="header" href="#configuring-the-interface">Configuring the Interface</a></h2>
<p><a href="https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/interfaces">Here</a> is the interface for applications to interact with Wormhole's Core Contract to publish VAAs or verify and parse a received VAAs.</p>
<p>Instantiating the interface will depend on the contract address of your development ecosystem and blockchain.</p>
<p>Below is an example line of code to instantiate the interface for mainnet Ethereum:</p>
<pre><code>address private wormhole_core_bridge_address = address(0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B);
IWormhole core_bridge = IWormhole(wormhole_core_bridge_address);
</code></pre>
<h2 id="primary-functions"><a class="header" href="#primary-functions">Primary functions</a></h2>
<p>The Wormhole Core Layer has two important interactions -- (1) emit VAAs, and (2) parse and verify VAAs that originated from other chains.</p>
<h3 id="emitting-a-vaa"><a class="header" href="#emitting-a-vaa">Emitting a VAA</a></h3>
<p>There are two forms of VAAs that can be emitted:</p>
<ul>
<li>Single VAA: all messages will be emitted in this format</li>
<li>Batch VAA: messages that are generated from the same transaction will be emitted in this format. This feature was developed to provide an easier paradigm for composability and better gas efficiency for more involved cross-chain activity.</li>
</ul>
<p>To emit a VAA, always use <code>publishMessage</code> which takes in the following arguments:</p>
<ol>
<li><code>nonce</code> (uint32): a number assigned to each message
<ul>
<li>The <code>nonce</code> provides a mechanism by which to group messages together within a Batch VAA. How the <code>nonce</code> is used is described below.</li>
</ul>
</li>
<li><code>Consistency</code> (uint8): the level of finality the guardians will reach before signing the message
<ul>
<li>Consistency should be considered an enum, not an integer.</li>
<li>On all EVM chains, <code>200</code> will result in an instant message</li>
<li>On Ethereum, <code>201</code> will wait until the block the transaction is in is <code>safe</code></li>
<li>On BSC, the consistency denotes how many block confirmations will be waited before producing the message.</li>
<li>On the remaining EVM chains, all other values will wait for finality, but using <code>1</code> is recommended.</li>
<li>More information about finality can be found <a href="technical/evm//wormhole/3_coreLayerContracts.html#consistency-levels">here</a></li>
</ul>
</li>
<li><code>Payload</code> (bytes[]): raw bytes to emit
<ul>
<li>It is up to the emitting contract to properly define this arbitrary set of bytes.</li>
</ul>
</li>
</ol>
<p><code>publishMessage</code> will output a <code>sequence</code> (uint64) that is used in conjunction with <code>emitterChainID</code> and <code>emitterAddress</code> to retrieve the generated VAA from the Guardian Network.</p>
<blockquote>
<p>How Batch VAAs are generated</p>
<p>There are two mechanisms that allow messages to be Batched together that represent a base and more advanced level of composability.</p>
<ol>
<li>All messages originating from the same transaction will be batched together.</li>
<li>Messages that originate from the same transaction and are assigned the same nonce are additionally batched together.</li>
</ol>
<p><em>Note: Single VAAs will always be emitted for each message within a transaction, regardless of if a message is included in a batch or not.</em></p>
<p>Here is an example of how messages generated from the same transaction may be batched together:</p>
<p>A transaction X that generates 6 messages [A, B, C, D, E, F] that are assigned <code>nonce</code> [1, 2, 2, 3, 3, 4] respectively will generate the following VAAs:</p>
<ul>
<li>(1) full transaction batch VAA
<ul>
<li>[A, B, C, D, E, F]</li>
</ul>
</li>
<li>(2) smaller batch VAA
<ul>
<li>[B, C]</li>
<li>[D, E]</li>
</ul>
</li>
<li>(6) single VAA
<ul>
<li>[A]</li>
<li>[B]</li>
<li>[C]</li>
<li>[D]</li>
<li>[E]</li>
<li>[F]</li>
</ul>
</li>
</ul>
</blockquote>
<h3 id="parsing-and-verifying-a-vaa"><a class="header" href="#parsing-and-verifying-a-vaa">Parsing and Verifying a VAA</a></h3>
<p>Parsing and Verifying a VAA will depend on the type of VAA that your application expects: a Single VAA or a Batch VAA.</p>
<p>For either VAA type, remember to collect gas fees associated with submitting them on-chain after all VAAs have been verified.</p>
<p><strong>Single VAA</strong></p>
<p>To properly parse and verify a single VAA, always use <code>parseAndVerifyVM</code> which takes in one argument: <code>encodedVM</code> (bytes). This function will return three arguments:</p>
<ol>
<li><code>vm</code> (VM): Structured data that reflects the content of the VAA.
<ul>
<li>A breakdown of this message format is described in the <a href="technical/evm/../../wormhole/4_vaa.html">VAA</a> section. Aside from the header information, which can be considered 'trusted', it is up to the recipient contract to properly parse the remaining payload, as this contains the verbatim message sent from the emitting contract.</li>
</ul>
</li>
<li><code>valid</code> (bool): Boolean that reflects whether or not the VAA was properly signed by the Guardian Network</li>
<li><code>reason</code> (string): Explanatory error message if a VAA is invalid, or an empty string if it is valid.</li>
</ol>
<p><strong>Batch VAA</strong></p>
<p>To properly parse and verify a batch VAA, always use <code>parseAndVerifyBatchVM</code> which takes in two arguments: <code>encodedVM</code> (bytes) and <code>cache</code> (bool).</p>
<p>In most scenarios, you'll want to set <code>cache</code> equal to true.</p>
<p>This will return a VM2 object, containing all the 'headless' VAAs contained inside the batch VAA. These headless VAAs can be verified by <code>parseAndVerifyVM</code>, which means that modules which verify messages in an xDapp can be agnostic as to whether a message came from a batch VAA or a single VAA.</p>
<p>The <a href="technical/evm/./bestPractices.html">Best Practices</a> section goes into more depth of how to interact with the coreLayer.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="token-bridge"><a class="header" href="#token-bridge">Token Bridge</a></h1>
<p>This section will explain how to properly interact with the Wormhole Token Bridge Module in an EVM ecosystem.</p>
<h2 id="configuring-the-interface-1"><a class="header" href="#configuring-the-interface-1">Configuring the interface</a></h2>
<p><a href="https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/bridge/interfaces/ITokenBridge.sol">Here</a> is the interface for applications to interact with Wormhole's Token Bridge.</p>
<p>Instantiating the interface will depend on your development ecosystem and blockchain. The Wormhole Token Bridge contract address is usually stored in your contract address.</p>
<p>Below is an example line of code to instantiate the interface for mainnet Ethereum:</p>
<pre><code>address private wormhole_token_bridge_address = address(0x3ee18B2214AFF97000D974cf647E7C347E8fa585);
ITokenBridge token_bridge = ITokenBridge(wormhole_token_bridge_address);
</code></pre>
<h2 id="registering-new-tokens"><a class="header" href="#registering-new-tokens">Registering New Tokens</a></h2>
<p>Attesting a token from EVM needs to happen once per token as it will not be claimable until so. However, there are no restrictions to reattesting a token; repeat attestations will update the metadata.</p>
<p>It is not advised to attest tokens on-chain for most use cases. To attest a token by an off-chain process, you can either do it by hand through one of the Token Bridge UIs (for example <a href="https://www.portalbridge.com/#/register">Portal</a>) or using the <a href="https://www.npmjs.com/package/@certusone/wormhole-sdk">Typescript SDK</a>.</p>
<p><em><a href="technical/evm/../../development/portal/evm/attestingToken.html">Here</a> is an example of how to attest a token using the Typescript SDK.</em></p>
<h2 id="basic-transfer"><a class="header" href="#basic-transfer">Basic Transfer</a></h2>
<p>Basic transfer should only be used if you are transferring tokens to an end user wallet. If the end destination is a contract, you should only use Contract Controlled Transfers (described below).</p>
<p>It is important to note the transferring native currency is a special case of the Basic Transfer. As such, a different function call for initiating and completing a transfer is provided as a QoL improvement that will handle the wrapping and unwrapping of ETH.</p>
<p>To transfer a token, there are four steps:</p>
<ol>
<li>Approve the Token Bridge to spend that token on our behalf.
<ul>
<li><em>Note: Tokens in EVM usually denote up to 18 decimals places. However. Wormhole normalizes this to <strong>8</strong> decimals.</em></li>
</ul>
</li>
</ol>
<pre><code>contractAddress.approve(token_bridge_address, amt);
</code></pre>
<ol start="2">
<li>Transfer the token to create the transfer VAA.
<ul>
<li>This function call will return a <code>sequence</code> (uint64) that is used in the VAA retrieval step.</li>
<li><em>Note: For the recipient address, Wormhole addresses are 32 bytes for standardization across the different blockchains within the Wormhole ecosystem.</em></li>
</ul>
</li>
</ol>
<pre><code>// To initiate transfer of normal ERC-20s
token_bridge.transferTokens(tokenAddress, amount, recipientChain, recipient, arbiterFee, nonce);
// To initiate transfer of native currency
token_bridge.wrapAndTransferETH(recipientChain, recipient, arbiterFee, nonce);
</code></pre>
<ol start="3">
<li>Retrieve the emitted VAA.
<ul>
<li>Basic Transfer VAAs are retrieved from the Guardian Network by the <code>emitterChainID</code>, <code>emitterAddress</code>, and <code>sequence</code>.</li>
</ul>
</li>
</ol>
<pre><code class="language-js">const emitterAddr = getEmitterAddressEth(network.tokenBridgeAddress);
const seq = parseSequenceFromLogEth(tx, network.bridgeAddress);
const vaaURL = `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`;
let vaaBytes = await (await fetch(vaaURL)).json();
while (!vaaBytes.vaaBytes) {
console.log(&quot;VAA not found, retrying in 5s!&quot;);
await new Promise((r) =&gt; setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
</code></pre>
<ol start="4">
<li>Complete the transfer using the VAA.</li>
</ol>
<pre><code>// To complete transfer of normal ERC-20s
token_bridge.completeTransfer(VAA);
// To complete transfer of native currency
completeTransferAndUnwrapETH(VAA);
</code></pre>
<h2 id="contract-controlled-transfer"><a class="header" href="#contract-controlled-transfer">Contract Controlled Transfer</a></h2>
<p>For any token transfers where the destination is a contract, you should always use Contract Controlled Transfers.</p>
<p>There are a few main differences between Contract Controlled Transfers and Basic Transfers:</p>
<ul>
<li>messages contains both tokens and an arbitrary payload</li>
<li>messages can only be redeemed by a specified contract address</li>
<li>messages do not have a relayer fee field because of the redemption restriction above</li>
</ul>
<p>As was the case with Basic Transfers, transferring native currency is a special case for Contract Controlled Transfers as well. As such, similar QoL improvement functions are provided that handle the wrapping and unwrapping of ETH.</p>
<p>The process of sending a Contract Controlled Transfer is very similar to that of a Basic Transfer:</p>
<ol>
<li>Approve the Token Bridge to spend that token on our behalf.
<ul>
<li><em>Note: Tokens in EVM usually denote up to 18 decimals places. However. Wormhole normalizes this to <strong>8</strong> decimals.</em></li>
</ul>
</li>
</ol>
<pre><code>contractAddress.approve(token_bridge_address, amt);
</code></pre>
<ol start="2">
<li>Transfer the token to create the transfer VAA.
<ul>
<li>This function call will return a <code>sequence</code> (uint64) that is used in the VAA retrieval step.</li>
<li><em>Note: For the recipient address, Wormhole addresses are 32 bytes for standardization across the different blockchains within the Wormhole ecosystem.</em></li>
</ul>
</li>
</ol>
<pre><code>// To initiate transfer of normal ERC-20s
token_bridge.transferTokensWithPayload(tokenAddress, amount, recipientChain, recipient, nonce, payload);
// To initiate transfer of native currency
token_bridge.wrapAndTransferETHWithPayload(recipientChain, recipient, nonce, payload);
</code></pre>
<ol start="3">
<li>Retrieve the emitted VAA.
<ul>
<li>Contract Controlled Transfer VAAs are retrieved from the Guardian Network by the <code>emitterChainID</code>, <code>emitterAddress</code>, and <code>sequence</code>.</li>
</ul>
</li>
</ol>
<pre><code class="language-js">const emitterAddr = getEmitterAddressEth(network.tokenBridgeAddress);
const seq = parseSequenceFromLogEth(tx, network.bridgeAddress);
const vaaURL = `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`;
let vaaBytes = await (await fetch(vaaURL)).json();
while (!vaaBytes.vaaBytes) {
console.log(&quot;VAA not found, retrying in 5s!&quot;);
await new Promise((r) =&gt; setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
</code></pre>
<ol start="4">
<li>Complete the transfer using the VAA.</li>
</ol>
<pre><code>// To complete transfer of normal ERC-20s
token_bridge.completeTransferWithPayload(VAA);
// To complete transfer of native currency
completeTransferAndUnwrapETHWithPayload(VAA);
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="nft-bridge"><a class="header" href="#nft-bridge">NFT Bridge</a></h1>
<p>This section will explain how to properly interact with the NFT Bridge Module in an EVM ecosystem.</p>
<h2 id="configuring-the-interface-2"><a class="header" href="#configuring-the-interface-2">Configuring the interface</a></h2>
<p><a href="https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/nft/interfaces/INFTBridge.sol">Here</a> is the interface for applications to interact with Wormhole's NFT Bridge.</p>
<!---
TODO
merge down the interface PR and link to actual file
-->
<p>Instantiating the interface will depend on the contract address of your development ecosystem and blockchain.</p>
<p>Below is an example line of code to instantiate the interface for mainnet Ethereum:</p>
<pre><code>address private wormhole_NFT_bridge_address = address(0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE);
INFTBridge NFT_bridge = INFTBridge(wormhole_nft_bridge_address);
</code></pre>
<h2 id="transferring-a-nft"><a class="header" href="#transferring-a-nft">Transferring a NFT</a></h2>
<p>The Wormhole NFT Bridge only supports tokens compliant with the ERC-721 interface, and functions by creating a 'wrapped NFT' with identical metadata. How this is implemented varies by ecosystem.</p>
<p><strong>Note</strong>: Unlike tokens, there is no attestation required for bridging NFTs.</p>
<p>To transfer a NFT, there are three steps:</p>
<ol>
<li>Initiate the NFT transfer
<ul>
<li>This function call will return a <code>sequence</code> (uint64) that is used in the VAA retrieval step</li>
</ul>
</li>
</ol>
<pre><code>transferNFT(tokenAddress, tokenID, recipientChain, recipient, nonce);
</code></pre>
<ol start="2">
<li>Retrieve the emitted VAA from the Guardian Network. (Usually done by a relayer)
<ul>
<li>NFT Transfer VAAs are retrieved from the Guardian Network by the <code>emitterChainID</code>, <code>emitterAddress</code>, and <code>sequence</code>.</li>
</ul>
</li>
</ol>
<pre><code class="language-js">const emitterAddr = getEmitterAddressEth(network.NFTBridgeAddress);
const seq = parseSequenceFromLogEth(tx, network.bridgeAddress);
const vaaURL = `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`;
let vaaBytes = await (await fetch(vaaURL)).json();
while (!vaaBytes.vaaBytes) {
console.log(&quot;VAA not found, retrying in 5s!&quot;);
await new Promise((r) =&gt; setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
</code></pre>
<ol start="3">
<li>Complete the NFT transfer by submitting the resultant VAA to the target chain.</li>
</ol>
<pre><code>completeTransfer(VAA);
</code></pre>
<!---
TODO
additional use cases, most specifically how to grab the origin address of the wrapped NFT
-->
<div style="break-before: page; page-break-before: always;"></div><h1 id="relayer-module"><a class="header" href="#relayer-module">Relayer Module</a></h1>
<p>The WormholeRelayer module allows developers to deliver their VAAs via an untrusted <strong>DeliveryProvider</strong>, rather than needing to develop and host their own relay infrastructure.</p>
<h2 id="quickstart"><a class="header" href="#quickstart">Quickstart</a></h2>
<ul>
<li><a href="https://github.com/wormhole-foundation/hello-wormhole#readme">Hello Wormhole</a> - Get a full Wormhole application working, deployed on testnet, and tested both locally and in Testnet</li>
<li><a href="https://github.com/wormhole-foundation/hello-wormhole/blob/main/hello-wormhole-explained.md">How Does Hello Wormhole Work</a> - Get a quick architecture overview of how HelloWormhole (and automatic relayers as a whole) work behind the scenes</li>
<li><a href="https://github.com/wormhole-foundation/hello-wormhole/blob/main/beyond-hello-wormhole.md">Beyond Hello Wormhole</a> - about Protections, Refunds, and Forwards</li>
<li><a href="https://github.com/wormhole-foundation/hello-token#readme">Hello Token</a> - Get a full TokenBridge Wormhole application working, deployed on testnet, and tested both locally and in Testnet</li>
<li><a href="https://github.com/wormhole-foundation/wormhole-solidity-sdk">Wormhole Solidity SDK</a> - Install useful helpers and interface files into your existing application to help it go cross-chain </li>
<li><a href="https://github.com/wormhole-foundation/example-cross-chain-borrow-lend">Example Cross Chain Borrow Lend</a> - Example application using the Wormhole Relayer module</li>
</ul>
<br/>
<br/>
<h2 id="interacting-with-the-module"><a class="header" href="#interacting-with-the-module">Interacting with the Module</a></h2>
<p>There are two relevant interfaces to discuss when utilizing the WormholeRelayer module:</p>
<ul>
<li><a href="https://github.com/wormhole-foundation/wormhole/blob/generic-relayer-merge/ethereum/contracts/interfaces/relayer/IWormholeRelayer.sol">IWormholeRelayer</a> - the primary interface by which you interact with the module. It allows you to request deliveries from a given DeliveryProvider.</li>
<li><a href="https://github.com/wormhole-foundation/wormhole/blob/generic-relayer-merge/ethereum/contracts/interfaces/relayer/IWormholeReceiver.sol">IWormholeReceiver</a> - this is the interface you are responsible for implementing. It allows the selected Delivery Provider to deliver messages to your contract.</li>
</ul>
<p>Check the <a href="technical/evm/../../reference/contracts.html">deployed contracts page</a> for contract addresses on each supported blockchain.</p>
<p>A minimal setup that can receive messages looks something like this:</p>
<pre><code class="language-solidity">import &quot;../IWormholeRelayer.sol&quot;;
import &quot;../IWormholeReceiver.sol&quot;;
contract HelloWorld is IWormholeReceiver {
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalVaas,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) public payable override onlyRelayerContract {
}
modifier onlyRelayerContract() {
require(msg.sender == WORMHOLE_RELAYER_CONTRACT_ADDRESS, &quot;msg.sender is not WormholeRelayer contract.&quot;);
_;
}
}
</code></pre>
<br/>
<br/>
<br/>
<br/>
<h1 id="sending-messages"><a class="header" href="#sending-messages">Sending Messages</a></h1>
<p>Let's cover how to send a &quot;Hello World&quot; message. The basic mechanism to call the <code>sendPayloadToEvm</code> function on the IWormholeRelayer interface. Here's its basic usage:</p>
<pre><code class="language-solidity">function sendHelloWorldMessage() public payable {
//spell out some constants
bytes memory helloWorld = abi.encode(&quot;Hello World&quot;);
uint256 gasLimit = 500000;
uint256 receiverValue = 0; // don't deliver any 'msg.value' along with the message
//calculate cost to deliver message
(uint256 deliveryCost,) = relayer.quoteEVMDeliveryPrice(
targetChain,
receiverValue,
gasLimit
);
// publish delivery request
relayer.sendPayloadToEvm{value: deliveryCost}(
TARGET_CHAIN, TARGET_CONTRACT, helloWorld, receiverValue, gasLimit
);
}
</code></pre>
<p>In this code, we first emit a &quot;Hello World&quot; message via the Wormhole Core Layer the same way it's always done. We then request from the default DeliveryProvider that they deliver all messages emitted from this transaction with nonce 1 to the TARGET_CONTRACT on the TARGET_CHAIN.</p>
<p>Let's break down all the things happening in this code.</p>
<ul>
<li><code>deliveryCost</code> - this calculates the necessary price for the selected DeliveryProvider to perform a delivery with 500,000 gas on the target chain. Thus, by paying this <code>deliveryCost</code>, you can be sure that your <code>receiveWormholeMessages</code> function will be invoked with a gas limit of 500,000. There's more info on the how these deliveries work in a later section.</li>
<li><code>targetChain</code> - Wormhole chainId where the messages should be sent. This is not the same as the EVM Network ID!</li>
<li><code>targetContract</code> - Contract address on targetChain where the messages should be sent. </li>
<li><code>gasLimit</code> - this specifies the maximum units of targetChain gas that can be used to execute receiveWormholeMessages on the targetChain. If the gasLimit is exceeded during contract execution you will enter a <strong>receiver failure</strong> state. Note: If you have a wallet on the targetChain (or any chain) that you wish to collect refunds to (i.e. if your contract takes less than <code>gasLimit</code> units of gas to execute), there is an option in IWormholeRelayer to have this happen.</li>
<li><code>receiverValue</code> - this amount (in targetChain wei) is passed to <code>receiveWormholeMessages</code> on the target chain when the delivery happens. This is useful for covering small fees encountered during execution on the target chain.</li>
</ul>
<br/>
<br/>
<br/>
<br/>
<h2 id="receiving-messages"><a class="header" href="#receiving-messages">Receiving Messages</a></h2>
<p>To receive messages through the relayer module, simply implement the <code>IWormholeReceiver</code> interface.</p>
<pre><code class="language-solidity">function receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalVaas,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) public payable override onlyRelayerContract {
//Do whatever you need to do with 'payload'
}
modifier onlyRelayerContract() {
require(msg.sender == WORMHOLE_RELAYER_ADDRESS, &quot;msg.sender is not WormholeRelayer contract.&quot;);
_;
}
</code></pre>
<p>Breaking down everything happening in this code snippet:</p>
<ul>
<li><code>receiveWormholeMessages</code> - this is the function which will be invoked by the WormholeRelayer contract when the DeliveryProvider completes the delivery. It will be executed with a gas limit that was specified in the deliveryRequest.</li>
<li><code>payload</code> - This is the payload that was sent in the delivery request</li>
<li><code>additionalVaas</code> - In the example shown above, this will be empty. This will contain any additional VAAs that were requested to be relayed through the 'vaaKeys' field. These should not be considered trusted until you call <code>core_bridge.parseAndVerifyVM</code> or otherwise verify them against the Core Contract! (More on this in <a href="technical/evm/./bestPractices.html">Best Practices</a>)</li>
<li><code>sourceAddress</code> - Address that requested this delivery (in Wormhole bytes32 format; i.e. if this is originally an EVM address, it will be left-padded with 12 zeros)</li>
<li><code>sourceChain</code> - Chain (in Wormhole Chain ID format) that this delivery was requested from </li>
<li><code>deliveryHash</code> - Unique identifier of this delivery request. Specifically, this is the hash of the delivery VAA. </li>
<li><code>onlyRelayerContract</code> - this prevents contracts other than the WormholeRelayer contract from calling this entrypoint. The WormholeRelayer contract handles the invocation of <code>receiveWormholeMessages</code>, and ensures that relayers can't improperly call it.</li>
</ul>
<p>Here are a few other important points to note:</p>
<ul>
<li>
<p><code>receiveWormholeMessages</code> function should generally not throw an exception or revert during execution. If an exception is thrown, or a 'require' statement is violated, you will enter a receiver failure. When a receiver failure occurs, the execution of <code>receiveWormholeMessages</code> is reverted.</p>
</li>
<li>
<p><code>receiveWormholeMessages</code> will only be called with as much gas as was specified by the gasLimit specified when the message delivery was requested. If you exceed this gas amount, you will enter a delivery failure.</p>
</li>
</ul>
<br/>
<br/>
<br/>
<br/>
<h1 id="delivery-guarantees--delivery-failures"><a class="header" href="#delivery-guarantees--delivery-failures">Delivery Guarantees &amp; Delivery Failures</a></h1>
<p>The WormholeRelayer protocol is intended to create a service interface whereby mutually distrustful integrators and DeliveryProviders can work together to provide a seamless Dapp experience. You don't trust the delivery providers with your data, and the delivery providers don't trust your smart contract. The primary agreement which is made between integrators and delivery providers is that:</p>
<p><strong>When a delivery is requested, the delivery provider will attempt to deliver the VAA within the provider's stated delivery timeframe.</strong></p>
<p>As a baseline, DeliveryProviders should aim to perform deliveries <strong>within 5 minutes of the VAA creation, assuming blockchain liveness</strong>.</p>
<p>This creates a marketplace whereby providers can set different price levels and service guarantees. Delivery providers effectively accept the slippage risk premium of delivering your VAAs in exchange for a set fee rate. Thus, the providers agree to deliver your messages <strong>even if they have to do so at a loss</strong>.</p>
<p>Relay providers should set their prices such that they turn a profit on average, but not necessarily on every single transfer. Thus, some providers may choose to set higher rates for tighter guarantees, or lower rates for less stringent guarantees.</p>
<br/>
<h3 id="receiver-failures"><a class="header" href="#receiver-failures">Receiver Failures</a></h3>
<p>All deliveries should result in one of following four outcomes prior to the delivery timeframe of the delivery provider. These outcomes are emitted as EVM events from the WormholeRelayer contract when they occur.</p>
<ul>
<li>Delivery Success</li>
<li>Receiver Failure</li>
<li>Forward Request Success (More on forwarding in a later section)</li>
<li>Forward Request Failure</li>
</ul>
<p>Receiver Failures are not a nebulous 'something went wrong' term in the Wormhole Core Relayer protocol. A delivery failure is a well-defined term which means that the selected provider <strong>performed the delivery, but the delivery was not able to be completed.</strong> There are only three causes for a delivery failure:</p>
<ul>
<li>the target contract does not implement the <code>IWormholeReceiver</code> interface</li>
<li>the target contract threw an exception or reverted during execution of <code>receiveWormholeMessages</code></li>
<li>the target contract exceeded the specified <code>gasLimit</code> while executing <code>receiveWormholeMessages</code></li>
</ul>
<p>All three of these scenarios should generally be avoidable by the integrator, and thus it is up to integrator to resolve them.</p>
<p>Any other senario which causes a delivery to not be performed should be considered an <strong>outage</strong> by some component of the system, including potentially the blockchains themselves.</p>
<br/>
<h3 id="redelivery"><a class="header" href="#redelivery">Redelivery</a></h3>
<p>What happens in the case of a delivery failure is up to you as the integrator. It is perfectly acceptable to just leave the delivery incomplete, if that's acceptable for your usecase.</p>
<p>However, in the scenario where you need to reattempt the delivery, there is a function specifically for this.</p>
<pre><code class="language-solidity">function resendExample() public payable {
//spelling out consts
IWormholeRelayer.VaaKey memory deliveryVaaKey = IWormholeRelayer.VaaKey({
chainId: SOURCE_CHAIN,
emitterAddress: EMITTER_ADDRESS, // address which requested the delivery, in Wormhole bytes32 format (for EVM addresses, left-padded with 12 zeros)
sequence: DELIVERY_SEQUENCE_NUMBER // return value from the send
});
uint16 targetChain = TARGET_CHAIN;
uint256 newReceiverValue = 0;
uint256 newGasLimit = 1000000;
(uint256 deliveryCost,) = relayer.quoteEVMDeliveryPrice(
targetChain,
newReceiverValue,
newGasLimit
);
relayer.resendToEvm{value: deliveryCost}(
deliveryVaaKey, targetChain, newReceiverValue, newGasLimit, relayer.getDefaultDeliveryProvider()
);
}
</code></pre>
<p>Note: <strong>the requester must pay a second time in order to initiate the redelivery</strong>.</p>
<p>Also note: <strong>Redeliveries must not decrease the original gasLimit, receiverValue, or targetChainRefundPerGasUnused</strong></p>
<br/>
<br/>
<br/>
<br/>
<h2 id="forwarding"><a class="header" href="#forwarding">Forwarding</a></h2>
<p>So far we've discussed how to perform a simple delivery from chain A to chain B. However, a fairly common scenario that you may encounter is that you may want to perform a multi-hop delivery from chain A to B to C, or to round-trip a delivery back to the source chain. Forwarding is a feature specifically designed to suit these use cases.</p>
<p>Forwarding is quite similar to a normal 'send' action, however it has a couple special traits.</p>
<ul>
<li>
<p>Instead of calling <code>send</code>, you should call <code>forward</code>.</p>
</li>
<li>
<p><code>forward</code> can only be called while a delivery is being executed (I.E, during execution of receiveWormholMessages), and can only be called by the contract which is receiving the delivery.</p>
</li>
<li>
<p>When a forward is requested, the <code>refundAddress</code> of the delivery is ignored, and the refund is instead used to pay for the next delivery.</p>
</li>
<li>
<p>You can add supplemental funds to cover the forwarding costs by passing additional tokens in msg.value.</p>
</li>
<li>
<p>If the refund amount + supplemental funds do not cover the cost of the delivery, you will encounter a Forward Failure.</p>
</li>
<li>
<p>Forward Failures, just like Receiver Failures, revert the previous delivery</p>
<br/>
<br/>
<br/>
<br/>
</li>
</ul>
<h2 id="security--proper-usage"><a class="header" href="#security--proper-usage">Security &amp; Proper Usage</a></h2>
<h3 id="validating-received-messages"><a class="header" href="#validating-received-messages">Validating Received Messages</a></h3>
<p>The array of <code>additionalVaas</code> which is passed to <code>receiveWormholeMessages</code> are non-validated VAAs. This means <em>you are responsible for validating these messages</em>. This is most commonly done by either calling <code>parseAndVerifyVM</code> on the Wormhole Core Contract, or by passing the VAA into another contract which will do its own verification. However, this design benefits you quite a few desireable security properties:</p>
<ul>
<li>
<p>Relayers are not trusted with payload content! If they were to modify the content of a payload during delivery, it would invalidate the signatures on the delivery VAA, which the WormholeRelayer contract checks before delivering the message. This means, as long as you restrict the receiveWormholeMessage to only be called by the WormholeRelayer contract, relayers are only trusted for <strong>liveness</strong>.</p>
</li>
<li>
<p>There are also very few trust assumptions placed on the WormholeRelayer contract. The WormholeRelayer contract only enforces a few protections, such as that refunds are correctly paid out</p>
</li>
</ul>
<p>However, as always with smart contract development, there are some things you should be aware of:</p>
<ul>
<li>Deliveries can potentially be performed multiple times, and redeliveries for any delivery can be requested by anyone. If you need replay protection on your deliveries, you will have to enforce it yourself. One common methodology for replay protection is simply to store a map of 'deliveryHash' to boolean in your contract state, and check that 'deliveryHash' doesn't already exist in this map.</li>
</ul>
<br/>
<br/>
<br/>
<br/>
<h2 id="tricks-tips-and-common-solutions"><a class="header" href="#tricks-tips-and-common-solutions">Tricks, Tips, and Common Solutions</a></h2>
<br />
<h3 id="safe-round-trips"><a class="header" href="#safe-round-trips">Safe Round-Trips</a></h3>
<p>A very common scenario for Hub-and-Spoke style applications is to want to round-trip a delivery from a Spoke chain to the Hub chain and then back. In this case, it's generally a good idea to set <code>receiverValue</code> to be what you expect the price of the second leg of the delivery will be (in Hub-chain currency) and then perform a <code>forward</code>, passing in receiverValue as msg.value, from the Hub chain with the end-user's wallet set to the <code>refundAddress</code>. Thus the end user is ultimately returned all unused funds.</p>
<h3 id="bridging-multiple-tokens"><a class="header" href="#bridging-multiple-tokens">Bridging Multiple Tokens</a></h3>
<p>Because the WormholeRelayer can handle delivery of multiple messages, you can call the Token Bridge module multiple times and have 'vaaKeys' identify the resulting VAAs, and then these VAAs will be included in the delivery array. This is great for scenarios where an action results in tokens being sent to two different locations, or multiple tokens needing to be sent atomically.</p>
<h3 id="faster-than-finality-transfers"><a class="header" href="#faster-than-finality-transfers">Faster-than-finality transfers</a></h3>
<p>One of the primary features of the WormholeRelayer protocol is that messages can be delivered faster than finality so long as the DeliveryProvider supports it. Normally the Token Bridge module can only transfer tokens once finality has been reached on a chain. However, with the WormholeRelayer protocol, you could potentially initiate two transfers in the same transaction.</p>
<ul>
<li>The first transfer sends funds instantly from a liqudity source, so that the end user receives their funds quickly.</li>
<li>The second transfer sends funds via the Token Bridge to reimburse the liquidity source on the <code>targetChain</code></li>
</ul>
<p>Beware, the second transfer may never arrive if there is a rollback on the <code>sourceChain</code>. However, this risk can be managed if the primary concern is to provide users with a smooth user experience.</p>
<br/>
<br/>
<div style="break-before: page; page-break-before: always;"></div><h1 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h1>
<p>The Wormhole contracts were designed in a manner such that composability is the default, but maximizing composability requires that xDapp developers follow certain conventions around the sending and receiving of messages.</p>
<h1 id="sending-messages-1"><a class="header" href="#sending-messages-1">Sending Messages</a></h1>
<p>When sending messages, you should follow the same paradigm as is used by the Wormhole modules, namely</p>
<ul>
<li>Don't couple the message emission to the message delivery</li>
<li>Pass through all the optional arguments (like nonce)</li>
<li>Always return the sequence</li>
</ul>
<h3 id="good-example"><a class="header" href="#good-example">Good Example</a></h3>
<pre><code class="language-solidity">// This function defines a super simple Wormhole 'module'.
// A module is just a piece of code which knows how to emit a composable message
// which can be utilized by other contracts.
function emitMyMessage(address intendedRecipient, uint32 nonce)
public payable returns (uint64 sequence) {
// Nonce is passed though to the core bridge.
// This allows other contracts to utilize it for batching or processing.
// intendedRecipient is key for composability!
// This field will allow the destination contract to enforce
// that the correct contract is submitting this VAA.
// Here we encode the payload into our wire format.
// There's basically always a corresponding decode/parse function as well.
bytes myMessage = abi.encode(&quot;My Message to &quot; + intendedRecipient);
// consistency level 200 means instant emission
sequence = core_bridge.publishMessage(nonce, myMessage, 200);
// The sequence is passed back to the caller, which can be useful relay information.
// Relaying is not done here, because it would 'lock' others into the same relay mechanism.
}
// This is the portion of the code which deals with composition and delivery.
// Its job is to string together multiple modules, and ensure they get relayed
// This code can be private or public, because it's tightly coupled to your application.
// Do whatever you need to here.
function sendMyMessage() public payable {
// First, emit a message intended for MY_OTHER_CONTRACT with nonce 1.
// Because processMyMessage enforces that msg.sender must equal the intendedRecipient,
// no one but MY_OTHER_CONTRACT will be able to call processMyMessage
// with the message emitted from this transaction.
// However, another contract could call emitMyMessage in a different transaction
// using their own address as the recipient.
// This allows for composability of the module logic while still being secure!
emitMyMessage(MY_OTHER_CONTRACT, 1);
// Suppose I also want to send tokens to my contract on the OTHER_CHAIN
// Because transferTokensWithPayload is a composable message, I can include it.
// Because the nonce of both these messages is 1, they will be combined into a batch VAA.
// NOTE: transferTokens (the basic transfer) is NOT considered a composable message
token_bridge.transferTokensWithPayload(SOME_TOKEN, SOME_AMOUNT, OTHER_CHAIN, MY_OTHER_CONTRACT,
1, null);
// Lastly, I request that the batch for nonce 1 be delivered to MY_OTHER_CONTRACT
ICoreRelayer.DeliveryRequest memory request = ICoreRelayer.DeliveryRequest(
OTHER_CHAIN, //targetChain
MY_OTHER_CONTRACT, //targetAddress
MY_OTHER_CONTRACT_CONTRACT, //refundAddress
msg.value, //computeBudget
0, //applicationBudget
relayer_contract.getDefaultRelayParams() //relayerParams
);
relayer.requestDelivery{value: msg.value}(
request, 1, relayer.getDefaultRelayProvider()
);
}
</code></pre>
<h1 id="receiving-messages-1"><a class="header" href="#receiving-messages-1">Receiving Messages</a></h1>
<p>The best practices for receiving messages employ similar concepts. You should keep in mind that other contracts might want to integrate with your specific logic. As such, you shouldn't tie your verification logic to the delivery mechanism of your VAAs, and you should also give external integrators a safe way to compose with your module.</p>
<h3 id="critical"><a class="header" href="#critical"><strong><em>Critical</em></strong></a></h3>
<ul>
<li>
<p>Always verify that the emitterAddress of the VAA comes from a contract you trust.</p>
</li>
<li>
<p>If the message should not be allowed to be 'replayed', immediately mark its hash as processed.</p>
</li>
<li>
<p>If your VAAs aren't replayable, you almost always want to include and enforce an intended recipient. Otherwise anyone can call your verify function directly with the single VAA, which will make life much harder for you and your integrators who want to process multiple VAAs at once. This is referred to as a 'scoop' exploit.</p>
</li>
</ul>
<h3 id="composability"><a class="header" href="#composability">Composability</a></h3>
<ul>
<li>When processing a VAA, always treat the messages as single VAAs. Destructuring batch VAAs is the responsibility of the integrator.</li>
<li>Once you have the function written to verify your message, pretend you are an external integrator.</li>
</ul>
<h3 id="good-example-1"><a class="header" href="#good-example-1">Good Example</a></h3>
<pre><code class="language-solidity">// Verification accepts a single VAA, and is publicly callable.
function processMyMessage(bytes32 memory VAA) public {
// This call accepts single VAAs and headless VAAs
(IWormhole.VM memory vm, bool valid, string memory reason) =
core_bridge.parseAndVerifyVM(VAA);
// Ensure core contract verification succeeded.
require(valid, reason);
// Ensure the emitterAddress of this VAA is a trusted address
require(myTrustedContracts[vm.emitterChainId] ==
vm.emitterAddress, &quot;Invalid Emitter Address!&quot;);
// Check that the VAA hasn't already been processed (replay protection)
require(!processedMessages[vm.hash], &quot;Message already processed&quot;);
// Check that the contract which is processing this VAA is the intendedRecipient
// If the two aren't equal, this VAA may have bypassed its intended entrypoint.
// This exploit is referred to as 'scooping'.
require(parseIntendedRecipient(vm.payload) == msg.sender);
// Add the VAA to processed messages so it can't be replayed
// you can alternatively rely on the replay protection
// of something like transferWithPayload from the Token Bridge module
processedMessages[vm.hash] = true
// The message content can now be trusted.
doBusinessLogic(vm.payload)
}
//This is the function which receives the the VAA from the CoreRelayer contract
function receiveWormholeMessages(bytes[] memory whMessages, bytes[] memory otherData)
public payable override onlyRelayerContract {
// I know from sendMyMessage that the first VAA should be a token bridge VAA,
// so let's hand that off to the token bridge module.
bytes vaaData = token_bridge.completeTransferWithPayload(whMessages[0]);
// The second VAA is my message, let's hand that off to my module.
processMyMessage(vm2.payloads[1]);
}
modifier onlyRelayerContract() {
require(msg.sender == CORE_RELAYER_CONTRACT_ADDRESS, &quot;msg.sender is not CoreRelayer contract.&quot;);
_;
}
</code></pre>
<!--
TODO these are not actually functioning examples and some of the interactions are incorrect. Demonstrates the concept.
>
<div style="break-before: page; page-break-before: always;"></div><h1 id="composable-verification"><a class="header" href="#composable-verification">Composable Verification</a></h1>
<p>Wormhole offers flexible <a href="technical/evm//wormhole/3_coreLayerContracts.html#consistency-levels">Consistency Levels</a>, but more advanced integrators may want additional layers of verification.</p>
<p>Working examples of the following are available in <a href="https://github.com/wormhole-foundation/example-composable-verification">this example repo</a>.</p>
<h2 id="additional-signers"><a class="header" href="#additional-signers">Additional Signers</a></h2>
<p>If your project has some additional off-chain processes or checks to perform <em>after</em> a message was emitted but <em>before</em> it could be consumed on the receiving chain, you may want to consider adding an additional signer requirement to your integration. There are plenty of ways to achieve this concept, but an approach like the following keeps the emission and verification in your integrating contract, entirely separate from Wormhole.</p>
<h3 id="1-emit-a-unique-message-hash"><a class="header" href="#1-emit-a-unique-message-hash">1. Emit a unique message hash</a></h3>
<pre><code class="language-solidity">event LogMessageHash(bytes32 hash);
...
bytes32 messageHash = keccak256(
abi.encodePacked(encodedMessage, messageSequence)
);
emit LogMessageHash(messageHash);
</code></pre>
<p>After calling <code>publishMessage</code>, emit a hash for the message and sequence number. This way, the signature will be unique for two different instances of the same message contents. You could also make it more unique across implementations by including the sending chain id and contract address.</p>
<h3 id="2-sign-the-hash"><a class="header" href="#2-sign-the-hash">2. Sign the hash</a></h3>
<pre><code class="language-solidity">function getSigningHash(bytes32 _messageHash) public view returns (bytes32) {
return
keccak256(abi.encodePacked(_messageHash, block.chainid, address(this)));
}
</code></pre>
<p>It is helpful to have a utility function on the receiving side so you can generate an even more unique hash which ensures the signature you generate is intended for this receiving chain and contract address.</p>
<pre><code class="language-typescript">const {
args: { hash },
} = sender.interface.parseLog(log);
const signingHash = await receiver.getSigningHash(hash);
const additionalSignature = await signer.signMessage(
ethers.utils.arrayify(signingHash)
);
</code></pre>
<p>Have your off-chain process pick up logs via your preferred method (like <code>finalized</code> block polling for <code>eth_getLogs</code>), perform its duties, and produce a signature.</p>
<h3 id="3-verify-the-signature"><a class="header" href="#3-verify-the-signature">3. Verify the signature</a></h3>
<pre><code class="language-solidity">function receiveMessage(
bytes memory _vaa,
bytes memory _additionalSignature
) public {
...
require(
verify(
keccak256(
abi.encodePacked(wormholeMessage.payload, wormholeMessage.sequence)
),
_additionalSignature
),
&quot;invalid additional signature&quot;
);
...
}
function verify(
bytes32 _messageHash,
bytes memory _signature
) public view returns (bool) {
bytes32 signingHash = getSigningHash(_messageHash);
bytes32 ethSignedMessageHash = getEthSignedMessageHash(signingHash);
return recoverSigner(ethSignedMessageHash, _signature) == signerAddress;
}
</code></pre>
<p>Add another parameter to your <code>receiveMessage</code> function and after calling <code>parseAndVerifyVM</code>, verify that the additional signature checks out!</p>
<h2 id="two-bridge-rule"><a class="header" href="#two-bridge-rule">Two-Bridge Rule</a></h2>
<p>This example sends a message from Ethereum to Optimism via Wormhole <em>and</em> sends the hash of that message via the <a href="https://community.optimism.io/docs/developers/bridge/messaging/">native bridge</a>. The receiver then requires both messages to agree, like requiring two keys to open a safe.</p>
<h3 id="1-send-a-unique-hash-natively"><a class="header" href="#1-send-a-unique-hash-natively">1. Send a unique hash natively</a></h3>
<pre><code class="language-solidity">/// Optimism L1-L2 bridge from https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli
address public crossDomainMessengerAddr =
0x5086d1eEF304eb5284A0f6720f79403b4e9bE294;
/// Optimism bridge requires a recipient address so the message can be relayed
address public receiverL2Addr;
...
// Send the expected message hash and sequence via the native bridge
bytes32 messageHash = keccak256(
abi.encodePacked(encodedMessage, messageSequence)
);
ICrossDomainMessenger(crossDomainMessengerAddr).sendMessage(
receiverL2Addr,
abi.encodeWithSignature(&quot;expectPayload(bytes32)&quot;, messageHash),
1000000 // within the free gas limit amount
);
</code></pre>
<p>Similar to the previous example, after calling <code>publishMessage</code>, send a hash for the message and sequence number over the native bridge.</p>
<h3 id="2-receive-the-expected-hash"><a class="header" href="#2-receive-the-expected-hash">2. Receive the expected hash</a></h3>
<pre><code class="language-solidity">/// Sender contract address for confirming validity of native bridge messages
address public immutable l1SenderAddress;
/// Stores the expected payload hash
bytes32 public expectedPayloadHash;
/// Used by the native bridge to set the expected payload hash
/// This signature must match the ICrossDomainMessenger.sendMessage call in the Sender
/// @param _expectedPayloadHash The hash of the expected payload for the corresponding Wormhole message
function expectPayload(bytes32 _expectedPayloadHash) public {
require(getXorig() == l1SenderAddress, &quot;invalid sender&quot;);
expectedPayloadHash = _expectedPayloadHash;
}
</code></pre>
<p>Again similar to a basic Wormhole integration where you <a href="technical/evm/./bestPractices.html#receiving-messages">verify the emitter</a>, verify that this message came from the expected L1 contract. This example only “expects” one message at a time, but you could just as easily make this a map like <code>processedMessages</code>.</p>
<h3 id="3-verify-the-hashes-match"><a class="header" href="#3-verify-the-hashes-match">3. Verify the hashes match</a></h3>
<pre><code class="language-solidity">require(
keccak256(
abi.encodePacked(wormholeMessage.payload, wormholeMessage.sequence)
) == expectedPayloadHash,
&quot;unexpected payload&quot;
);
</code></pre>
<p>After calling <code>parseAndVerifyVM</code>, verify that the hash checks out!</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="solana-1"><a class="header" href="#solana-1">Solana</a></h1>
<p>The implementation contracts for Wormhole's official Solana integration can be found <a href="https://github.com/wormhole-foundation/wormhole/tree/main/solana">here</a>.</p>
<p>The deployed contract addresses can be found on the <a href="technical/solana/../../reference/contracts.html">Contracts page</a>.</p>
<p>Solana is a rust-based programming environment. You'll likely find the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/rust">Wormhole Rust SDK</a> useful.</p>
<p>The Solana integration is also fully supported by the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/js">Wormhole Typescript SDK</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="cosmos-1"><a class="header" href="#cosmos-1">Cosmos</a></h1>
<p>The implementation contracts for Wormhole's official Cosmos integration can be found <a href="https://github.com/wormhole-foundation/wormhole/tree/main/cosmwasm">here</a>.</p>
<p>The deployed contract addresses can be found on the <a href="technical/cosmos/../../reference/contracts.html">Contracts page</a>.</p>
<p>Cosmos is a rust-based programming environment. You'll likely find the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/rust">Wormhole Rust SDK</a> useful.</p>
<p>The Cosmos integration is also fully supported by the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/js">Wormhole Typescript SDK</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="algorand-1"><a class="header" href="#algorand-1">Algorand</a></h1>
<p>The implementation contracts for Wormhole's official Algorand integration can be found <a href="https://github.com/wormhole-foundation/wormhole/tree/main/algorand">here</a>.</p>
<p>An overview of the contracts and how they work is outlined <a href="https://github.com/wormhole-foundation/wormhole/blob/main/algorand/README.md">here</a>.</p>
<p>The deployed contract addresses can be found on the <a href="technical/algorand/../../reference/contracts.html">Contracts page</a>.</p>
<p>The Algorand integration is also fully supported by the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/js">Wormhole Typescript SDK</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="near-1"><a class="header" href="#near-1">NEAR</a></h1>
<p>The implementation contracts for Wormhole's official NEAR integration can be found <a href="https://github.com/wormhole-foundation/wormhole/tree/main/near">here</a>.</p>
<p>The deployed contract addresses can be found on the <a href="technical/near/../../reference/contracts.html">Contracts page</a>.</p>
<p>NEAR is a rust-based programming environment. You'll likely find the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/rust">Wormhole Rust SDK</a> useful.</p>
<p>The NEAR integration is also fully supported by the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/js">Wormhole Typescript SDK</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="aptos-1"><a class="header" href="#aptos-1">Aptos</a></h1>
<p>Aptos is currently only supported in testnet.</p>
<p>The official Wormhole integration can be found <a href="https://github.com/wormhole-foundation/wormhole/tree/aptos/integration">here</a>.</p>
<p>Information for how to integrate with these contracts can be found <a href="https://github.com/wormhole-foundation/wormhole/tree/aptos/integration/aptos/README.md">here</a>.</p>
<p>The deployed contract addresses can be found on the <a href="technical/aptos/../../reference/contracts.html">Contracts page</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="relayer-development"><a class="header" href="#relayer-development">Relayer Development</a></h1>
<p>This chapter will help you with relayer development in the Wormhole ecosystem.</p>
<p>The two broad categorizations of relayers are <a href="technical/relayer/./genericRelayer.html">Generic Relayers</a> and <a href="technical/relayer/./specializedRelayers.html">Specialized Relayers</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="generic-relayers-2"><a class="header" href="#generic-relayers-2">Generic Relayers</a></h1>
<p>The defining characteristic of generic relayers is that they do not have any off-chain components for the xDapp developer. All aspects of this integration are on chain.</p>
<p>The implementation details vary by blockchain so you should reference the <code>relayer module</code> documentation for each ecosystem. However, the general workflow is the same.</p>
<p>Developers are responsible for implementing a standardized interface which is part of the API agreement with the generic relayer network. This interface generally looks something like</p>
<pre><code>wormholeReceiver(
bytes[] batchVAA,
sourceChain
sourceAddress
payload)
</code></pre>
<p>This is the entrypoint on your contract which will be called by the relayer.</p>
<p>You are able to request delivery of a VAA via calling the <code>relayer module</code> on-chain. As part of the delivery request, you are required to specify and pre-pay a 'future compute' budget, which will designate a limit for how much budget can be spent on the target transaction.</p>
<p>This interface is generally along the lines of:</p>
<pre><code>requestDelivery(
targetChain,
targetAddress,
computeBudget,
nonce,
consistencyLevel,
)
</code></pre>
<p>If the requested delivery either runs out of compute budget or throws an exception, the delivery will fail. In the case of a delivery failure, you're always able to request a second delivery. However, the prepaid fee is not refunded. Thus, it is recommended to always place a top-level try-catch around your entrypoint, and to specify a worst-case computation budget. <!-- TODO mention refunds --></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="specialized-relayers-2"><a class="header" href="#specialized-relayers-2">Specialized Relayers</a></h1>
<p>Rather than home-rolling a relayer, it's recommended that integrators start from the existing <a href="https://github.com/wormhole-foundation/wormhole/tree/main/relayer/spy_relayer">Spy Relayer</a> provided in the Wormhole Core Repository.</p>
<p>Additionally there's an extensible relayer (called the <a href="https://github.com/wormhole-foundation/wormhole/tree/feat/plugin_relayer/relayer/plugin_relayer">Plugin Relayer</a>) currently in development.</p>
<!-- To aid in the development of relayers, a extensible relayer implementation (called the [plugin relayer]()) has been provided in the Wormhole Core Repository.
It's recommended that integrators create their own plugin for the plugin relayer, rather than home-roll a relayer themselves. Using the plugin relayer allows integrators to take advantage of the robust hot-wallet and scheduling built into the relayer's kernel, as well as leveraging plugins which are built by other integrators. -->
<div style="break-before: page; page-break-before: always;"></div><h1 id="wormhole-typescript-sdk-1"><a class="header" href="#wormhole-typescript-sdk-1">Wormhole Typescript SDK</a></h1>
<p>A Wormhole Typescript SDK provided for applications that only need to interact with the Core and Token Bridge contracts off-chain.</p>
<p>It can be installed using npm:</p>
<pre><code class="language-sh">npm i @certusone/wormhole-sdk
</code></pre>
<p>The following sections will explain and provide examples of how to perform key functions with Wormhole using the Typescript SDK.</p>
<p>For more examples with a more exhaustive coverage of all the supported blockchains in Wormhole, be sure to check the <a href="https://github.com/wormhole-foundation/wormhole/tree/main/sdk/js">official codebase</a> for the Typescript SDK.</p>
<p>Virtually all functions of the SDK are demonstrated in the <a href="https://github.com/wormhole-foundation/example-token-bridge-ui">reference bridge UI</a>, which makes it an excellent source of example code as well.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="registering-tokens"><a class="header" href="#registering-tokens">Registering Tokens</a></h1>
<p>Registering tokens with the token bridge can be done from any supported blockchain, and only needs to be done once - globally - per token. This is typically done via a UI (such as <a href="technical/typescript/portalbridge.com">Portal</a>) rather than done on-chain.</p>
<p>If you need to do it programmatically, you can also use the Typescript SDK to attest a token.</p>
<p>There are three steps to registering a token:</p>
<ol>
<li>Create an AttestMeta VAA by calling <code>attest()</code> function from the SDK and passing in the Token Bridge address, and the address of the Token we want to attest.</li>
</ol>
<p>For example, here is the code to produce an attestation VAA using ethers:</p>
<pre><code class="language-js">const networkTokenAttestation = await attestFromEth(
network.tokenBridgeAddress, // Token Bridge Address
signer, //Private Key to sign and pay for TX + RPC Endpoint
network.testToken //Token Address
);
</code></pre>
<p>The attestation transaction will produce a signed VAA. This signed VAA is necessary in order to register the tokens on other chains.</p>
<ol start="2">
<li>Retrieve the VAA with the <code>emitterAddress</code> of the Token Bridge and the <code>sequence</code> from the logs of the transaction receipt.</li>
</ol>
<p>With those, you can fetch the VAA from any Guardian REST endpoint. It could take a moment (up to 30 seconds) for the Guardian to see and sign the VAA, so it's a good idea to poll the Guardian every few seconds until the VAA is found.</p>
<p>Here is a relatively compact piece of code which is able to fetch <strong>any</strong> VAA, given an emitter address and sequence number.</p>
<pre><code class="language-js">const emitterAddr = getEmitterAddressEth(network.tokenBridgeAddress);
const seq = parseSequenceFromLogEth(
networkTokenAttestation,
network.bridgeAddress
);
const vaaURL = `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`;
console.log(&quot;Searching for: &quot;, vaaURL);
let vaaBytes = await (await fetch(vaaURL)).json();
while (!vaaBytes.vaaBytes) {
console.log(&quot;VAA not found, retrying in 5s!&quot;);
await new Promise((r) =&gt; setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
</code></pre>
<ol start="3">
<li>Submit the VAA onto the target chain to create a wrapped version of the token by calling <code>createWrapped()</code>.</li>
</ol>
<p>You can get the new wrapped token address by calling the <code>wrappedAsset()</code> function of the TokenBridge.</p>
<p>Here is how this can be accomplished using Ethers:</p>
<pre><code class="language-js">await targetTokenBridge.createWrapped(
Buffer.from(vaaBytes.vaaBytes, &quot;base64&quot;),
{
gasLimit: 2000000,
}
);
await new Promise((r) =&gt; setTimeout(r, 5000)); //Time out to let block propagate
const wrappedTokenAddress = await targetTokenBridge.wrappedAsset(
network.wormholeChainId,
Buffer.from(tryNativeToHexString(network.testToken, &quot;ethereum&quot;), &quot;hex&quot;)
);
console.log(&quot;Wrapped token created at: &quot;, wrappedTokenAddress);
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="token-transfers"><a class="header" href="#token-transfers">Token Transfers</a></h1>
<!-- //TODO this information should be captured elsewhere One challenge that arises for new EVM developers is that, because EVM uses unsigned integers, there's no concept of decimals. Therefore, tokens usually have up to 18 zeros behind them to denote up to 18 decimal places. Wormhole normalizes this to *eight* zeros, with transfer amounts rounded down to the nearest 8th decimal. -->
<p>Before transferring tokens, you should ensure that the token is <a href="technical/typescript/./attestingToken.html">registered</a> on the chain you are transferring to, and that any necessary prerequisite steps (such as sending token approvals or creating associated token accounts) have already been done.</p>
<p>There are four steps to transferring a token:</p>
<ol>
<li>If not already done, complete a standard ERC-20 token approval prior to performing a bridge action if you're in the EVM ecosystem.</li>
</ol>
<pre><code class="language-js">// Here we are approving and transferring 50 tokens. The ERC20 token we are transferring has 18 decimal places.
const bridgeAmt = ethers.utils.parseUnits(&quot;50&quot;, &quot;18&quot;);
await treasury.approveTokenBridge(bridgeAmt, {
gasLimit: 2000000,
});
</code></pre>
<ol start="2">
<li>Initate a transfer by calling <code>transfer</code> on the token bridge module which will create a transfer VAA. </li>
</ol>
<p><em>Note that the target receipient is a Wormhole-format address (referred to as 'hex' format in the Typescript SDK).</em></p>
<pre><code class="language-js">const targetRecepient = Buffer.from(
tryNativeToHexString(targetDeployment.deployedAddress, &quot;ethereum&quot;),
&quot;hex&quot;
);
const tx = await (
await treasury.bridgeToken(
bridgeAmt,
targetNetwork.wormholeChainId,
targetRecepient
)
).wait();
</code></pre>
<ol start="3">
<li>Retrieve the VAA with the <code>emitterAddress</code> of the Token Bridge and the <code>sequence</code> from the logs of the transaction receipt. (This is the same code as shown in the previous section.)</li>
</ol>
<pre><code class="language-js">const emitterAddr = getEmitterAddressEth(network.tokenBridgeAddress);
const seq = parseSequenceFromLogEth(tx, network.bridgeAddress);
const vaaURL = `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`;
let vaaBytes = await (await fetch(vaaURL)).json();
while (!vaaBytes.vaaBytes) {
console.log(&quot;VAA not found, retrying in 5s!&quot;);
await new Promise((r) =&gt; setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
</code></pre>
<ol start="4">
<li>Submit the VAA to the target chain by calling <code>completeTransfer()</code>.</li>
</ol>
<p>If you're not using a relayer, you'll have to submit the target chain transaction yourself. <a href="technical/typescript/./polygon-oasis-relayer.html">This section</a> outlines how to use relayers.</p>
<pre><code class="language-js">const completeTransferTx = await targetTokenBridge.completeTransfer(
Buffer.from(vaaBytes.vaaBytes, &quot;base64&quot;)
);
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="cross-ecosystem-token-transfer"><a class="header" href="#cross-ecosystem-token-transfer">Cross-Ecosystem Token Transfer</a></h1>
<p>A defining feature of cross chain apps (xDapps) is the ability to move tokens from one chain to another, even if those blockchains have radically different virtual machine models.</p>
<p>To demonstrate, lets do a simple programmatic transfer from Ethereum to Solana.</p>
<p>First, determine the address on Solana where we're sending the tokens. Unlike EVM chains where the wallet address is used, we need to send the tokens to the recipient's associated token account for that token. We'll use a couple helper functions from the Wormhole Typescript SDK to make this easier.</p>
<pre><code class="language-ts">import {
Token,
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
} from &quot;@solana/spl-token&quot;;
import {
getForeignAssetSolana,
hexToUint8Array,
nativeToHexString,
CHAIN_ID_ETH,
} from &quot;@certusone/wormhole-sdk&quot;;
const SOLANA_TOKEN_BRIDGE_ADDRESS =
&quot;wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb&quot;;
// determine destination address - an associated token account
const solanaMintKey = new PublicKey(
(await getForeignAssetSolana(
connection,
SOLANA_TOKEN_BRIDGE_ADDRESS,
CHAIN_ID_ETH,
hexToUint8Array(nativeToHexString(tokenAddress, CHAIN_ID_ETH) || &quot;&quot;)
)) || &quot;&quot;
);
const recipientAddress = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
solanaMintKey,
recipientWalletAddress
);
</code></pre>
<p>After we have the receipt token account on Solana, it's time to submit the transfer message on Ethereum. This will output a log that contains a sequence number (a unique number for the message) and an emitter address (the ETH Token Bridge Address in Wormhole format). The sequence number and emitter address will be used to fetch the VAA after its been signed by Guardians.</p>
<pre><code class="language-ts">import {
trasnferFromEth,
parseSequenceFromLogEth,
getEmitterAddressEth,
CHAIN_ID_SOLANA,
} from &quot;@certusone/wormhole-sdk&quot;;
const ETH_TOKEN_BRIDGE_ADDRESS = &quot;0x3ee18B2214AFF97000D974cf647E7C347E8fa585&quot;;
// Submit transaction - results in a Wormhole message being published
const receipt = await transferFromEth(
ETH_TOKEN_BRIDGE_ADDRESS,
signer,
tokenAddress,
amount,
CHAIN_ID_SOLANA,
recipientAddress
);
// Get the sequence number and emitter address required to fetch the signedVAA of our message
const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
</code></pre>
<p>Once the Guardians have signed the token bridge VAA, it needs to be retrieved from the Guardian Network. This time we'll use the Guardian GRPC endpoint, though the REST endpoint used in previous sections works as well.</p>
<pre><code class="language-ts">import { getSignedVAA } from &quot;@certusone/wormhole-sdk&quot;;
// Fetch the signedVAA from the Wormhole Network (this may require retries while you wait for confirmation)
const { signedVAA } = await getSignedVAA(
WORMHOLE_RPC_HOST,
CHAIN_ID_ETH,
emitterAddress,
sequence
);
</code></pre>
<p>Now, we post the VAA to Solana in order to mint the wrapped tokens. Because of the compute limit on Solana, we split the signature verification and token claim into steps. To do that, verify all the signatures and create a claim account for the token.</p>
<pre><code class="language-ts">const SOL_BRIDGE_ADDRESS = &quot;worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth&quot;;
await postVaaSolana(
connection, // Solana Mainnet Connection
wallet, //Solana Wallet Signer
SOL_BRIDGE_ADDRESS,
payerAddress,
signedVAA
);
</code></pre>
<p>Finally, claim the tokens:</p>
<pre><code class="language-ts">const transaction = await redeemOnSolana(
connection,
SOL_BRIDGE_ADDRESS,
SOL_TOKEN_BRIDGE_ADDRESS,
payerAddress,
signedVAA,
isSolanaNative,
mintAddress
);
const signed = await wallet.signTransaction(transaction);
const txid = await connection.sendRawTransaction(signed.serialize());
await connection.confirmTransaction(txid);
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="using-relayers"><a class="header" href="#using-relayers">Using Relayers</a></h1>
<p>In this example, well utilize the token bridge relayer network to complete a token transfer from Polygon to Oasis.</p>
<p>This code is written for a browser environment. If you're working in node, consider using node-fetch:</p>
<pre><code class="language-bash">npm i --save @certusone/wormhole-sdk ethers node-fetch
</code></pre>
<pre><code class="language-ts">import { BigNumber, ethers } from &quot;ethers&quot;;
import fetch from &quot;node-fetch&quot;;
import {
getEmitterAddressEth,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
CHAIN_ID_POLYGON,
CHAIN_ID_OASIS,
transferFromEthNative,
getIsTransferCompletedEth,
setDefaultWasm,
} from &quot;@certusone/wormhole-sdk&quot;;
</code></pre>
<h3 id="setup-the-polygon-and-oasis-wallets"><a class="header" href="#setup-the-polygon-and-oasis-wallets">Setup the Polygon and Oasis Wallets</a></h3>
<p>Now, set up the two wallets well be sending and receiving from. While we are instantiating both wallets with their private keys, we only need the Public key of the receiving wallet for this example.</p>
<pre><code class="language-ts">const EmeraldWallet = new ethers.Wallet(
privatekey_emerald,
new ethers.providers.JsonRpcProvider(&quot;https://emerald.oasis.dev&quot;)
);
const PolygonWallet = new ethers.Wallet(
privatekey_polygon,
new ethers.providers.JsonRpcProvider(&quot;https://polygon-rpc.com/&quot;)
);
</code></pre>
<h3 id="fetch-the-fee-schedule"><a class="header" href="#fetch-the-fee-schedule">Fetch the fee schedule</a></h3>
<p>Fetch the fee schedule for the token bridge relayers. This fee schedule outlines the minimum fee for each recipient chain that relayers will accept. As long as we attach at least that fee in the relayer fee, we can expect a relayer pick up the transaction and relay it to the recipient chain. The fee will cover the gas cost for the relayer along with a little extra to make it worth their time to run the relayer service.</p>
<p>We will also define the transfer amount in this step. The fee schedule will either return a flat fee in USD for the recipient chain, or a percentage fee (usually only for Ethereum). Either way, well need to calculate the fee in BigNumber format (no decimals).</p>
<pre><code class="language-ts">const transferAmount = BigNumber.from(&quot;1000000000000000000&quot;); // We are sending 1 MATIC over the wall to Oasis
const relayerFeeSchedule = await(
await fetch(
&quot;https://raw.githubusercontent.com/certusone/wormhole-relayer-list/main/relayer.json&quot;
)
).json();
</code></pre>
<p>The fee schedule has the following interface:</p>
<pre><code class="language-ts">export interface RelayerFeeSchedule {
supportedTokens: ChainAddress[];
relayers: Relayer[];
feeSchedule: FeeSchedule;
}
interface ChainAddress {
chainId: number;
address: string;
coingeckoId: string;
}
interface Relayer {
name: string;
url: string;
}
interface FeeSchedule {
[chainId: string]: {
type: &quot;flat&quot; | &quot;percent&quot;;
feeUsd?: number;
feePercent?: number;
gasEstimate?: number;
};
}
</code></pre>
<p>After fetching the fee schedule, find the fee in wei that needs to be paid to the Relayer. At the time of writing, Oasis has a flat fee of $0.50, so to calculate how much MATIC we need to pay for the $0.50 fee, we need to fetch the MATIC price. To do that, use the free CoinGecko api:</p>
<pre><code class="language-ts">let feeWei: number;
if (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].type == &quot;flat&quot;) {
const feeUsd = relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feeUsd;
const MATIC_PRICE = await(
await fetch(
&quot;https://api.coingecko.com/api/v3/simple/token_price/polygon-pos?contract_addresses=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270&amp;vs_currencies=usd&quot;
)
).json()[&quot;0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270&quot;][&quot;usd&quot;];
feeWei = (feeUsd / MATIC_PRICE) * 1e18;
} else if (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].type == &quot;percent&quot;) {
let feeWei =
(relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feePercent / 100) *
transferAmount.toNumber();
}
</code></pre>
<h3 id="overrides--quirks"><a class="header" href="#overrides--quirks">Overrides &amp; Quirks</a></h3>
<p>Dependent on the specific blockchains you are working with, you may need to perform special actions when submitting this transaction. Because we're dealing with Polygon in this example, there's an additional step to overestimate the gas. This is because Ethers library has some problems with fee estimation after EIP-1559.</p>
<pre><code class="language-ts">let overrides;
let feeData = await PolygonWallet.provider.getFeeData();
overrides = {
maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(50) || undefined,
};
</code></pre>
<h3 id="emit-token-bridge-message"><a class="header" href="#emit-token-bridge-message">Emit Token Bridge Message</a></h3>
<p>Now we have all the pieces we need to emit a token bridge message with a relay fee attached. We do this using the transferFromEthNative() method. EthNative is used because were transferring the native token of the Polygon network rather than an ERC20 token.</p>
<pre><code class="language-ts">const POLYGON_TOKEN_BRIDGE = &quot;0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE&quot;;
const receipt = await transferFromEthNative(
POLYGON_TOKEN_BRIDGE,
PolygonWallet,
transferAmount,
CHAIN_ID_OASIS,
hexToUint8Array(
nativeToHexString(await EmeraldWallet.getAddress(), CHAIN_ID_OASIS) || &quot;&quot;
),
BigNumber.from(feeWei.toString()),
overrides
);
console.log(&quot;Receipt: &quot;, receipt);
const POLYGON_CORE_BRIDGE_ADDRESS =
&quot;0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7&quot;;
const sequence = parseSequenceFromLogEth(receipt, POLYGON_CORE_BRIDGE_ADDRESS);
const emitterAddress = getEmitterAddressEth(POLYGON_TOKEN_BRIDGE);
console.log(&quot;Sequence: &quot;, sequence);
console.log(&quot;EmitterAddress: &quot;, emitterAddress);
</code></pre>
<p>Lets walk through each of the arguments of this function and what they mean.</p>
<p><code>POLYGON_TOKEN_BRIDGE</code> is the address of the token bridge module on the Polygon network. You can find it and other addresses on the <a href="technical/typescript/../../reference/contracts.html">contracts</a> page.</p>
<p><code>PolygonWallet</code> is a signer you get from the Ethers library that holds a private key that can sign transactions.</p>
<p><code>transferAmount</code> is a BigNumber that contains the amount to transfer in the smallest unit of the network.</p>
<p><code>CHAIN_ID_OASIS</code> is a constant that identifies the target chain.</p>
<p><code>hexToUint8Array()</code> translates the target publickey into a wormhole public key.</p>
<p><code>BigNumber.from(feeWei.toString())</code> identifies the fee in smallest unit of the network for the relayer.</p>
<p><code>overrides</code> are used if we need to override the gas cost, which we need to do for Polygon.</p>
<h3 id="check-vaa-was-signed"><a class="header" href="#check-vaa-was-signed">Check VAA was signed</a></h3>
<p>Wait 15 min for finality on Polygon and then check to see if it was submitted. If successful, youll be able to fetch a base64 encoded vaaBytes. We need this in the next step where we check if the transaction was successfully relayed.</p>
<pre><code class="language-ts">await new Promise((r) =&gt; setTimeout(r, 900000)); //15m in seconds
const WORMHOLE_RPC = &quot;https://wormhole-v2-mainnet-api.certus.one&quot;;
let vaaBytes = undefined;
while (!vaaBytes) {
try {
vaaBytes = await(
await fetch(
`${WORMHOLE_RPC}/v1/signed_vaa/${CHAIN_ID_POLYGON}/${emitterAddress}/${sequence}`
)
).json().vaaBytes;
} catch (e) {
await new Promise((r) =&gt; setTimeout(r, 5000));
}
}
console.log(&quot;VAA Bytes: &quot;, vaaBytes);
</code></pre>
<h3 id="check-if-the-transfer-was-completed"><a class="header" href="#check-if-the-transfer-was-completed">Check if the transfer was completed</a></h3>
<p>In the final step, use the getIsTransferCompletedEth() method to check if the transfer was completed on the Oasis Emerald chain. If its not, wait 5 seconds and check again.</p>
<pre><code class="language-ts">setDefaultWasm(&quot;node&quot;); //only needed if running in node.js
const EMERALD_TOKEN_BRIDGE = &quot;0x5848C791e09901b40A9Ef749f2a6735b418d7564&quot;;
let transferCompleted = await getIsTransferCompletedEth(
EMERALD_TOKEN_BRIDGE,
EmeraldWallet.provider,
vaaBytes
);
while (!transferCompleted) {
await new Promise((r) =&gt; setTimeout(r, 5000));
transferCompleted = await getIsTransferCompletedEth(
EMERALD_TOKEN_BRIDGE,
EmeraldWallet.provider,
vaaBytes
);
}
console.log(&quot;VAA Relayed!&quot;);
</code></pre>
<p>Success! You've programmatically relayed a transaction!</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="other-resources"><a class="header" href="#other-resources">Other Resources</a></h1>
<p>Here is a collection of other resources and reference sources which you're likely to find helpful.</p>
<ul>
<li><a href="reference/./glossary.html">Glossary &amp; Terms</a></li>
<li><a href="reference/./usefulLinks.html">Tools &amp; Helpful Links</a></li>
<li><a href="reference/./contracts.html">Contract Addresses &amp; Environment Information</a></li>
<li><a href="reference/./rpcnodes.html">RPC Info</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="glossary"><a class="header" href="#glossary">Glossary</a></h1>
<p><em>Disclaimer: In some instances, Wormhole uses general terms for decentralized, cross-chain elements as branded verbiage. In most cases, the definition of the general term does not differ from Wormhole's definition though Wormhole's definitions may be more narrow than general interpretations.</em></p>
<p><strong>Guardian</strong> - One of the 19 validators in the Guardian Network that contributes to the VAA multisig.</p>
<p><a href="reference/../wormhole/5_guardianNetwork.html"><strong>Guardian Network</strong></a> - Validators that exist in their own p2p network that serve as Wormhole's oracle by observing activity on-chain and generating signed messages attesting to that activity.</p>
<p><a href="reference/../wormhole/6_relayers.html"><strong>Relayer</strong></a> - Any process which delivers VAAs to a destination.</p>
<p><a href="reference/../wormhole/4_vaa.html"><strong>VAA</strong></a> - Verifiable Action Approvals (VAAs) are the key piece of data in the Wormhole ecosystem, containing the messages emitted by xDapps along with information such as what contract emitted the message. The VAAs are signed by the Guardians and need 13/19 signatures to be considered authentic.</p>
<p><a href="reference/../wormhole/8_wormchain.html"><strong>Wormchain</strong></a> - A purpose-built cosmos blockchain which aids the Guardian Network and allows for formal interaction with the Guardians.</p>
<p><a href="reference/../dapps/3_xdataxassets.html"><strong>xAssets</strong></a> - Chain-and-path agnostic token that exists on a layer outside the blockchain ecosystem, which can be used to conduct transactions on any blockchain. There are currently two implemented modules: (1) <a href="reference/../technical/evm/tokenLayer.html">Token Bridge Module</a> and (2) <a href="reference/../technical/evm/nftLayer.html">NFT Bridge Module</a></p>
<p><strong>xChain</strong> - Term that refers to the full range of cross-blockchain interoperability.</p>
<p><a href="reference/../dapps/4_whatIsanXdapp.html"><strong>xDapp</strong></a> - Decentralized application that enables users to create and/or use xData.</p>
<p><a href="reference/../dapps/3_xdataxassets.html"><strong>xData</strong></a> - Data that exists in a layer outside of Layer 1 blockchains, which is accessible by all chains.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="tools-and-useful-links"><a class="header" href="#tools-and-useful-links">Tools and Useful Links</a></h1>
<p>Below are a variety of tools and information in the Wormhole ecosystem that can help you develop xDapps.</p>
<h3 id="design-documents"><a class="header" href="#design-documents"><a href="https://github.com/certusone/wormhole/tree/main/whitepapers">Design Documents</a></a></h3>
<p>Wormhole's component design specifications outline the reasoning behind design decisions with added technical depth.</p>
<h3 id="testnet-1"><a class="header" href="#testnet-1">Testnet</a></h3>
<p>Wormhole has deployed Core Bridge, Token Bridge and NFT Bridge contracts on various testnets of the chains connected by Wormhole. You can see the deployed addresses <a href="reference/./contracts.html">here</a>. </p>
<p><em>Note: There's only a single Guardian that oversees the testnets, so you might experience a higher rate of missed VAAs than you would on mainnet.</em></p>
<h3 id="testnet-bridge-ui"><a class="header" href="#testnet-bridge-ui"><a href="https://wormhole-foundation.github.io/example-token-bridge-ui/#/transfer">Testnet Bridge UI</a></a></h3>
<p>An example UI provided to test out attesting and bridging tokens on testnet.</p>
<h3 id="tilt"><a class="header" href="#tilt">Tilt</a></h3>
<p>Tilt is a Kubernetes-based tool that runs a copy of every chain along side a Guardian node to create a simulated testing environment. Details on how to set it up and test against it is <a href="reference/../development/tilt/overview.html">here</a>.</p>
<h3 id="wormhole-core-repository-1"><a class="header" href="#wormhole-core-repository-1">Wormhole Core Repository</a></h3>
<p>The Wormhole core repository can be found at <a href="https://github.com/wormhole-foundation/wormhole">https://github.com/wormhole-foundation/wormhole</a>.</p>
<h3 id="wormhole-explorer"><a class="header" href="#wormhole-explorer"><a href="https://wormholenetwork.com/explorer">Wormhole Explorer</a></a></h3>
<p>Tool to observe all Wormhole activity and can help you parse VAAs after they've been picked up the Guardian network.</p>
<h3 id="wormhole-sdk"><a class="header" href="#wormhole-sdk">Wormhole SDK</a></h3>
<p>The SDK is a set of Javascript tools to help you do Token Bridge transfers, plus fetch and submit VAAs from one chain to another. You can install it via <a href="https://www.npmjs.com/package/@certusone/wormhole-sdk">NPM</a>.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="contracts"><a class="header" href="#contracts">Contracts</a></h1>
<p>Here you can find the addresses for the deployed contracts on all the chains that Wormhole supports, including testnet.</p>
<p>The <a href="https://github.com/wormhole-foundation/wormhole/blob/main/sdk/js/src/utils/consts.ts">constants</a> of the Wormhole SDK always has the most up-to-date contract addresses, along with additional useful constants. Check there if something you're looking for isn't found here. Note that the Sui contract addresses are the contract state object IDs and not the package IDs. For an overview of objects on Sui, please see the <a href="https://docs.sui.io/learn/objects">docs</a>.</p>
<h2 id="mainnet"><a class="header" href="#mainnet">Mainnet</a></h2>
<h3 id="core-bridge"><a class="header" href="#core-bridge">Core Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left">mainnet-beta</td><td style="text-align: left">worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth</td></tr>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left">1</td><td style="text-align: left">0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B</td></tr>
<tr><td style="text-align: left">Terra Classic</td><td style="text-align: left">3</td><td style="text-align: left">columbus-5</td><td style="text-align: left">terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">56</td><td style="text-align: left">0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B</td></tr>
<tr><td style="text-align: left">Polygon</td><td style="text-align: left">5</td><td style="text-align: left">137</td><td style="text-align: left">0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7</td></tr>
<tr><td style="text-align: left">Avalanche (C-Chain)</td><td style="text-align: left">6</td><td style="text-align: left">43114</td><td style="text-align: left">0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c</td></tr>
<tr><td style="text-align: left">Oasis (Emerald)</td><td style="text-align: left">7</td><td style="text-align: left">4262</td><td style="text-align: left">0xfE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585</td></tr>
<tr><td style="text-align: left">Algorand</td><td style="text-align: left">8</td><td style="text-align: left"></td><td style="text-align: left">842125965</td></tr>
<tr><td style="text-align: left">Aurora</td><td style="text-align: left">9</td><td style="text-align: left">1313161554</td><td style="text-align: left">0xa321448d90d4e5b0A732867c18eA198e75CAC48E</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">250</td><td style="text-align: left">0x126783A6Cb203a3E35344528B26ca3a0489a1485</td></tr>
<tr><td style="text-align: left">Karura</td><td style="text-align: left">11</td><td style="text-align: left">686</td><td style="text-align: left">0xa321448d90d4e5b0A732867c18eA198e75CAC48E</td></tr>
<tr><td style="text-align: left">Acala</td><td style="text-align: left">12</td><td style="text-align: left">787</td><td style="text-align: left">0xa321448d90d4e5b0A732867c18eA198e75CAC48E</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">8217</td><td style="text-align: left">0x0C21603c4f3a6387e241c0091A7EA39E43E90bb7</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">42220</td><td style="text-align: left">0xa321448d90d4e5b0A732867c18eA198e75CAC48E</td></tr>
<tr><td style="text-align: left">NEAR</td><td style="text-align: left">15</td><td style="text-align: left"></td><td style="text-align: left">contract.wormhole_crypto.near</td></tr>
<tr><td style="text-align: left">Moonbeam</td><td style="text-align: left">16</td><td style="text-align: left">1284</td><td style="text-align: left">0xC8e2b0cD52Cf01b0Ce87d389Daa3d414d4cE29f3</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">18</td><td style="text-align: left">phoenix-1</td><td style="text-align: left">terra12mrnzvhx3rpej6843uge2yyfppfyd3u9c3uq223q8sl48huz9juqffcnh</td></tr>
<tr><td style="text-align: left">Injective</td><td style="text-align: left">19</td><td style="text-align: left"></td><td style="text-align: left">inj17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9l2q74d</td></tr>
<tr><td style="text-align: left">Sui</td><td style="text-align: left">21</td><td style="text-align: left"></td><td style="text-align: left">0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c</td></tr>
<tr><td style="text-align: left">Aptos</td><td style="text-align: left">22</td><td style="text-align: left"></td><td style="text-align: left">0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left"></td><td style="text-align: left">0xa5f208e072434bC67592E4C49C1B991BA79BCA46</td></tr>
<tr><td style="text-align: left">Optimism</td><td style="text-align: left">24</td><td style="text-align: left">10</td><td style="text-align: left">0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722</td></tr>
<tr><td style="text-align: left">XPLA</td><td style="text-align: left">28</td><td style="text-align: left"></td><td style="text-align: left">xpla1jn8qmdda5m6f6fqu9qv46rt7ajhklg40ukpqchkejcvy8x7w26cqxamv3w</td></tr>
</tbody></table>
<h4 id="core-bridge---read-only"><a class="header" href="#core-bridge---read-only">Core Bridge - Read Only</a></h4>
<p>These chains can <em>verify</em> Wormhole messages submitted to them, but cannot <em>emit</em> messages for other chains.</p>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Gnosis</td><td style="text-align: left">25</td><td style="text-align: left">100</td><td style="text-align: left">0xa321448d90d4e5b0A732867c18eA198e75CAC48E</td></tr>
</tbody></table>
<h3 id="token-bridge-1"><a class="header" href="#token-bridge-1">Token Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left">mainnet-beta</td><td style="text-align: left">wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb</td></tr>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left">1</td><td style="text-align: left">0x3ee18B2214AFF97000D974cf647E7C347E8fa585</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">3</td><td style="text-align: left">columbus-5</td><td style="text-align: left">terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">56</td><td style="text-align: left">0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7</td></tr>
<tr><td style="text-align: left">Polygon</td><td style="text-align: left">5</td><td style="text-align: left">137</td><td style="text-align: left">0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE</td></tr>
<tr><td style="text-align: left">Avalanche (C-Chain)</td><td style="text-align: left">6</td><td style="text-align: left">43114</td><td style="text-align: left">0x0e082F06FF657D94310cB8cE8B0D9a04541d8052</td></tr>
<tr><td style="text-align: left">Oasis (Emerald)</td><td style="text-align: left">7</td><td style="text-align: left">4262</td><td style="text-align: left">0x5848C791e09901b40A9Ef749f2a6735b418d7564</td></tr>
<tr><td style="text-align: left">Algorand</td><td style="text-align: left">8</td><td style="text-align: left"></td><td style="text-align: left">842126029</td></tr>
<tr><td style="text-align: left">Aurora</td><td style="text-align: left">9</td><td style="text-align: left">1313161554</td><td style="text-align: left">0x51b5123a7b0F9b2bA265f9c4C8de7D78D52f510F</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">250</td><td style="text-align: left">0x7C9Fc5741288cDFdD83CeB07f3ea7e22618D79D2</td></tr>
<tr><td style="text-align: left">Karura</td><td style="text-align: left">11</td><td style="text-align: left">686</td><td style="text-align: left">0xae9d7fe007b3327AA64A32824Aaac52C42a6E624</td></tr>
<tr><td style="text-align: left">Acala</td><td style="text-align: left">12</td><td style="text-align: left">787</td><td style="text-align: left">0xae9d7fe007b3327AA64A32824Aaac52C42a6E624</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">8217</td><td style="text-align: left">0x5b08ac39EAED75c0439FC750d9FE7E1F9dD0193F</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">42220</td><td style="text-align: left">0x796Dff6D74F3E27060B71255Fe517BFb23C93eed</td></tr>
<tr><td style="text-align: left">NEAR</td><td style="text-align: left">15</td><td style="text-align: left"></td><td style="text-align: left">contract.portalbridge.near</td></tr>
<tr><td style="text-align: left">Moonbeam</td><td style="text-align: left">16</td><td style="text-align: left">1284</td><td style="text-align: left">0xb1731c586ca89a23809861c6103f0b96b3f57d92</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">18</td><td style="text-align: left">phoenix-1</td><td style="text-align: left">terra153366q50k7t8nn7gec00hg66crnhkdggpgdtaxltaq6xrutkkz3s992fw9</td></tr>
<tr><td style="text-align: left">Injective</td><td style="text-align: left">19</td><td style="text-align: left"></td><td style="text-align: left">inj1ghd753shjuwexxywmgs4xz7x2q732vcnxxynfn</td></tr>
<tr><td style="text-align: left">Sui</td><td style="text-align: left">21</td><td style="text-align: left"></td><td style="text-align: left">0xc57508ee0d4595e5a8728974a4a93a787d38f339757230d441e895422c07aba9</td></tr>
<tr><td style="text-align: left">Aptos</td><td style="text-align: left">22</td><td style="text-align: left"></td><td style="text-align: left">0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left"></td><td style="text-align: left">0x0b2402144Bb366A632D14B83F244D2e0e21bD39c</td></tr>
<tr><td style="text-align: left">Optimism</td><td style="text-align: left">24</td><td style="text-align: left">10</td><td style="text-align: left">0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b</td></tr>
<tr><td style="text-align: left">XPLA</td><td style="text-align: left">28</td><td style="text-align: left"></td><td style="text-align: left">xpla137w0wfch2dfmz7jl2ap8pcmswasj8kg06ay4dtjzw7tzkn77ufxqfw7acv</td></tr>
</tbody></table>
<h3 id="wormhole-relayer-contract"><a class="header" href="#wormhole-relayer-contract">Wormhole Relayer Contract</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left">1</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">56</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Polygon</td><td style="text-align: left">5</td><td style="text-align: left">137</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Avalanche (C-Chain)</td><td style="text-align: left">6</td><td style="text-align: left">43114</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">250</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">8217</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">42220</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Moonbeam</td><td style="text-align: left">16</td><td style="text-align: left">1284</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left">42161</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
<tr><td style="text-align: left">Optimism</td><td style="text-align: left">24</td><td style="text-align: left">10</td><td style="text-align: left">0x27428DD2d3DD32A4D7f7C497eAaa23130d894911</td></tr>
</tbody></table>
<h3 id="default-delivery-providers"><a class="header" href="#default-delivery-providers">Default Delivery Providers</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left">1</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">56</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Polygon</td><td style="text-align: left">5</td><td style="text-align: left">137</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Avalanche (C-Chain)</td><td style="text-align: left">6</td><td style="text-align: left">43114</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">250</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">8217</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">42220</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Moonbeam</td><td style="text-align: left">16</td><td style="text-align: left">1284</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left">42161</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
<tr><td style="text-align: left">Optimism</td><td style="text-align: left">24</td><td style="text-align: left">10</td><td style="text-align: left">0x7A0a53847776f7e94Cc35742971aCb2217b0Db81</td></tr>
</tbody></table>
<h3 id="nft-bridge-1"><a class="header" href="#nft-bridge-1">NFT Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left">mainnet-beta</td><td style="text-align: left">WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD</td></tr>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left">1</td><td style="text-align: left">0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">56</td><td style="text-align: left">0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE</td></tr>
<tr><td style="text-align: left">Polygon</td><td style="text-align: left">5</td><td style="text-align: left">137</td><td style="text-align: left">0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf</td></tr>
<tr><td style="text-align: left">Avalanche (C-Chain)</td><td style="text-align: left">6</td><td style="text-align: left">43114</td><td style="text-align: left">0xf7B6737Ca9c4e08aE573F75A97B73D7a813f5De5</td></tr>
<tr><td style="text-align: left">Oasis (Emerald)</td><td style="text-align: left">7</td><td style="text-align: left">4262</td><td style="text-align: left">0x04952D522Ff217f40B5Ef3cbF659EcA7b952a6c1</td></tr>
<tr><td style="text-align: left">Aurora</td><td style="text-align: left">9</td><td style="text-align: left">1313161554</td><td style="text-align: left">0x6dcC0484472523ed9Cdc017F711Bcbf909789284</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">250</td><td style="text-align: left">0xA9c7119aBDa80d4a4E0C06C8F4d8cF5893234535</td></tr>
<tr><td style="text-align: left">Karura</td><td style="text-align: left">11</td><td style="text-align: left">686</td><td style="text-align: left">0xb91e3638F82A1fACb28690b37e3aAE45d2c33808</td></tr>
<tr><td style="text-align: left">Acala</td><td style="text-align: left">12</td><td style="text-align: left">787</td><td style="text-align: left">0xb91e3638F82A1fACb28690b37e3aAE45d2c33808</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">8217</td><td style="text-align: left">0x3c3c561757BAa0b78c5C025CdEAa4ee24C1dFfEf</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">42220</td><td style="text-align: left">0xA6A377d75ca5c9052c9a77ED1e865Cc25Bd97bf3</td></tr>
<tr><td style="text-align: left">Moonbeam</td><td style="text-align: left">16</td><td style="text-align: left">1284</td><td style="text-align: left">0x453cfbe096c0f8d763e8c5f24b441097d577bde2</td></tr>
<tr><td style="text-align: left">Aptos</td><td style="text-align: left">22</td><td style="text-align: left"></td><td style="text-align: left">0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left">42161</td><td style="text-align: left">0x453cfbe096c0f8d763e8c5f24b441097d577bde2</td></tr>
<tr><td style="text-align: left">Optimism</td><td style="text-align: left">24</td><td style="text-align: left">10</td><td style="text-align: left">0xfE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585</td></tr>
</tbody></table>
<h2 id="testnet-2"><a class="header" href="#testnet-2">Testnet</a></h2>
<h3 id="core-bridge-1"><a class="header" href="#core-bridge-1">Core Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left">devnet</td><td style="text-align: left">3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5</td></tr>
<tr><td style="text-align: left">Ethereum (Goerli)</td><td style="text-align: left">2</td><td style="text-align: left">5</td><td style="text-align: left">0x706abc4E45D419950511e474C7B9Ed348A4a716c</td></tr>
<tr><td style="text-align: left">Ethereum (Sepolia)</td><td style="text-align: left">10002</td><td style="text-align: left">11155111</td><td style="text-align: left">0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">3</td><td style="text-align: left">bombay-12</td><td style="text-align: left">terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">97</td><td style="text-align: left">0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D</td></tr>
<tr><td style="text-align: left">Polygon (Mumbai)</td><td style="text-align: left">5</td><td style="text-align: left">80001</td><td style="text-align: left">0x0CBE91CF822c73C2315FB05100C2F714765d5c20</td></tr>
<tr><td style="text-align: left">Avalanche (Fuji)</td><td style="text-align: left">6</td><td style="text-align: left">43113</td><td style="text-align: left">0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C</td></tr>
<tr><td style="text-align: left">Oasis (Emerald Testnet)</td><td style="text-align: left">7</td><td style="text-align: left">42261</td><td style="text-align: left">0xc1C338397ffA53a2Eb12A7038b4eeb34791F8aCb</td></tr>
<tr><td style="text-align: left">Algorand (Testnet)</td><td style="text-align: left">8</td><td style="text-align: left"></td><td style="text-align: left">86525623</td></tr>
<tr><td style="text-align: left">Aurora</td><td style="text-align: left">9</td><td style="text-align: left">1313161555</td><td style="text-align: left">0xBd07292de7b505a4E803CEe286184f7Acf908F5e</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">4002</td><td style="text-align: left">0x1BB3B4119b7BA9dfad76B0545fb3F531383c3bB7</td></tr>
<tr><td style="text-align: left">Karura</td><td style="text-align: left">11</td><td style="text-align: left">686</td><td style="text-align: left">0xE4eacc10990ba3308DdCC72d985f2a27D20c7d03</td></tr>
<tr><td style="text-align: left">Acala</td><td style="text-align: left">12</td><td style="text-align: left">787</td><td style="text-align: left">0x4377B49d559c0a9466477195C6AdC3D433e265c0</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">1001</td><td style="text-align: left">0x1830CC6eE66c84D2F177B94D544967c774E624cA</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">44787</td><td style="text-align: left">0x88505117CA88e7dd2eC6EA1E13f0948db2D50D56</td></tr>
<tr><td style="text-align: left">NEAR</td><td style="text-align: left">15</td><td style="text-align: left"></td><td style="text-align: left">wormhole.wormhole.testnet</td></tr>
<tr><td style="text-align: left">Moonbase alpha</td><td style="text-align: left">16</td><td style="text-align: left">1287</td><td style="text-align: left">0xa5B7D85a8f27dd7907dc8FdC21FA5657D5E2F901</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">18</td><td style="text-align: left">pisco-1</td><td style="text-align: left">terra19nv3xr5lrmmr7egvrk2kqgw4kcn43xrtd5g0mpgwwvhetusk4k7s66jyv0</td></tr>
<tr><td style="text-align: left">Injective</td><td style="text-align: left">19</td><td style="text-align: left">testnet</td><td style="text-align: left">inj1xx3aupmgv3ce537c0yce8zzd3sz567syuyedpg</td></tr>
<tr><td style="text-align: left">Sui</td><td style="text-align: left">21</td><td style="text-align: left"></td><td style="text-align: left">0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790</td></tr>
<tr><td style="text-align: left">Aptos</td><td style="text-align: left">22</td><td style="text-align: left"></td><td style="text-align: left">0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left">421613</td><td style="text-align: left">0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e</td></tr>
<tr><td style="text-align: left">Optimism (Goerli)</td><td style="text-align: left">24</td><td style="text-align: left">420</td><td style="text-align: left">0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35</td></tr>
<tr><td style="text-align: left">Base</td><td style="text-align: left">30</td><td style="text-align: left">84531</td><td style="text-align: left">0x23908A62110e21C04F3A4e011d24F901F911744A</td></tr>
</tbody></table>
<p>These chains can <em>verify</em> Wormhole messages submitted to them, but cannot <em>emit</em> messages for other chains.</p>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Gnosis (Sokol)</td><td style="text-align: left">25</td><td style="text-align: left">77</td><td style="text-align: left">0xE4eacc10990ba3308DdCC72d985f2a27D20c7d03</td></tr>
</tbody></table>
<h3 id="token-bridge-2"><a class="header" href="#token-bridge-2">Token Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left">devnet</td><td style="text-align: left">DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe</td></tr>
<tr><td style="text-align: left">Ethereum (Goerli)</td><td style="text-align: left">2</td><td style="text-align: left">5</td><td style="text-align: left">0xF890982f9310df57d00f659cf4fd87e65adEd8d7</td></tr>
<tr><td style="text-align: left">Ethereum (Sepolia)</td><td style="text-align: left">10002</td><td style="text-align: left">11155111</td><td style="text-align: left">0xDB5492265f6038831E89f495670FF909aDe94bd9</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">3</td><td style="text-align: left">bombay-12</td><td style="text-align: left">terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">97</td><td style="text-align: left">0x9dcF9D205C9De35334D646BeE44b2D2859712A09</td></tr>
<tr><td style="text-align: left">Polygon (Mumbai)</td><td style="text-align: left">5</td><td style="text-align: left">80001</td><td style="text-align: left">0x377D55a7928c046E18eEbb61977e714d2a76472a</td></tr>
<tr><td style="text-align: left">Avalanche (Fuji)</td><td style="text-align: left">6</td><td style="text-align: left">43113</td><td style="text-align: left">0x61E44E506Ca5659E6c0bba9b678586fA2d729756</td></tr>
<tr><td style="text-align: left">Oasis (Emerald Testnet)</td><td style="text-align: left">7</td><td style="text-align: left">42261</td><td style="text-align: left">0x88d8004A9BdbfD9D28090A02010C19897a29605c</td></tr>
<tr><td style="text-align: left">Algorand (Testnet)</td><td style="text-align: left">8</td><td style="text-align: left"></td><td style="text-align: left">86525641</td></tr>
<tr><td style="text-align: left">Aurora</td><td style="text-align: left">9</td><td style="text-align: left">1313161555</td><td style="text-align: left">0xD05eD3ad637b890D68a854d607eEAF11aF456fba</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">4002</td><td style="text-align: left">0x599CEa2204B4FaECd584Ab1F2b6aCA137a0afbE8</td></tr>
<tr><td style="text-align: left">Karura</td><td style="text-align: left">11</td><td style="text-align: left">686</td><td style="text-align: left">0xd11De1f930eA1F7Dd0290Fe3a2e35b9C91AEFb37</td></tr>
<tr><td style="text-align: left">Acala</td><td style="text-align: left">12</td><td style="text-align: left">787</td><td style="text-align: left">0xebA00cbe08992EdD08ed7793E07ad6063c807004</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">1001</td><td style="text-align: left">0xC7A13BE098720840dEa132D860fDfa030884b09A</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">44787</td><td style="text-align: left">0x05ca6037eC51F8b712eD2E6Fa72219FEaE74E153</td></tr>
<tr><td style="text-align: left">Near</td><td style="text-align: left">15</td><td style="text-align: left"></td><td style="text-align: left">token.wormhole.testnet</td></tr>
<tr><td style="text-align: left">Moonbase alpha</td><td style="text-align: left">16</td><td style="text-align: left">1287</td><td style="text-align: left">0xbc976D4b9D57E57c3cA52e1Fd136C45FF7955A96</td></tr>
<tr><td style="text-align: left">Injective</td><td style="text-align: left">19</td><td style="text-align: left">testnet</td><td style="text-align: left">inj1q0e70vhrv063eah90mu97sazhywmeegp7myvnh</td></tr>
<tr><td style="text-align: left">Sui</td><td style="text-align: left">21</td><td style="text-align: left"></td><td style="text-align: left">0x6fb10cdb7aa299e9a4308752dadecb049ff55a892de92992a1edbd7912b3d6da</td></tr>
<tr><td style="text-align: left">Aptos</td><td style="text-align: left">22</td><td style="text-align: left"></td><td style="text-align: left">0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left">421613</td><td style="text-align: left">0x23908A62110e21C04F3A4e011d24F901F911744A</td></tr>
<tr><td style="text-align: left">Optimism (Goerli)</td><td style="text-align: left">24</td><td style="text-align: left">420</td><td style="text-align: left">0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e</td></tr>
<tr><td style="text-align: left">Base</td><td style="text-align: left">30</td><td style="text-align: left">84531</td><td style="text-align: left">0xA31aa3FDb7aF7Db93d18DDA4e19F811342EDF780</td></tr>
</tbody></table>
<h3 id="wormhole-relayer-contract-1"><a class="header" href="#wormhole-relayer-contract-1">Wormhole Relayer Contract</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">97</td><td style="text-align: left">0x80aC94316391752A193C1c47E27D382b507c93F3</td></tr>
<tr><td style="text-align: left">Polygon (Mumbai)</td><td style="text-align: left">5</td><td style="text-align: left">80001</td><td style="text-align: left">0x0591C25ebd0580E0d4F27A82Fc2e24E7489CB5e0</td></tr>
<tr><td style="text-align: left">Avalanche (Fuji)</td><td style="text-align: left">6</td><td style="text-align: left">43113</td><td style="text-align: left">0xA3cF45939bD6260bcFe3D66bc73d60f19e49a8BB</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">44787</td><td style="text-align: left">0x306B68267Deb7c5DfCDa3619E22E9Ca39C374f84</td></tr>
<tr><td style="text-align: left">Moonbase</td><td style="text-align: left">16</td><td style="text-align: left">1287</td><td style="text-align: left">0x0591C25ebd0580E0d4F27A82Fc2e24E7489CB5e0</td></tr>
<tr><td style="text-align: left">Base</td><td style="text-align: left">30</td><td style="text-align: left">84531</td><td style="text-align: left">0xea8029CD7FCAEFFcD1F53686430Db0Fc8ed384E1</td></tr>
</tbody></table>
<h3 id="default-delivery-providers-1"><a class="header" href="#default-delivery-providers-1">Default Delivery Providers</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">97</td><td style="text-align: left">0x60a86b97a7596eBFd25fb769053894ed0D9A8366</td></tr>
<tr><td style="text-align: left">Polygon (Mumbai)</td><td style="text-align: left">5</td><td style="text-align: left">80001</td><td style="text-align: left">0x60a86b97a7596eBFd25fb769053894ed0D9A8366</td></tr>
<tr><td style="text-align: left">Avalanche (Fuji)</td><td style="text-align: left">6</td><td style="text-align: left">43113</td><td style="text-align: left">0x60a86b97a7596eBFd25fb769053894ed0D9A8366</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">44787</td><td style="text-align: left">0x60a86b97a7596eBFd25fb769053894ed0D9A8366</td></tr>
<tr><td style="text-align: left">Moonbase</td><td style="text-align: left">16</td><td style="text-align: left">1287</td><td style="text-align: left">0x60a86b97a7596eBFd25fb769053894ed0D9A8366</td></tr>
<tr><td style="text-align: left">Base</td><td style="text-align: left">16</td><td style="text-align: left">84531</td><td style="text-align: left">0x60a86b97a7596eBFd25fb769053894ed0D9A8366</td></tr>
</tbody></table>
<h3 id="nft-bridge-2"><a class="header" href="#nft-bridge-2">NFT Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left">devnet</td><td style="text-align: left">2rHhojZ7hpu1zA91nvZmT8TqWWvMcKmmNBCr2mKTtMq4</td></tr>
<tr><td style="text-align: left">Ethereum (Goerli)</td><td style="text-align: left">2</td><td style="text-align: left">5</td><td style="text-align: left">0xD8E4C2DbDd2e2bd8F1336EA691dBFF6952B1a6eB</td></tr>
<tr><td style="text-align: left">Ethereum (Sepolia)</td><td style="text-align: left">10002</td><td style="text-align: left">11155111</td><td style="text-align: left">0x6a0B52ac198e4870e5F3797d5B403838a5bbFD99</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left">97</td><td style="text-align: left">0xcD16E5613EF35599dc82B24Cb45B5A93D779f1EE</td></tr>
<tr><td style="text-align: left">Polygon (Mumbai)</td><td style="text-align: left">5</td><td style="text-align: left">80001</td><td style="text-align: left">0x51a02d0dcb5e52F5b92bdAA38FA013C91c7309A9</td></tr>
<tr><td style="text-align: left">Avalanche (Fuji)</td><td style="text-align: left">6</td><td style="text-align: left">43113</td><td style="text-align: left">0xD601BAf2EEE3C028344471684F6b27E789D9075D</td></tr>
<tr><td style="text-align: left">Oasis (Emerald Testnet)</td><td style="text-align: left">7</td><td style="text-align: left">42261</td><td style="text-align: left">0xC5c25B41AB0b797571620F5204Afa116A44c0ebA</td></tr>
<tr><td style="text-align: left">Aurora</td><td style="text-align: left">9</td><td style="text-align: left">1313161555</td><td style="text-align: left">0x8F399607E9BA2405D87F5f3e1B78D950b44b2e24</td></tr>
<tr><td style="text-align: left">Fantom</td><td style="text-align: left">10</td><td style="text-align: left">4002</td><td style="text-align: left">0x63eD9318628D26BdCB15df58B53BB27231D1B227</td></tr>
<tr><td style="text-align: left">Karura</td><td style="text-align: left">11</td><td style="text-align: left">686</td><td style="text-align: left">0x0A693c2D594292B6Eb89Cb50EFe4B0b63Dd2760D</td></tr>
<tr><td style="text-align: left">Acala</td><td style="text-align: left">12</td><td style="text-align: left">787</td><td style="text-align: left">0x96f1335e0AcAB3cfd9899B30b2374e25a2148a6E</td></tr>
<tr><td style="text-align: left">Klaytn</td><td style="text-align: left">13</td><td style="text-align: left">1001</td><td style="text-align: left">0x94c994fC51c13101062958b567e743f1a04432dE</td></tr>
<tr><td style="text-align: left">Celo</td><td style="text-align: left">14</td><td style="text-align: left">44787</td><td style="text-align: left">0xaCD8190F647a31E56A656748bC30F69259f245Db</td></tr>
<tr><td style="text-align: left">Moonbase alpha</td><td style="text-align: left">16</td><td style="text-align: left">1287</td><td style="text-align: left">0x98A0F4B96972b32Fcb3BD03cAeB66A44a6aB9Edb</td></tr>
<tr><td style="text-align: left">Arbitrum</td><td style="text-align: left">23</td><td style="text-align: left">421613</td><td style="text-align: left">0xEe3dB83916Ccdc3593b734F7F2d16D630F39F1D0</td></tr>
<tr><td style="text-align: left">Optimism (Goerli)</td><td style="text-align: left">24</td><td style="text-align: left">420</td><td style="text-align: left">0x23908A62110e21C04F3A4e011d24F901F911744A</td></tr>
<tr><td style="text-align: left">Base</td><td style="text-align: left">30</td><td style="text-align: left">84531</td><td style="text-align: left">0xF681d1cc5F25a3694E348e7975d7564Aa581db59</td></tr>
</tbody></table>
<h2 id="devnet--tilt"><a class="header" href="#devnet--tilt">Devnet / Tilt</a></h2>
<h3 id="core-bridge-2"><a class="header" href="#core-bridge-2">Core Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left"></td><td style="text-align: left">Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o</td></tr>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left"></td><td style="text-align: left">0xC89Ce4735882C9F0f0FE26686c53074E09B0D550</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">3</td><td style="text-align: left"></td><td style="text-align: left">terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left"></td><td style="text-align: left">0xC89Ce4735882C9F0f0FE26686c53074E09B0D550</td></tr>
<tr><td style="text-align: left">Algorand</td><td style="text-align: left">8</td><td style="text-align: left"></td><td style="text-align: left">4</td></tr>
<tr><td style="text-align: left">NEAR</td><td style="text-align: left">15</td><td style="text-align: left"></td><td style="text-align: left">wormhole.test.near</td></tr>
<tr><td style="text-align: left">Terra2</td><td style="text-align: left">18</td><td style="text-align: left"></td><td style="text-align: left">terra14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9ssrc8au</td></tr>
<tr><td style="text-align: left">Sui</td><td style="text-align: left">21</td><td style="text-align: left"></td><td style="text-align: left">0x5a5160ca3c2037f4b4051344096ef7a48ebf4400b3f385e57ea90e1628a8bde0</td></tr>
<tr><td style="text-align: left">Aptos</td><td style="text-align: left">22</td><td style="text-align: left"></td><td style="text-align: left">0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017</td></tr>
<tr><td style="text-align: left">Wormholechain</td><td style="text-align: left">3104</td><td style="text-align: left"></td><td style="text-align: left">wormhole1ap5vgur5zlgys8whugfegnn43emka567dtq0jl</td></tr>
</tbody></table>
<h3 id="token-bridge-3"><a class="header" href="#token-bridge-3">Token Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left"></td><td style="text-align: left">B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE</td></tr>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left"></td><td style="text-align: left">0x0290FB167208Af455bB137780163b7B7a9a10C16</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">3</td><td style="text-align: left"></td><td style="text-align: left">terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left"></td><td style="text-align: left">0x0290FB167208Af455bB137780163b7B7a9a10C16</td></tr>
<tr><td style="text-align: left">Algorand</td><td style="text-align: left">8</td><td style="text-align: left"></td><td style="text-align: left">6</td></tr>
<tr><td style="text-align: left">NEAR</td><td style="text-align: left">15</td><td style="text-align: left"></td><td style="text-align: left">token.test.near</td></tr>
<tr><td style="text-align: left">Terra2</td><td style="text-align: left">18</td><td style="text-align: left"></td><td style="text-align: left">terra1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrquka9l6</td></tr>
<tr><td style="text-align: left">Sui</td><td style="text-align: left">21</td><td style="text-align: left"></td><td style="text-align: left">0xa6a3da85bbe05da5bfd953708d56f1a3a023e7fb58e5a824a3d4de3791e8f690</td></tr>
<tr><td style="text-align: left">Aptos</td><td style="text-align: left">22</td><td style="text-align: left"></td><td style="text-align: left">0x84a5f374d29fc77e370014dce4fd6a55b58ad608de8074b0be5571701724da31</td></tr>
<tr><td style="text-align: left">Wormholechain</td><td style="text-align: left">3104</td><td style="text-align: left"></td><td style="text-align: left">wormhole1zugu6cajc4z7ue29g9wnes9a5ep9cs7yu7rn3z</td></tr>
</tbody></table>
<h3 id="nft-bridge-3"><a class="header" href="#nft-bridge-3">NFT Bridge</a></h3>
<table><thead><tr><th style="text-align: left">Chain Name</th><th style="text-align: left">Wormhole Chain ID</th><th style="text-align: left">Network ID</th><th style="text-align: left">Address</th></tr></thead><tbody>
<tr><td style="text-align: left">Solana</td><td style="text-align: left">1</td><td style="text-align: left"></td><td style="text-align: left">NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA</td></tr>
<tr><td style="text-align: left">Ethereum</td><td style="text-align: left">2</td><td style="text-align: left"></td><td style="text-align: left">0x26b4afb60d6c903165150c6f0aa14f8016be4aec</td></tr>
<tr><td style="text-align: left">Terra</td><td style="text-align: left">3</td><td style="text-align: left"></td><td style="text-align: left">terra1plju286nnfj3z54wgcggd4enwaa9fgf5kgrgzl</td></tr>
<tr><td style="text-align: left">Binance Smart Chain</td><td style="text-align: left">4</td><td style="text-align: left"></td><td style="text-align: left">0x26b4afb60d6c903165150c6f0aa14f8016be4aec</td></tr>
</tbody></table>
<div style="break-before: page; page-break-before: always;"></div><h1 id="rpc-nodes"><a class="header" href="#rpc-nodes">RPC Nodes</a></h1>
<p>These RPC nodes are maintained by the Guardians to help fetch VAAs and query the Wormhole network.</p>
<h2 id="mainnet-guardian-rpc"><a class="header" href="#mainnet-guardian-rpc">Mainnet Guardian RPC</a></h2>
<pre><code>https://wormhole-v2-mainnet-api.certus.one
https://wormhole.inotel.ro
https://wormhole-v2-mainnet-api.mcf.rocks
https://wormhole-v2-mainnet-api.chainlayer.network
https://wormhole-v2-mainnet-api.staking.fund
https://wormhole-v2-mainnet.01node.com
</code></pre>
<h2 id="testnet-guardian-rpc"><a class="header" href="#testnet-guardian-rpc">Testnet Guardian RPC</a></h2>
<pre><code>https://wormhole-v2-testnet-api.certus.one
</code></pre>
<h2 id="testnet-guardian-public-key"><a class="header" href="#testnet-guardian-public-key">Testnet Guardian Public Key</a></h2>
<pre><code>0x13947Bd48b18E53fdAeEe77F3473391aC727C638
</code></pre>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
<script type="text/javascript" src="src/static/solidity.min.js"></script>
<script type="text/javascript">
window.addEventListener('load', function () {
window.setTimeout(window.print, 100);
});
</script>
</body>
</html>