Merge branch 'mvp' into main

This commit is contained in:
chase-45 2022-09-23 14:30:34 -04:00
commit 1d48181147
100 changed files with 3850 additions and 604 deletions

View File

@ -1,5 +1,5 @@
[book]
title = "Wormhole xDapp Development Book v0.1.0"
title = "Wormhole Development Book"
authors = [
"Dev Bharel"
]

View File

@ -1,3 +1,7 @@
# Install Deps
yarn
#cd chains/evm && forge install --no-git --no-commit && cd ../../
# Deploy evm0 and evm1
ts-node orchestrator.ts deploy evm0
ts-node orchestrator.ts deploy evm1

View File

@ -1,6 +1,10 @@
# Warning
echo "Due to the nature of Solana accounts, you'll need to reset the Solana Local Validator (evm can stay as is) every time you run this test, otherwise it'll error saying accounts are already created and in use."
# Install Deps
yarn
#cd chains/evm && forge install --no-git --no-commit && cd ../../
# Build solana code so it's available in the handler
cd chains/solana && anchor build && cd ../../

View File

@ -32,4 +32,9 @@ filename = "./token/emitter_eth.json"
address = "BmRvjCA2cQ1qUNAMVAnPgmjATSBPa2pxE3Q7bRoSGFED"
filename = "./token/emitter_bsc.json"
# METAPLEX Metadata Account
[[test.genesis]]
address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
filename = "./thirdparty/mpl_token_metadata.so"
# NFT BRIDGE

Binary file not shown.

22
projects/xmint/README.md Normal file
View File

@ -0,0 +1,22 @@
# Example Project: xMint
## Summary
xMint is a sample project that uses Ultra Light Clients and Contract Controlled Transfers to allow users to to pay in their native currency to receive tokens from a foreign chain. It does this by bridging the native currency over to the foreign chain, then purchasing the foreign tokens, and bridging the foreign tokens back to their native chain wallet.
Behind this simple transaction is a set of complex Wormhole interactions.
## Setup
### Contract Deploy
### Register App
## Calls
### Buy Token
### Sell Token
### Balance

View File

@ -32,9 +32,9 @@ contract Xmint is ERC20 {
/**
Registers it's sibling applications on other chains as the only ones that can send this instance messages
*/
function registerApplicationContracts(uint16 chainId, bytes32 applicationAddr) public {
function registerApplicationContracts(uint16 chainId, bytes32 emitterAddr) public {
require(msg.sender == owner, "Only owner can register new chains!");
_applicationContracts[chainId] = applicationAddr;
_applicationContracts[chainId] = emitterAddr;
}
/**
@ -44,6 +44,8 @@ contract Xmint is ERC20 {
*/
function submitForeignPurchase(bytes memory encodedVm) public returns (uint64) {
// Complete transfer will give the Tokens to this Contract
// Unlike solana, we don't need to check that the emitter is a Portal contract as register_ scripts register all the Portal contracts
// and the completeTransfer function checks the emitter is a valid Portal contract on one of the chains it's registered with
BridgeStructs.TransferWithPayload memory vaa = _decodePayload(token_bridge.completeTransferWithPayload(encodedVm));
// Mint tokens to this contract
//amt they paid is NATIVE

View File

@ -164,6 +164,18 @@ dependencies = [
"thiserror",
]
[[package]]
name = "anchor-spl"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d65904c3106851f6d1bb87d504044764819d69c51d2b4346d59d399d8afa7d18"
dependencies = [
"anchor-lang",
"solana-program",
"spl-associated-token-account",
"spl-token",
]
[[package]]
name = "anchor-syn"
version = "0.25.0"
@ -293,7 +305,7 @@ checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775"
dependencies = [
"borsh-derive-internal",
"borsh-schema-derive-internal",
"proc-macro-crate",
"proc-macro-crate 0.1.5",
"proc-macro2",
"syn",
]
@ -537,6 +549,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
[[package]]
name = "fixed-hash"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c"
dependencies = [
"static_assertions",
]
[[package]]
name = "generic-array"
version = "0.14.6"
@ -599,6 +620,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.8.1"
@ -769,6 +796,38 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mpl-token-metadata"
version = "1.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af5d66f2608f309ae54488f5e5154c98a1a860a15018426620b8ace580ed685"
dependencies = [
"arrayref",
"borsh",
"mpl-token-vault",
"num-derive",
"num-traits",
"shank",
"solana-program",
"spl-associated-token-account",
"spl-token",
"thiserror",
]
[[package]]
name = "mpl-token-vault"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ade4ef15bc06a6033076c4ff28cba9b42521df5ec61211d6f419415ace2746a"
dependencies = [
"borsh",
"num-derive",
"num-traits",
"solana-program",
"spl-token",
"thiserror",
]
[[package]]
name = "num-derive"
version = "0.3.3"
@ -799,6 +858,27 @@ dependencies = [
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
dependencies = [
"proc-macro-crate 1.2.1",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "once_cell"
version = "1.13.0"
@ -840,6 +920,16 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "primitive-types"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a"
dependencies = [
"fixed-hash",
"uint",
]
[[package]]
name = "proc-macro-crate"
version = "0.1.5"
@ -849,6 +939,17 @@ dependencies = [
"toml",
]
[[package]]
name = "proc-macro-crate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
dependencies = [
"once_cell",
"thiserror",
"toml",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
@ -1093,6 +1194,40 @@ dependencies = [
"keccak",
]
[[package]]
name = "shank"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2935c06d5a141ad2854622a014e30c5eeaa40096667df6bdd682dba9f8f81819"
dependencies = [
"shank_macro",
]
[[package]]
name = "shank_macro"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ab8a1eb550845e36c88007f4c00175eeeb436d2fc6c70d05207a338cba7519f"
dependencies = [
"proc-macro2",
"quote",
"shank_macro_impl",
"syn",
]
[[package]]
name = "shank_macro_impl"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23ec51b35f2336faaeff44daf8952ddee4fe3e3693a313804ae7366f18a9967d"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"serde",
"syn",
]
[[package]]
name = "sized-chunks"
version = "0.6.5"
@ -1114,6 +1249,12 @@ name = "solana"
version = "0.1.0"
dependencies = [
"anchor-lang",
"anchor-spl",
"borsh",
"byteorder",
"mpl-token-metadata",
"primitive-types",
"sha3",
]
[[package]]
@ -1205,6 +1346,38 @@ dependencies = [
"syn",
]
[[package]]
name = "spl-associated-token-account"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b013067447a1396303ddfc294f36e3d260a32f8a16c501c295bcdc7de39b490"
dependencies = [
"borsh",
"solana-program",
"spl-token",
]
[[package]]
name = "spl-token"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d05653bed5932064a287340dbc8a3cb298ee717e5c7ec3353d7cdb9f8fb7e1"
dependencies = [
"arrayref",
"bytemuck",
"num-derive",
"num-traits",
"num_enum",
"solana-program",
"thiserror",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "subtle"
version = "2.4.1"
@ -1257,6 +1430,18 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "uint"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
dependencies = [
"byteorder",
"crunchy",
"hex",
"static_assertions",
]
[[package]]
name = "unicode-ident"
version = "1.0.3"

View File

@ -17,3 +17,9 @@ default = []
[dependencies]
anchor-lang = "0.25.0"
anchor-spl = "0.25.0"
mpl-token-metadata = {version = "1.3.6", features = ["no-entrypoint"]}
sha3 = "0.10.1"
byteorder = "1.4.3"
borsh = "0.9.3"
primitive-types = { version = "0.11.1", default-features = false }

View File

@ -9,10 +9,18 @@ pub struct Config {
#[account]
#[derive(Default)]
pub struct EmitterAddrAccount{
pub chain_id: u16,
pub emitter_addr: String
pub emitter_chain: u16,
pub emitter_address: [u8; 32],
}
//Empty account, we just need to check that it *exists*
#[account]
pub struct ProcessedVAA {}
pub struct ProcessedVAA {}
#[account]
pub struct Redeemer {}
#[account]
pub struct MintInfo {
pub mint: Pubkey
}

View File

@ -0,0 +1,2 @@
pub const CORE_BRIDGE_ADDRESS: &str = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o";
pub const TOKEN_BRIDGE_ADDRESS: &str = "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";

View File

@ -1,5 +1,12 @@
use anchor_lang::prelude::*;
use crate::account::*;
use anchor_spl::token::{Mint};
use mpl_token_metadata::ID as metadata_program_id;
use crate::wormhole::*;
use crate::constant::*;
use std::str::FromStr;
use anchor_spl::token::ID as spl_id;
#[derive(Accounts)]
pub struct Initialize<'info>{
@ -13,11 +20,50 @@ pub struct Initialize<'info>{
pub config: Account<'info, Config>,
#[account(mut)]
pub owner: Signer<'info>,
pub system_program: Program<'info, System>
pub system_program: Program<'info, System>,
pub mint: Account<'info, Mint>,
#[account(
init,
seeds = [b"redeemer"],
bump,
space = 8,
payer=owner
)]
pub redeemer: Account<'info, Redeemer>,
//Accounts for Metadata
#[account(
init,
seeds=[b"mint_authority"],
bump,
payer=owner,
space=8+32
)]
pub mint_authority: Account<'info, MintInfo>,
pub rent: Sysvar<'info, Rent>,
/// CHECK: This account is initalized by the metadata program
#[account(
mut,
seeds = [
b"metadata",
metadata_program_id.to_bytes().as_ref(),
mint.key().to_bytes().as_ref()
],
bump,
seeds::program = metadata_program_id
)]
pub metadata_account: AccountInfo<'info>,
/// CHECK: Check that the account actually is the metadata program
#[account(
constraint = metadata_program.key() == metadata_program_id
)]
pub metadata_program: AccountInfo<'info>,
}
#[derive(Accounts)]
#[instruction(chain_id:u16, emitter_addr:String)]
#[instruction(chain_id:u16, emitter_addr:Vec<u8>)]
pub struct RegisterChain<'info> {
#[account(mut)]
pub owner: Signer<'info>,
@ -31,7 +77,142 @@ pub struct RegisterChain<'info> {
seeds=[b"EmitterAddress".as_ref(), chain_id.to_be_bytes().as_ref()],
payer=owner,
bump,
space=8+2+256
space=8+2+32
)]
pub emitter_acc: Account<'info, EmitterAddrAccount>,
}
#[derive(Accounts)]
pub struct SubmitForeignPurchase<'info> {
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(
mut,
seeds = [b"config"],
bump,
)]
pub config: Account<'info, Config>,
// Fetch the VAA
/// CHECK: Checked in lib.rs because it requires some fancy hashing
#[account(
constraint = core_bridge_vaa.to_account_info().owner == &Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap()
)]
pub core_bridge_vaa: AccountInfo<'info>,
#[account(
init,
seeds=[
(EmitterAddrAccount::try_from_slice(&emitter_acc.data.borrow())?).emitter_address.as_slice().as_ref(),
(EmitterAddrAccount::try_from_slice(&emitter_acc.data.borrow())?).emitter_chain.to_be_bytes().as_ref(),
(PostedMessageData::try_from_slice(&core_bridge_vaa.data.borrow())?.0).sequence.to_be_bytes().as_ref()
],
payer=payer,
bump,
space=8
)]
pub processed_vaa: Account<'info, ProcessedVAA>,
/// CHECK: Checks that it's registered with token bridge
#[account(
seeds = [
(PostedMessageData::try_from_slice(&core_bridge_vaa.data.borrow())?.0).emitter_chain.to_be_bytes().as_ref(),
(PostedMessageData::try_from_slice(&core_bridge_vaa.data.borrow())?.0).emitter_address.as_ref()
],
seeds::program = &Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap(),
bump,
)]
pub emitter_acc: AccountInfo<'info>,
// Complete Transfer of P3
#[account(
mut,
seeds = [b"config"],
bump,
seeds::program = Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
)]
/// CHECK: Token Bridge Config
pub token_bridge_config: AccountInfo<'info>,
/// CHECK: Processed claim PDA for Portal
#[account(
mut,
seeds = [
(PostedMessageData::try_from_slice(&core_bridge_vaa.data.borrow())?.0).emitter_address.as_ref(),
(PostedMessageData::try_from_slice(&core_bridge_vaa.data.borrow())?.0).emitter_chain.to_be_bytes().as_ref(),
(PostedMessageData::try_from_slice(&core_bridge_vaa.data.borrow())?.0).sequence.to_be_bytes().as_ref(),
],
bump,
seeds::program = Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
)]
pub token_bridge_claim_key: AccountInfo<'info>,
/// CHECK: TODO: Probably should check that this is owned by the SPL program and belongs to Config acc
#[account(mut)]
pub weth_ata_account: AccountInfo<'info>,
#[account(
mut,
seeds = [b"redeemer"],
bump,
)]
pub redeemer: Account<'info, Redeemer>,
/// CHECK: Can be the same as mint_ata_account if no relayer
#[account(mut)]
pub fee_recipient: AccountInfo<'info>,
#[account(mut)]
pub weth_mint: Account<'info, Mint>,
/// CHECK: The warapped meta for the wrapped mint
#[account(
seeds = [
b"meta",
weth_mint.key().to_bytes().as_ref()
],
bump,
seeds::program = Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
)]
pub weth_mint_wrapped_meta: AccountInfo<'info>,
/// CHECK: The mint authority for the wrapped token you're passing in
#[account(
seeds = [b"mint_signer"],
seeds::program = Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap(),
bump,
)]
pub mint_authority_wrapped: AccountInfo<'info>,
pub rent_account: Sysvar<'info, Rent>,
/// CHECK: Make sure this is the right token bridge account
#[account(
constraint = token_bridge_program.key() == Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap()
)]
pub token_bridge_program: AccountInfo<'info>,
/// CHECK: SPL Program should be actual SPL Program
#[account(
constraint = spl_program.key() == spl_id
)]
pub spl_program: AccountInfo<'info>,
// Mint SOL#T SPL tokens to Contract PDA
pub xmint_token_mint: Account<'info, Mint>,
#[account(
seeds=[b"mint_authority"],
bump,
)]
pub xmint_authority: Account<'info, MintInfo>,
#[account(mut)]
pub xmint_ata_account: AccountInfo<'info>,
// P1 Portal Transfer to Receipient
}

View File

@ -0,0 +1,12 @@
use anchor_lang::prelude::*;
#[error_code]
pub enum XmintError {
#[msg("Posted VAA Key Mismatch")]
VAAKeyMismatch,
#[msg("Posted VAA Emitter Chain ID or Address Mismatch")]
VAAEmitterMismatch,
}

View File

@ -1,5 +1,21 @@
use anchor_lang::prelude::*;
use mpl_token_metadata::instruction::create_metadata_accounts_v3;
use mpl_token_metadata::ID as metadata_program_id;
use anchor_lang::solana_program::program::invoke_signed;
use anchor_lang::solana_program::instruction::{Instruction, AccountMeta};
use anchor_lang::solana_program::{sysvar::rent, system_program};
use anchor_spl::token::{ID as spl_id, mint_to};
use sha3::Digest;
use byteorder::{
BigEndian,
WriteBytesExt,
};
use std::io::{
Cursor,
Write,
};
use std::str::FromStr;
mod account;
mod context;
@ -10,25 +26,240 @@ mod portal;
mod wormhole;
use context::*;
use wormhole::*;
use error::*;
use constant::*;
declare_id!("BHz6MJGvo8PJaBFqaxyzgJYdY6o8h1rBgsRrUmnHCU9k");
#[program]
pub mod solana {
use anchor_spl::token::MintTo;
use crate::account::EmitterAddrAccount;
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
pub fn initialize(ctx: Context<Initialize>, name: String, symbol:String, uri: String) -> Result<()> {
ctx.accounts.config.owner = ctx.accounts.owner.key();
ctx.accounts.config.nonce = 0;
ctx.accounts.mint_authority.mint = ctx.accounts.mint.key();
// Create Metadata Accounts for SPL Token
let ix = &create_metadata_accounts_v3(
metadata_program_id,
ctx.accounts.metadata_account.key(),
ctx.accounts.mint.key(),
ctx.accounts.mint_authority.key(),
ctx.accounts.owner.key(),
ctx.accounts.mint_authority.key(),
name,
symbol,
uri,
None,
0,
true,
true,
None,
None,
None
);
let accounts = vec![
ctx.accounts.metadata_program.to_account_info(),
ctx.accounts.metadata_account.to_account_info(),
ctx.accounts.mint.to_account_info(),
ctx.accounts.mint_authority.to_account_info(),
ctx.accounts.owner.to_account_info(),
ctx.accounts.mint_authority.to_account_info(),
ctx.accounts.system_program.to_account_info(),
ctx.accounts.rent.to_account_info()
];
let seeds:&[&[u8]] = &[
b"mint_authority",
&[*ctx.bumps.get("mint_authority").unwrap()]
];
invoke_signed(
ix,
accounts.as_slice(),
&[&seeds[..]]
)?;
Ok(())
}
//Register Application Contracts
pub fn register_chain(ctx:Context<RegisterChain>, chain_id:u16, emitter_addr:String) -> Result<()> {
ctx.accounts.emitter_acc.chain_id = chain_id;
ctx.accounts.emitter_acc.emitter_addr = emitter_addr;
pub fn register_chain(ctx:Context<RegisterChain>, chain_id:u16, emitter_addr:Vec<u8>) -> Result<()> {
ctx.accounts.emitter_acc.emitter_chain = chain_id;
ctx.accounts.emitter_acc.emitter_address = emitter_addr.as_slice().try_into().unwrap();
Ok(())
}
//Submit Foreign Purchase
}
pub fn submit_foreign_purchase(ctx:Context<SubmitForeignPurchase>) -> Result <()> {
// Fetch the VAA
//Hash a VAA Extract and derive a VAA Key
let vaa = PostedMessageData::try_from_slice(&ctx.accounts.core_bridge_vaa.data.borrow())?.0;
let serialized_vaa = serialize_vaa(&vaa);
let mut h = sha3::Keccak256::default();
h.write(serialized_vaa.as_slice()).unwrap();
let vaa_hash: [u8; 32] = h.finalize().into();
let (vaa_key, _) = Pubkey::find_program_address(&[
b"PostedVAA",
&vaa_hash
], &Pubkey::from_str(CORE_BRIDGE_ADDRESS).unwrap());
if ctx.accounts.core_bridge_vaa.key() != vaa_key {
return err!(XmintError::VAAKeyMismatch);
}
// Already checked that the SignedVaa is owned by core bridge in account constraint logic
//Check that the emitter chain and address match up with the vaa
// This VAA will be from Portal Token Bridge so use the Emitter Portal Addr
let emitter: EmitterAddrAccount = EmitterAddrAccount::try_from_slice(&ctx.accounts.emitter_acc.data.borrow()).unwrap();
if vaa.emitter_chain != emitter.emitter_chain ||
vaa.emitter_address != emitter.emitter_address {
return err!(XmintError::VAAEmitterMismatch)
}
// Complete Transfer of P3 on Portal
let complete_wrapped_ix = Instruction {
program_id: Pubkey::from_str(TOKEN_BRIDGE_ADDRESS).unwrap(),
accounts: vec![
AccountMeta::new(ctx.accounts.payer.key(), true),
AccountMeta::new_readonly(ctx.accounts.token_bridge_config.key(), false),
AccountMeta::new_readonly(ctx.accounts.core_bridge_vaa.key(), false),
AccountMeta::new(ctx.accounts.token_bridge_claim_key.key(), false),
AccountMeta::new_readonly(ctx.accounts.emitter_acc.key(), false),
AccountMeta::new(ctx.accounts.weth_ata_account.key(), false), // to
AccountMeta::new_readonly(ctx.accounts.redeemer.key(), true), // to owner
AccountMeta::new(ctx.accounts.fee_recipient.key(), false),
AccountMeta::new(ctx.accounts.weth_mint.key(), false),
AccountMeta::new_readonly(ctx.accounts.weth_mint_wrapped_meta.key(), false),
AccountMeta::new_readonly(ctx.accounts.mint_authority_wrapped.key(), false),
AccountMeta::new_readonly(rent::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(ctx.accounts.token_bridge_program.key(), false),
AccountMeta::new_readonly(spl_id, false),
],
data: (
crate::portal::Instruction::CompleteWrappedWithPayload,
{}
).try_to_vec()?
};
let accounts = vec![
ctx.accounts.payer.to_account_info(),
ctx.accounts.token_bridge_config.to_account_info(),
ctx.accounts.core_bridge_vaa.to_account_info(),
ctx.accounts.token_bridge_claim_key.to_account_info(),
ctx.accounts.emitter_acc.to_account_info(),
ctx.accounts.weth_ata_account.to_account_info(), //to
ctx.accounts.redeemer.to_account_info(), // to_owner
ctx.accounts.fee_recipient.to_account_info(),
ctx.accounts.weth_mint.to_account_info(),
ctx.accounts.weth_mint_wrapped_meta.to_account_info(),
ctx.accounts.mint_authority_wrapped.to_account_info(),
ctx.accounts.rent_account.to_account_info(),
ctx.accounts.system_program.to_account_info(),
ctx.accounts.token_bridge_program.to_account_info(),
ctx.accounts.spl_program.to_account_info()
];
let complete_p3_seeds:&[&[u8]] = &[
b"redeemer",
&[*ctx.bumps.get("redeemer").unwrap()]
];
invoke_signed(
&complete_wrapped_ix,
&accounts,
&[&complete_p3_seeds[..]]
)?;
// Mint SOL#T SPL tokens to Contract PDA
let mint_seeds:&[&[u8]] = &[
b"mint_authority",
&[*ctx.bumps.get("mint_authority").unwrap()]
];
mint_to(
CpiContext {
accounts: MintTo {
mint: ctx.accounts.xmint_token_mint.to_account_info(),
authority: ctx.accounts.xmint_authority.to_account_info(),
to: ctx.accounts.xmint_ata_account.to_account_info(),
},
remaining_accounts: vec![],
program: ctx.accounts.spl_program.to_account_info(),
signer_seeds: &[&mint_seeds[..]]
},
0
)?;
// Transfer tokens from Contract PDA to P1 on Portal
Ok(())
}
}
// Convert a full VAA structure into the serialization of its unique components, this structure is
// what is hashed and verified by Guardians.
pub fn serialize_vaa(vaa: &MessageData) -> Vec<u8> {
let mut v = Cursor::new(Vec::new());
v.write_u32::<BigEndian>(vaa.vaa_time).unwrap();
v.write_u32::<BigEndian>(vaa.nonce).unwrap();
v.write_u16::<BigEndian>(vaa.emitter_chain.clone() as u16).unwrap();
v.write(&vaa.emitter_address).unwrap();
v.write_u64::<BigEndian>(vaa.sequence).unwrap();
v.write_u8(vaa.consistency_level).unwrap();
v.write(&vaa.payload).unwrap();
v.into_inner()
}
/*
Ok(Instruction {
program_id,
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new_readonly(config_key, false),
message_acc,
claim_acc,
AccountMeta::new_readonly(endpoint, false),
AccountMeta::new(to, false),
AccountMeta::new_readonly(to_owner, true),
if let Some(fee_r) = fee_recipient {
AccountMeta::new(fee_r, false)
} else {
AccountMeta::new(to, false)
},
AccountMeta::new(mint_key, false),
AccountMeta::new_readonly(meta_key, false),
AccountMeta::new_readonly(mint_authority_key, false),
// Dependencies
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
// Program
AccountMeta::new_readonly(bridge_id, false),
AccountMeta::new_readonly(spl_token::id(), false),
],
data: (
crate::instruction::Instruction::CompleteWrappedWithPayload,
data,
)
.try_to_vec()?,
})
*/

View File

@ -0,0 +1,164 @@
use anchor_lang::prelude::*;
use primitive_types::U256;
use anchor_lang::solana_program::{
program_error::ProgramError::InvalidAccountData,
pubkey::Pubkey,
};
use std::{
io::{
Cursor,
Read,
Write,
},
ops::Deref,
};
pub trait SerializePayload: Sized {
fn serialize<W: Write>(&self, writer: &mut W) -> std::result::Result<(), Error>;
fn try_to_vec(&self) -> std::result::Result<Vec<u8>, Error> {
let mut result = Vec::with_capacity(256);
self.serialize(&mut result)?;
Ok(result)
}
}
pub trait DeserializePayload: Sized {
fn deserialize(buf: &mut &[u8]) -> std::result::Result<Self, Error>;
}
#[derive(AnchorDeserialize, AnchorSerialize)]
pub enum Instruction{
Initialize,
AttestToken,
CompleteNative,
CompleteWrapped,
TransferWrapped,
TransferNative,
RegisterChain,
CreateWrapped,
UpgradeContract,
CompleteNativeWithPayload,
CompleteWrappedWithPayload,
TransferWrappedWithPayload,
TransferNativeWithPayload,
}
#[derive(PartialEq, Debug, Clone)]
pub struct PayloadTransfer {
/// Amount being transferred (big-endian uint256)
pub amount: U256,
/// Address of the token. Left-zero-padded if shorter than 32 bytes
pub token_address: Address,
/// Chain ID of the token
pub token_chain: ChainID,
/// Address of the recipient. Left-zero-padded if shorter than 32 bytes
pub to: Address,
/// Chain ID of the recipient
pub to_chain: ChainID,
/// Amount of tokens (big-endian uint256) that the user is willing to pay as relayer fee. Must be <= Amount.
pub fee: U256,
}
impl DeserializePayload for PayloadTransfer {
fn deserialize(buf: &mut &[u8]) -> Result<Self> {
let mut v = Cursor::new(buf);
if v.read_u8()? != 1 {
return Err(SolitaireError::Custom(0));
};
let mut am_data: [u8; 32] = [0; 32];
v.read_exact(&mut am_data)?;
let amount = U256::from_big_endian(&am_data);
let mut token_address = Address::default();
v.read_exact(&mut token_address)?;
let token_chain = v.read_u16::<BigEndian>()?;
let mut to = Address::default();
v.read_exact(&mut to)?;
let to_chain = v.read_u16::<BigEndian>()?;
let mut fee_data: [u8; 32] = [0; 32];
v.read_exact(&mut fee_data)?;
let fee = U256::from_big_endian(&fee_data);
if v.position() != v.into_inner().len() as u64 {
return Err(InvalidAccountData.into());
}
Ok(PayloadTransfer {
amount,
token_address,
token_chain,
to,
to_chain,
fee,
})
}
}
impl SerializePayload for PayloadTransfer {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<(), SolitaireError> {
// Payload ID
writer.write_u8(1)?;
let mut am_data: [u8; 32] = [0; 32];
self.amount.to_big_endian(&mut am_data);
writer.write_all(&am_data)?;
writer.write_all(&self.token_address)?;
writer.write_u16::<BigEndian>(self.token_chain)?;
writer.write_all(&self.to)?;
writer.write_u16::<BigEndian>(self.to_chain)?;
let mut fee_data: [u8; 32] = [0; 32];
self.fee.to_big_endian(&mut fee_data);
writer.write_all(&fee_data)?;
Ok(())
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct PayloadTransferWithPayload {
/// Amount being transferred (big-endian uint256)
pub amount: U256,
/// Address of the token. Left-zero-padded if shorter than 32 bytes
pub token_address: [u8; 32],
/// Chain ID of the token
pub token_chain: u16,
/// Address of the recipient. Left-zero-padded if shorter than 32 bytes
pub to: [u8; 32],
/// Chain ID of the recipient
pub to_chain: u16,
/// Sender of the transaction
pub from_address: [u8; 32],
/// Arbitrary payload
pub payload: Vec<u8>,
}
#[derive(PartialEq, Debug)]
pub struct PayloadAssetMeta {
/// Address of the token. Left-zero-padded if shorter than 32 bytes
pub token_address: [u8; 32],
/// Chain ID of the token
pub token_chain: u16,
/// Number of decimals of the token
pub decimals: u8,
/// Symbol of the token
pub symbol: String,
/// Name of the token
pub name: String,
}
#[error_code]
pub enum PortalError {
#[msg("")]
}

View File

@ -0,0 +1,115 @@
use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use std::{
io::Write,
};
pub type Address = [u8; 32];
pub type ChainID = u16;
#[derive(AnchorDeserialize, AnchorSerialize)]
pub struct PostMessageData {
/// Unique nonce for this message
pub nonce: u32,
/// Message payload
pub payload: Vec<u8>,
/// Commitment Level required for an attestation to be produced
pub consistency_level: ConsistencyLevel,
}
#[derive(AnchorDeserialize, AnchorSerialize)]
pub enum ConsistencyLevel {
Confirmed,
Finalized
}
#[derive(AnchorDeserialize, AnchorSerialize)]
pub enum Instruction{
Initialize,
PostMessage,
PostVAA,
SetFees,
TransferFees,
UpgradeContract,
UpgradeGuardianSet,
VerifySignatures,
}
#[derive(AnchorDeserialize, AnchorSerialize, Clone)]
pub struct BridgeData {
/// The current guardian set index, used to decide which signature sets to accept.
pub guardian_set_index: u32,
/// Lamports in the collection account
pub last_lamports: u64,
/// Bridge configuration, which is set once upon initialization.
pub config: BridgeConfig,
}
#[derive(AnchorDeserialize, AnchorSerialize, Clone)]
pub struct BridgeConfig {
/// Period for how long a guardian set is valid after it has been replaced by a new one. This
/// guarantees that VAAs issued by that set can still be submitted for a certain period. In
/// this period we still trust the old guardian set.
pub guardian_set_expiration_time: u32,
/// Amount of lamports that needs to be paid to the protocol to post a message
pub fee: u64,
}
#[derive(Debug)]
#[repr(transparent)]
pub struct PostedMessageData(pub MessageData);
#[derive(Debug, Default, BorshDeserialize, BorshSerialize)]
pub struct MessageData {
/// Header of the posted VAA
pub vaa_version: u8,
/// Level of consistency requested by the emitter
pub consistency_level: u8,
/// Time the vaa was submitted
pub vaa_time: u32,
/// Account where signatures are stored
pub vaa_signature_account: Pubkey,
/// Time the posted message was created
pub submission_time: u32,
/// Unique nonce for this message
pub nonce: u32,
/// Sequence number of this message
pub sequence: u64,
/// Emitter of the message
pub emitter_chain: ChainID,
/// Emitter of the message
pub emitter_address: Address,
/// Message payload
pub payload: Vec<u8>,
}
impl AnchorSerialize for PostedMessageData {
fn serialize<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write(b"msg")?;
BorshSerialize::serialize(&self.0, writer)
}
}
impl AnchorDeserialize for PostedMessageData {
fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
*buf = &buf[3..];
Ok(PostedMessageData(
<MessageData as BorshDeserialize>::deserialize(buf)?,
))
}
}

View File

@ -56,6 +56,7 @@ export async function deploy(src: string){
JSON.stringify({
address: deploymentAddress,
tokenAddress: deploymentAddress,
tokenReceipientAddress: deploymentAddress,
vaas: []
}, null, 4)
);
@ -166,7 +167,7 @@ export async function attest(src: string, target: string, address:string = null)
// in this context the target is network we're attesting *from* so it's the network the vaa comes from (hence being placed as the 'source')
// The emitter for this is PORTAL, not our contract, so we set portal=true in fetchVaa
const attestVaa = await fetchVaa(src, tx, true);
console.log(`Creating wrapped on ${target}`);
switch(targetNetwork.type){
case "evm":
await createWrapped(target, src, attestVaa)
@ -199,7 +200,7 @@ export async function createWrapped(src:string, target: string, vaa:string){
srcNetwork.tokenBridgeAddress,
signer,
targetNetwork.wormholeChainId,
tryNativeToUint8Array(targetDeployInfo.address, targetNetwork.wormholeChainId)
tryNativeToUint8Array(targetDeployInfo.tokenAddress, targetNetwork.wormholeChainId)
);
console.log(`${src} Network has new PortalWrappedToken for ${target} network at ${foreignAddress}`);
}
@ -354,7 +355,7 @@ export async function buyToken(src:string, target: string, amount: number): Prom
//For this project, 1 Native Token will always equal 100 Chain Tokens, no matter the source or target chains
const ethToTransferAmt = ethers.utils.parseUnits((amount/100).toString(), "18"); //how much native you want to transfer to buy AMT worth of Tokens on target chain
const targetChainAddress = tryNativeToUint8Array(targetDeploymentInfo.address, targetNetwork.wormholeChainId);
const targetChainAddress = tryNativeToUint8Array(targetDeploymentInfo.tokenReceipientAddress, targetNetwork.wormholeChainId);
//The payload is just the purchaser's public key
// This is used to send a Payload 1 Transfer of Tokens back
@ -378,7 +379,7 @@ export async function buyToken(src:string, target: string, amount: number): Prom
ethToTransferAmt,
targetNetwork.wormholeChainId,
targetChainAddress,
ethers.BigNumber.from(0),
0,
{
gasPrice: 2000000000
},
@ -432,7 +433,7 @@ export async function balance(src:string, target: string) : Promise<string> {
srcNetwork.tokenBridgeAddress,
signer,
targetNetwork.wormholeChainId,
tryNativeToUint8Array(targetDeploymentInfo.address, targetNetwork.wormholeChainId)
tryNativeToUint8Array(targetDeploymentInfo.tokenAddress, targetNetwork.wormholeChainId)
);
const TKN = new ethers.Contract(

View File

@ -12,15 +12,22 @@ import {
getEmitterAddressSolana,
getForeignAssetSolana,
parseSequenceFromLogSolana,
postVaaSolanaWithRetry,
setDefaultWasm,
tryNativeToUint8Array
tryNativeToUint8Array,
importCoreWasm
} from '@certusone/wormhole-sdk';
import * as byteify from 'byteify';
import keccak256 from "keccak256";
import {
getAccount,
getOrCreateAssociatedTokenAccount,
createMint
createMint,
TOKEN_PROGRAM_ID
} from '@solana/spl-token'
import {
PROGRAM_ID as metaplexProgramID,
} from '@metaplex-foundation/mpl-token-metadata';
const exec = promisify(require('child_process').exec);
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString());
@ -48,7 +55,6 @@ export async function deploy(src: string){
console.log(stdout);
// Also initalize the contract
await new Promise((r) => setTimeout(r, 15000)) // wait for the chain to recognize the program
//Initalize the Contract
const xmint = new anchor.Program<SolanaTypes>(
IDL,
@ -58,32 +64,65 @@ export async function deploy(src: string){
new anchor.Wallet(srcKey),
{}));
const [configAcc, _] = findProgramAddressSync([
Buffer.from("config")
], xmint.programId);
await xmint.methods
.initialize()
.accounts({
config: configAcc,
owner: xmint.provider.publicKey,
systemProgram: anchor.web3.SystemProgram.programId
})
.rpc();
const [configAcc, _] = findProgramAddressSync([Buffer.from("config")], xmint.programId);
// Deploy the SPL Token for Xmint
const mintAuthorityPDA = findProgramAddressSync([Buffer.from("mint_authority")], xmint.programId)[0];
const mint = await createMint(
connection,
srcKey,
xmint.programId,
xmint.programId,
9 // We are using 9 to match the CLI decimal default exactly
mintAuthorityPDA,
mintAuthorityPDA,
9, // We are using 9 to match the CLI decimal default exactly
);
await new Promise((r) => setTimeout(r, 15000)) // wait for the chain to recognize the program
const metadataAccount = findProgramAddressSync([
Buffer.from("metadata"),
metaplexProgramID.toBuffer(),
mint.toBuffer()
], metaplexProgramID)[0]
/*
const tokenReceipientAddress = getOrCreateAssociatedTokenAccount(
connection,
srcKey,
//WETH MINT\\,
)
*/
const redeemerAcc = findProgramAddressSync([Buffer.from("redeemer")], xmint.programId)[0];
// Initalize will also CPI into metaplex metadata program to setup metadata for the token
await xmint.methods
.initialize(
`${src}-TOKEN`,
`${src}T`,
""
)
.accounts({
config: configAcc,
owner: srcKey.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
mint: mint,
mintAuthority: mintAuthorityPDA,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
metadataAccount: metadataAccount,
metadataProgram: metaplexProgramID,
redeemer: redeemerAcc
})
.rpc();
await new Promise((r) => setTimeout(r, 15000)) // wait for the chain to recognize the metadata
// Store deploy info
fs.writeFileSync(`./deployinfo/${src}.deploy.json`, JSON.stringify({
address: CONTRACT_ADDRESS,
tokenAddress: mint,
tokenReceipientAddress: redeemerAcc.toString(),
vaas: []
}, null, 4));
}
@ -106,17 +145,21 @@ export async function attest(src: string, target:string, address:string = null){
if(!address){
address = srcDeployInfo.tokenAddress;
}
console.log(`Attesting ${address} from ${target} network onto ${src}`);
console.log(`Attesting ${address} from ${src} network onto ${target}`);
setDefaultWasm("node");
const tx = await attestFromSolana(
connection,
srcNetwork.bridgeAddress,
srcNetwork.tokenBridgeAddress,
srcKey.publicKey.toString(),
address
)
const attestVaa = await fetchVaa(src, tx, true);
);
tx.partialSign(srcKey);
const txid = await connection.sendRawTransaction(tx.serialize());
console.log("TXID: ", txid);
const attestVaa = await fetchVaa(src, txid, true);
switch(targetNetwork.type){
case "evm":
await evm.createWrapped(target, src, attestVaa);
@ -132,7 +175,7 @@ export async function attest(src: string, target:string, address:string = null){
* @param tx
* @param portal
*/
async function fetchVaa(src:string, tx, portal:boolean=false):Promise<string>{
async function fetchVaa(src:string, tx:string, portal:boolean=false):Promise<string>{
const srcNetwork = config.networks[src];
const srcDeployInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
const srcKey = anchor.web3.Keypair.fromSecretKey(Uint8Array.from(JSON.parse((fs.readFileSync(`keypairs/${src}.key`).toString())
@ -141,8 +184,15 @@ async function fetchVaa(src:string, tx, portal:boolean=false):Promise<string>{
setDefaultWasm("node");
const emitterAddr = portal ? await getEmitterAddressSolana(srcNetwork.tokenBridgeAddress) : await getEmitterAddressSolana(srcDeployInfo.address);
const seq = parseSequenceFromLogSolana(await connection.getTransaction(tx));
let transaction = await connection.getTransaction(tx);
let timeElapsed = 0;
while (!transaction) {
await new Promise((r) => setTimeout(r, 1000)) // wait for the chain to recognize the program
transaction = await connection.getTransaction(tx);
timeElapsed += 1;
}
console.log(`VAA from TX(${tx}) found in ${timeElapsed}s`);
const seq = parseSequenceFromLogSolana(transaction);
await new Promise((r) => setTimeout(r, 5000)); //wait for gaurdian to pick up messsage
console.log(
"Searching for: ",
@ -172,6 +222,20 @@ export async function createWrapped(src:string, target:string, vaa:string){
)));
const connection = new anchor.web3.Connection(srcNetwork.rpc);
setDefaultWasm("node");
//Have to Post the VAA first before we can use it
await postVaaSolanaWithRetry(
connection,
async (transaction) => {
transaction.partialSign(srcKey);
return transaction;
},
srcNetwork.bridgeAddress,
srcKey.publicKey.toString(),
Buffer.from(vaa, "base64"),
10
);
const tx = await createWrappedOnSolana(
connection,
srcNetwork.bridgeAddress,
@ -179,18 +243,44 @@ export async function createWrapped(src:string, target:string, vaa:string){
srcKey.publicKey.toString(),
Buffer.from(vaa, "base64")
);
tx.partialSign(srcKey);
const txid = await connection.sendRawTransaction(tx.serialize());
await new Promise((r) => setTimeout(r, 5000)); // wait for blocks to advance before fetching new foreign address
await new Promise((r) => setTimeout(r, 25000)); // wait for blocks to advance before fetching new foreign address
const foreignAddress = await getForeignAssetSolana(
connection,
srcNetwork.tokenBridgeAddress,
targetNetwork.wormholeChainId,
tryNativeToUint8Array(targetDeployInfo.address, targetNetwork.wormholeChainId)
tryNativeToUint8Array(targetDeployInfo.tokenAddress, targetNetwork.wormholeChainId)
);
console.log(`${src} Network has new PortalWrappedToken for ${target} network at ${foreignAddress}`);
//If the attestation is WETH, save the ATA for config of the WETH mint as recipient address
}
export async function debug(){
const src = "sol0";
const target = 'evm0';
const srcNetwork = config.networks[src];
const targetNetwork = config.networks[target];
const srcDeployInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
const targetDeployInfo = JSON.parse(fs.readFileSync(`./deployinfo/${target}.deploy.json`).toString());
const srcKey = anchor.web3.Keypair.fromSecretKey(Uint8Array.from(JSON.parse((fs.readFileSync(`keypairs/${src}.key`).toString())
)));
const connection = new anchor.web3.Connection(srcNetwork.rpc);
setDefaultWasm("node");
await new Promise((r) => setTimeout(r, 20000)); // wait for blocks to advance before fetching new foreign address
const foreignAddress = await getForeignAssetSolana(
connection,
srcNetwork.tokenBridgeAddress,
targetNetwork.wormholeChainId,
tryNativeToUint8Array(targetDeployInfo.tokenAddress, targetNetwork.wormholeChainId)
);
console.log(`${src} Network has new PortalWrappedToken for ${target} network at ${foreignAddress}`);
}
export async function registerApp(src:string, target:string){
const srcNetwork = config.networks[src];
const targetNetwork = config.networks[target];
@ -199,8 +289,7 @@ export async function registerApp(src:string, target:string){
const srcKey = anchor.web3.Keypair.fromSecretKey(Uint8Array.from(JSON.parse((fs.readFileSync(`keypairs/${src}.key`).toString())
)));
const connection = new anchor.web3.Connection(srcNetwork.rpc);
let targetEmitter;
let targetEmitter:String;
switch (targetNetwork.type){
case 'evm':
targetEmitter = getEmitterAddressEth(targetDeployInfo.address);
@ -223,12 +312,17 @@ export async function registerApp(src:string, target:string){
byteify.serializeUint16(targetNetwork.wormholeChainId)
], xmint.programId);
const [configAcc, _] = findProgramAddressSync([
Buffer.from("config")
], xmint.programId);
const tx = await xmint.methods
.registerChain(
targetNetwork.wormholeChainId,
targetEmitter
Buffer.from(targetEmitter, 'hex'),
)
.accounts({
config: configAcc,
emitterAcc: emitterAcc,
owner: xmint.provider.publicKey,
systemProgram: anchor.web3.SystemProgram.programId
@ -249,10 +343,6 @@ export async function registerApp(src:string, target:string){
console.log(`Attested ${target} token on ${src}`);
}
export async function submitForeignPurchase(src:string, target:string, vaa:string){}
export async function submitForeignSale(src:string, target:string, vaa:string){}
export async function sellToken(src:string, target:string, amount:number){}
export async function balance(src: string, target: string) : Promise<string>{
const srcNetwork = config.networks[src];
const targetNetwork = config.networks[target];
@ -267,12 +357,13 @@ export async function balance(src: string, target: string) : Promise<string>{
return (await connection.getBalance(srcKey.publicKey)).toString()
}
setDefaultWasm("node")
// Else get the Token Balance of the Foreign Network's token on the Src Network
const foreignAddress = await getForeignAssetSolana(
connection,
srcNetwork.tokenBridgeAddress,
targetNetwork.wormholeChainId,
tryNativeToUint8Array(targetDeployInfo.address, targetNetwork.wormholeChainId)
tryNativeToUint8Array(targetDeployInfo.tokenAddress, targetNetwork.wormholeChainId)
);
const tokenAccountAddress = await getOrCreateAssociatedTokenAccount(
@ -291,5 +382,154 @@ export async function balance(src: string, target: string) : Promise<string>{
}
export async function buyToken(src:string, target: string, amount:number){
// Buy token on target chain with SOL on SRC chain
// Create p3 that pays SOL, reciepient address is
}
export async function submitForeignPurchase(src:string, target:string, vaa:string){
const srcNetwork = config.networks[src];
const targetNetwork = config.networks[target];
const srcDeployInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
const targetDeployInfo = JSON.parse(fs.readFileSync(`./deployinfo/${target}.deploy.json`).toString());
const srcKey = anchor.web3.Keypair.fromSecretKey(Uint8Array.from(JSON.parse((fs.readFileSync(`keypairs/${src}.key`).toString())
)));
const connection = new anchor.web3.Connection(srcNetwork.rpc);
setDefaultWasm("node");
// Post the VAA to Solana Chain for signature verification
await postVaaSolanaWithRetry(
connection,
async (tx) => {
tx.partialSign(srcKey);
return tx;
},
srcNetwork.bridgeAddress,
srcKey.publicKey.toString(),
Buffer.from(vaa, "base64"),
10
);
const xmint = new anchor.Program<SolanaTypes>(
IDL,
CONTRACT_ADDRESS,
new anchor.AnchorProvider(
connection,
new anchor.Wallet(srcKey),
{}));
const { parse_vaa } = await importCoreWasm(); //this function can only be imported from the WASM right now, not directly
const parsed_vaa = parse_vaa(Buffer.from(vaa, 'base64'));
const vaaHash = getVaaHash(parsed_vaa); //await getSignedVAAHash(Buffer.from(vaa, "base64"));
// Account that we stored the registered foreign emitter
let emitterAddressAcc = findProgramAddressSync([
byteify.serializeUint16(parsed_vaa.emitter_chain),
Buffer.from(parsed_vaa.emitter_address, 'hex')
], new anchor.web3.PublicKey(srcNetwork.tokenBridgeAddress))[0];
// A blank account we're creating just to keep track of already processed messages
let targetEmitterAddress;
switch (targetNetwork.type) {
case "evm":
targetEmitterAddress = getEmitterAddressEth(targetNetwork.tokenBridgeAddress);
break;
case "solana":
targetEmitterAddress = await getEmitterAddressSolana(targetNetwork.tokenBridgeAddress);
}
let processedVaaKey = findProgramAddressSync([
Buffer.from(targetEmitterAddress, "hex"),
byteify.serializeUint16(parsed_vaa.emitter_chain),
byteify.serializeUint64(parsed_vaa.sequence)
], xmint.programId)[0];
// Account where the core bridge stored the vaa after the signatures checked out
const tokenBridgePubKey = new anchor.web3.PublicKey(srcNetwork.tokenBridgeAddress);
const coreBridgePubKey = new anchor.web3.PublicKey(srcNetwork.bridgeAddress);
let coreBridgeVaaKey = findProgramAddressSync([
Buffer.from("PostedVAA"),
Buffer.from(vaaHash, 'hex')
], coreBridgePubKey)[0]
const [configAcc, _] = findProgramAddressSync([Buffer.from("config")], xmint.programId);
// Accounts for Completing P3
const tokenBridgeConfigAcc = findProgramAddressSync([Buffer.from("config")], tokenBridgePubKey)[0];
const tokenBridgeClaimAcc = findProgramAddressSync([
Buffer.from(targetEmitterAddress, "hex"),
byteify.serializeUint16(parsed_vaa.emitter_chain),
byteify.serializeUint64(parsed_vaa.sequence)
], tokenBridgePubKey)[0];
// WETH is being transferred in, so we need the WETH Portal Mint on Solana
const wethMint = new anchor.web3.PublicKey(await getForeignAssetSolana(
connection,
srcNetwork.tokenBridgeAddress,
targetNetwork.wormholeChainId,
tryNativeToUint8Array(targetNetwork.wrappedNativeAddress, targetNetwork.wormholeChainId)
));
const wethWrappedMeta = findProgramAddressSync([
Buffer.from("meta"),
wethMint.toBuffer()
], tokenBridgePubKey)[0];
const mintAuthorityWrapped = findProgramAddressSync([
Buffer.from("mint_signer")
], tokenBridgePubKey)[0];
const redeemerAcc = findProgramAddressSync([Buffer.from("redeemer")], xmint.programId)[0];
const wethAtaAcc = await getOrCreateAssociatedTokenAccount(
connection,
srcKey,
wethMint,
redeemerAcc,
true // Allow off curve because the owner of the mintATA acc is a PDA
);
const tx = await xmint.methods
.submitForeignPurchase()
.accounts({
payer: srcKey.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
config: configAcc,
coreBridgeVaa: coreBridgeVaaKey,
processedVaa: processedVaaKey,
emitterAcc: emitterAddressAcc,
tokenBridgeConfig: tokenBridgeConfigAcc,
tokenBridgeClaimKey: tokenBridgeClaimAcc,
wethAtaAccount: wethAtaAcc.address,
feeRecipient: wethAtaAcc.address,
wethMint: wethMint,
wethMintWrappedMeta: wethWrappedMeta,
mintAuthorityWrapped: mintAuthorityWrapped,
rentAccount: anchor.web3.SYSVAR_RENT_PUBKEY,
tokenBridgeProgram: tokenBridgePubKey,
splProgram: TOKEN_PROGRAM_ID,
redeemer:redeemerAcc,
})
.preInstructions([
anchor.web3.ComputeBudgetProgram.requestUnits({
units: 1400000,
additionalFee: 0,
})
])
.rpc();
}
export async function sellToken(src:string, target:string, amount:number){}
export async function submitForeignSale(src:string, target:string, vaa:string){}
function getVaaHash(parsed_vaa){
//Create VAA Hash to use in core bridge key
let buffer_array = []
buffer_array.push(byteify.serializeUint32(parsed_vaa.timestamp));
buffer_array.push(byteify.serializeUint32(parsed_vaa.nonce));
buffer_array.push(byteify.serializeUint16(parsed_vaa.emitter_chain));
buffer_array.push(Uint8Array.from(parsed_vaa.emitter_address));
buffer_array.push(byteify.serializeUint64(parsed_vaa.sequence));
buffer_array.push(byteify.serializeUint8(parsed_vaa.consistency_level));
buffer_array.push(Uint8Array.from(parsed_vaa.payload));
const hash = keccak256(Buffer.concat(buffer_array));
return hash.toString("hex");
}

View File

@ -63,8 +63,9 @@ xmint
await srcHandler.registerApp(src,target)
try{
console.log(`Attesting ${src} Wrapped Native to ${target}`);
await srcHandler.attest(target, src, config.networks[src].wrappedNativeAddress)
await srcHandler.attest(src, target, config.networks[src].wrappedNativeAddress)
} catch (e) {
console.log("ERROR: ", e);
console.log("Wrapped Native attestion exists already")
}
@ -157,10 +158,17 @@ xmint
balance = await evm.balance(src, target);
break;
case "solana":
balance = await solana.balance(src, target);
break;
}
console.log(`Balance of ${src} key for ${target} tokens is ${balance}`);
})
xmint
.command("debug")
.action(async () => {
await solana.debug();
})
xmint.parse();

View File

@ -6,6 +6,7 @@
"license": "MIT",
"dependencies": {
"@certusone/wormhole-sdk": "^0.6.1",
"@metaplex-foundation/mpl-token-metadata": "^2.2.4",
"@project-serum/anchor": "^0.25.0",
"@solana/spl-token": "^0.3.1",
"@types/node": "^18.7.1",
@ -13,6 +14,7 @@
"byteify": "^2.0.10",
"commander": "^9.4.0",
"ethers": "^5.6.9",
"keccak256": "^1.0.6",
"node-fetch": "2"
}
}

View File

@ -1,3 +1,6 @@
# Install Deps
cd chains/evm && forge install --no-git --no-commit && cd ../../
# Deploy the code
ts-node orchestrator.ts deploy evm0
ts-node orchestrator.ts deploy evm1

View File

@ -1,20 +1,21 @@
# Build SOL code
cd chains/solana && anchor build && cd ../../
cd chains/evm && forge install --no-git --no-commit && cd ../../
# Deploy the code on EVM0 and SOL0
ts-node orchestrator.ts deploy evm0
ts-node orchestrator.ts deploy sol0
# Register Apps EVM<>SOL
ts-node orchestrator.ts register-app evm0 sol0
ts-node orchestrator.ts register-app sol0 evm0
# Print Balances for EVM0 and SOL0 Keypairs
ts-node orchestrator.ts balance evm0 evm0
ts-node orchestrator.ts balance evm0 sol0
ts-node orchestrator.ts balance sol0 sol0
ts-node orchestrator.ts balance sol0 evm0
# Register Apps EVM<>SOL
ts-node orchestrator.ts register-app evm0 sol0
ts-node orchestrator.ts register-app sol0 evm0
# Buy SOL0-TOKEN with eth
ts-node orchestrator.ts buy-token evm0 sol0 100
# Print SOL0 Balance

View File

@ -1,13 +1,21 @@
set -e pipefail
# Rerun solana validator
cd ../wormhole-local-validator && npm run evm && npm run solana && npm run wormhole && cd ../xmint
# Deploy the code on EVM0 and SOL0
ts-node orchestrator.ts deploy evm0
ts-node orchestrator.ts deploy sol0
# Register Apps EVM<>SOL
ts-node orchestrator.ts register-app evm0 sol0
ts-node orchestrator.ts register-app sol0 evm0
# Print Balances for EVM0 and SOL0 Keypairs
ts-node orchestrator.ts balance evm0 evm0
ts-node orchestrator.ts balance evm0 sol0
ts-node orchestrator.ts balance sol0 sol0
ts-node orchestrator.ts balance sol0 evm0
# Register Apps EVM<>SOL
ts-node orchestrator.ts register-app evm0 sol0
ts-node orchestrator.ts register-app sol0 evm0
# Buy SOL0-TOKEN with eth
ts-node orchestrator.ts buy-token evm0 sol0 100

View File

@ -382,6 +382,18 @@
"@ethersproject/properties" "^5.6.0"
"@ethersproject/strings" "^5.6.1"
"@hapi/hoek@^9.0.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
"@hapi/topo@^5.0.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
dependencies:
"@hapi/hoek" "^9.0.0"
"@improbable-eng/grpc-web@^0.14.1":
version "0.14.1"
resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz#f4662f64dc89c0f956a94bb8a3b576556c74589c"
@ -396,6 +408,67 @@
dependencies:
browser-headers "^0.4.1"
"@metaplex-foundation/beet-solana@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.3.0.tgz#28180e1f31a94fe0e926f3728c8a25d8f02ec562"
integrity sha512-7kl9E7PWKshYCKVAE/EB6s0wXPmqyubwMbfuRHlgECQoX7RM+4OlnC156WUThOsHBy8GeZxSwc6te6PkgqED4A==
dependencies:
"@metaplex-foundation/beet" ">=0.1.0"
"@solana/web3.js" "^1.44.0"
bs58 "^5.0.0"
debug "^4.3.4"
"@metaplex-foundation/beet@>=0.1.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.6.0.tgz#f31fc35c64844f66d981dbf6c282dbd6f29ae64f"
integrity sha512-z/xkIJ/P4aHKdkO+O48Ryyyx+cp2pxpVBR3LNYJtpiAygvRB4Fi3I6jr4XQBMGwGwa2TYTDIyoMVWfXIAxtX0w==
dependencies:
ansicolors "^0.3.2"
bn.js "^5.2.0"
debug "^4.3.3"
"@metaplex-foundation/beet@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.4.0.tgz#eb2a0a6eb084bb25d67dd9bed2f7387ee7e63a55"
integrity sha512-2OAKJnLatCc3mBXNL0QmWVQKAWK2C7XDfepgL0p/9+8oSx4bmRAFHFqptl1A/C0U5O3dxGwKfmKluW161OVGcA==
dependencies:
ansicolors "^0.3.2"
bn.js "^5.2.0"
debug "^4.3.3"
"@metaplex-foundation/cusper@^0.0.2":
version "0.0.2"
resolved "https://registry.yarnpkg.com/@metaplex-foundation/cusper/-/cusper-0.0.2.tgz#dc2032a452d6c269e25f016aa4dd63600e2af975"
integrity sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA==
"@metaplex-foundation/mpl-token-metadata@^2.2.4":
version "2.2.4"
resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-token-metadata/-/mpl-token-metadata-2.2.4.tgz#d934cf20d6e22f7923fc96b80c7534595780eda6"
integrity sha512-cayIZ7w/XUKkUygNyWyx5l5Q8PO5qzIemk6c/7dxKhdHLlaVPwKjRhbg6kIuzZ8tvYP5afUmJKln9rWnI8tQCA==
dependencies:
"@metaplex-foundation/beet" "^0.4.0"
"@metaplex-foundation/beet-solana" "^0.3.0"
"@metaplex-foundation/cusper" "^0.0.2"
"@solana/spl-token" "^0.2.0"
"@solana/web3.js" "^1.35.1"
bn.js "^5.2.0"
debug "^4.3.3"
"@noble/ed25519@^1.7.0":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.0.tgz#583ac38340a479314b9e348d4572101ed9492f9d"
integrity sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg==
"@noble/hashes@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183"
integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==
"@noble/secp256k1@^1.6.3":
version "1.6.3"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94"
integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==
"@project-serum/anchor@^0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.25.0.tgz#88ee4843336005cf5a64c80636ce626f0996f503"
@ -478,6 +551,23 @@
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
"@sideway/address@^4.1.3":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==
dependencies:
"@hapi/hoek" "^9.0.0"
"@sideway/formula@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c"
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==
"@sideway/pinpoint@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
"@solana/buffer-layout-utils@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca"
@ -507,6 +597,16 @@
buffer-layout "^1.2.0"
dotenv "10.0.0"
"@solana/spl-token@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.2.0.tgz#329bb6babb5de0f9c40035ddb1657f01a8347acd"
integrity sha512-RWcn31OXtdqIxmkzQfB2R+WpsJOVS6rKuvpxJFjvik2LyODd+WN58ZP3Rpjpro03fscGAkzlFuP3r42doRJgyQ==
dependencies:
"@solana/buffer-layout" "^4.0.0"
"@solana/buffer-layout-utils" "^0.2.0"
"@solana/web3.js" "^1.32.0"
start-server-and-test "^1.14.0"
"@solana/spl-token@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.1.tgz#20ba93f8e86b0913e6bfa49fc0708f7fd3bdaf5e"
@ -562,6 +662,28 @@
superstruct "^0.14.2"
tweetnacl "^1.0.3"
"@solana/web3.js@^1.35.1", "@solana/web3.js@^1.44.0":
version "1.55.0"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.55.0.tgz#9d7ceb7f71a7316b1e5dbd4434ef82f6537de5d9"
integrity sha512-hXEc5CtA/5tZMvwEy0Cv6iynBpWoSLk6ud2PmoiGfWrZXZqkrfgTPkgd4yBw+VNZJRBRMKVbs/P7kT1sBwZUrw==
dependencies:
"@babel/runtime" "^7.12.5"
"@noble/ed25519" "^1.7.0"
"@noble/hashes" "^1.1.2"
"@noble/secp256k1" "^1.6.3"
"@solana/buffer-layout" "^4.0.0"
bigint-buffer "^1.1.5"
bn.js "^5.0.0"
borsh "^0.7.0"
bs58 "^4.0.1"
buffer "6.0.1"
fast-stable-stringify "^1.0.0"
jayson "^3.4.4"
js-sha3 "^0.8.0"
node-fetch "2"
rpc-websockets "^7.5.0"
superstruct "^0.14.2"
"@terra-money/legacy.proto@npm:@terra-money/terra.proto@^0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@terra-money/terra.proto/-/terra.proto-0.1.7.tgz#59c18f30da10d43200bab3ba8feb5b17e43a365f"
@ -708,11 +830,23 @@ algosdk@^1.15.0:
optionalDependencies:
fsevents "2.1.2"
ansicolors@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979"
integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^0.21.1:
version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
dependencies:
follow-redirects "^1.14.0"
axios@^0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
@ -739,6 +873,11 @@ base-x@^3.0.2:
dependencies:
safe-buffer "^5.0.1"
base-x@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a"
integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==
base64-js@^1.3.1, base64-js@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -796,6 +935,11 @@ bip39@^3.0.3:
pbkdf2 "^3.0.9"
randombytes "^2.0.1"
bluebird@3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
bn.js@^4.11.8, bn.js@^4.11.9:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
@ -840,6 +984,13 @@ bs58@^4.0.0, bs58@^4.0.1:
dependencies:
base-x "^3.0.2"
bs58@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279"
integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==
dependencies:
base-x "^4.0.0"
bs58check@<3.0.0, bs58check@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
@ -862,7 +1013,7 @@ buffer@6.0.1:
base64-js "^1.3.1"
ieee754 "^1.2.1"
buffer@6.0.3, buffer@^6.0.2, buffer@~6.0.3:
buffer@6.0.3, buffer@^6.0.2, buffer@^6.0.3, buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
@ -903,6 +1054,11 @@ camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
check-more-types@2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==
cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
@ -973,12 +1129,28 @@ cross-fetch@^3.1.5:
dependencies:
node-fetch "2.6.7"
cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
crypto-hash@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247"
integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==
debug@^4.1.1:
debug@4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
debug@^4.1.1, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -1013,6 +1185,11 @@ dotenv@10.0.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
duplexer@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.4:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
@ -1074,11 +1251,39 @@ ethers@^5.6.9:
"@ethersproject/web" "5.6.1"
"@ethersproject/wordlists" "5.6.1"
event-stream@=3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==
dependencies:
duplexer "~0.1.1"
from "~0"
map-stream "~0.1.0"
pause-stream "0.0.11"
split "0.3"
stream-combiner "~0.0.4"
through "~2.3.1"
eventemitter3@^4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
execa@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
dependencies:
cross-spawn "^7.0.3"
get-stream "^6.0.0"
human-signals "^2.1.0"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.1"
onetime "^5.1.2"
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
eyes@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
@ -1099,7 +1304,7 @@ file-uri-to-path@1.0.0:
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
follow-redirects@^1.14.4, follow-redirects@^1.14.8:
follow-redirects@^1.14.0, follow-redirects@^1.14.4, follow-redirects@^1.14.8:
version "1.15.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
@ -1118,6 +1323,11 @@ formidable@^1.2.2:
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168"
integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==
from@~0:
version "0.1.7"
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -1142,6 +1352,11 @@ get-intrinsic@^1.0.2:
has "^1.0.3"
has-symbols "^1.0.3"
get-stream@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
glob@^7.1.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
@ -1202,6 +1417,11 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
ieee754@^1.1.13, ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
@ -1220,6 +1440,16 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-stream@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
isomorphic-ws@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc"
@ -1246,6 +1476,17 @@ jayson@^3.4.4:
uuid "^8.3.2"
ws "^7.4.5"
joi@^17.4.0:
version "17.6.0"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2"
integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==
dependencies:
"@hapi/hoek" "^9.0.0"
"@hapi/topo" "^5.0.0"
"@sideway/address" "^4.1.3"
"@sideway/formula" "^3.0.0"
"@sideway/pinpoint" "^2.0.0"
js-base64@^3.6.1:
version "3.7.2"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745"
@ -1288,7 +1529,30 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
lodash@^4.17.20:
keccak256@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58"
integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==
dependencies:
bn.js "^5.2.0"
buffer "^6.0.3"
keccak "^3.0.2"
keccak@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0"
integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==
dependencies:
node-addon-api "^2.0.0"
node-gyp-build "^4.2.0"
readable-stream "^3.6.0"
lazy-ass@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==
lodash@^4.17.20, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -1317,6 +1581,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
map-stream@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@ -1326,6 +1595,11 @@ md5.js@^1.3.4:
inherits "^2.0.1"
safe-buffer "^5.1.2"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
methods@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@ -1348,6 +1622,11 @@ mime@^2.4.6:
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@ -1365,6 +1644,11 @@ minimatch@^3.1.1:
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
@ -1400,6 +1684,13 @@ node-gyp-build@^4.2.0, node-gyp-build@^4.3.0:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40"
integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==
npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
object-inspect@^1.9.0:
version "1.12.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
@ -1412,6 +1703,13 @@ once@^1.3.0:
dependencies:
wrappy "1"
onetime@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
pako@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d"
@ -1422,6 +1720,18 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
pause-stream@0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==
dependencies:
through "~2.3"
pbkdf2@^3.0.9:
version "3.1.2"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
@ -1471,6 +1781,13 @@ protobufjs@~6.11.2:
"@types/node" ">=13.7.0"
long "^4.0.0"
ps-tree@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd"
integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==
dependencies:
event-stream "=3.3.4"
punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
@ -1539,7 +1856,7 @@ rpc-websockets@^7.5.0:
bufferutil "^4.0.1"
utf-8-validate "^5.0.2"
rxjs@^7.5.6:
rxjs@^7.1.0, rxjs@^7.5.6:
version "7.5.6"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc"
integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==
@ -1580,6 +1897,18 @@ sha.js@^2.4.0, sha.js@^2.4.8:
inherits "^2.0.1"
safe-buffer "^5.0.1"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
dependencies:
shebang-regex "^3.0.0"
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@ -1589,6 +1918,11 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
signal-exit@^3.0.3:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
@ -1597,6 +1931,33 @@ snake-case@^3.0.4:
dot-case "^3.0.4"
tslib "^2.0.3"
split@0.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==
dependencies:
through "2"
start-server-and-test@^1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.14.0.tgz#c57f04f73eac15dd51733b551d775b40837fdde3"
integrity sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw==
dependencies:
bluebird "3.7.2"
check-more-types "2.24.0"
debug "4.3.2"
execa "5.1.1"
lazy-ass "1.6.0"
ps-tree "1.2.0"
wait-on "6.0.0"
stream-combiner@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==
dependencies:
duplexer "~0.1.1"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@ -1604,6 +1965,11 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
superagent@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/superagent/-/superagent-6.1.0.tgz#09f08807bc41108ef164cfb4be293cebd480f4a6"
@ -1636,7 +2002,7 @@ text-encoding-utf-8@^1.0.2:
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
"through@>=2.2.7 <3":
through@2, "through@>=2.2.7 <3", through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
@ -1706,6 +2072,17 @@ vlq@^2.0.4:
resolved "https://registry.yarnpkg.com/vlq/-/vlq-2.0.4.tgz#6057b85729245b9829e3cc7755f95b228d4fe041"
integrity sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==
wait-on@6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7"
integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw==
dependencies:
axios "^0.21.1"
joi "^17.4.0"
lodash "^4.17.21"
minimist "^1.2.5"
rxjs "^7.1.0"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@ -1733,6 +2110,13 @@ whatwg-url@^5.0.0:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
wif@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704"

View File

@ -8,8 +8,8 @@
- [xDapps](./dapps/0_xdappOverview.md)
- [Dapp Ecosystem Basics](./dapps/1_defiBasics.md)
- [Cross-Chain Interoperability](./dapps/2_crossChainInteroperability.md)
- [Ecosystem Basics](./dapps/1_defiBasics.md)
- [Blockchain Interoperability](./dapps/2_crossChainInteroperability.md)
- [xData & xAssets](./dapps/3_xdataxassets.md)
- [What is an xDapp?](./dapps/4_whatIsanXdapp.md)
- [Advantages of xDapps](./dapps/5_advantages.md)
@ -22,7 +22,7 @@
- [VAA: Verified Action Approval](./wormhole/4_vaa.md)
- [Guardian Network](./wormhole/5_guardianNetwork.md)
- [Relayers](./wormhole/6_relayers.md)
- [Portal xAsset Bridge](./wormhole/7_portalTokenBridge.md)
- [xAsset Bridge](./wormhole/7_xAssetBridge.md)
- [Wormchain](./wormhole/8_wormchain.md)
- [xDapp Design](./dapps/architecture/0_dappDesign.md)
@ -35,52 +35,45 @@
---
# xDapp Development
# Developing xDapps
- [Before You Start](./development/overview.md)
- [Wormhole Local Validator](./development/wormhole-local-validator.md)
- [Tilt Installation](./development/tilt/overview.md)
- [MacOS](./development/tilt/mac.md)
- [Linux](./development/tilt/linux.md)
- [Constants](./development/tilt/constants.md)
- [Project Scaffold](./development/scaffold/overview.md)
- [Sending Messages](./development/messages/sending/overview.md)
- [EVM](./development/messages/sending/evm.md)
- [Registering xDapps](./development/messages/registration/overview.md)
- [EVM](./development/messages/registration/evm.md)
- [Relaying Messages](./development/messages/relaying/overview.md)
- [Manual Relays]()
- [REST & Spy Relayer]()
- [Generic Relayers]()
- [Receving Messages](./development/messages/receiving/overview.md)
- [EVM](./development/messages/receiving/evm.md)
- [Projects](./projects/summary.md)
- [EVM Messenger](./projects/evm-messenger/overview.md)
- [Solidity](./projects/evm-messenger/messenger.md)
- [JS Client](./projects/evm-messenger/client.md)
- [Messenger](./projects/messenger/introduction.md)
- [Prerequistes]()
- [EVM]()
- [Environment Setup](./technical/env/environments.md)
- [Tilt (Devnet)](./technical/env/tilt.md)
- [Troubleshooting](./technical/env/troubleshooting.md)
- [Wormhole Local Validator](./technical/env/wlv.md)
- [Testnet](./technical/env/testnet.md)
- [Tooling](./technical/env/tooling.md)
- [Contract Development](./technical/overview.md)
- [EVM](./technical/evm/overview.md)
- [Core Layer](./technical/evm/coreLayer.md)
- [Token Bridge Module](./technical/evm/tokenLayer.md)
- [NFT Bridge Module](./technical/evm/nftLayer.md)
- [Relayer Module](./technical/evm/relayer.md)
- [Best Practices](./technical/evm/bestPractices.md)
- [Solana](./technical/solana/overview.md)
- [Cosmos](./technical/cosmos/overview.md)
- [Algorand](./technical/algorand/overview.md)
- [Near](./technical/near/overview.md)
- [Aptos](./technical/aptos/overview.md)
- [Relayers](./technical/relayer/overview.md)
- [Generic Relayers](./technical/relayer/genericRelayer.md)
- [Specialized Relayers](./technical/relayer/specializedRelayers.md)
- [Wormhole Typescript SDK](./technical/typescript/overview.md)
- [Token Registration](./technical/typescript/attestingToken.md)
- [Token Transfer Basics](./technical/typescript/tokenTransfer.md)
- [Cross-Ecosystem Transfer](./technical/typescript/cross-ecosystem-transfer.md)
- [Using Relayers](./technical/typescript/using-relayer.md)
---
# Portal Token Bridge
- [Portal](./development/portal/overview.md)
- [EVM]()
- [Attesting](./development/portal/evm/attestingToken.md)
- [Transfer Tokens](./development/portal/evm/tokenTransfer.md)
- [Portal JS SDK](./development/portal/sdkjs/overview.md)
- [EVM to Solana Transfer](./development/portal/sdkjs/evm-solana-transfer.md)
- [Polygon to Oasis with Relayers](./development/portal/sdkjs/polygon-oasis-relayer.md)
- [Portal Payloads]()
---
# Other Resources
- [Reference]()
- [Tools](./reference/tools.md)
- [Github & Useful Links](./reference/github.md)
- [Other Resources](./reference/overview.md)
- [Glossary](./reference/glossary.md)
- [Useful Links](./reference/usefulLinks.md)
- [Deployed Contracts](./reference/contracts.md)
- [RPC Nodes](./reference/rpcnodes.md)

View File

@ -1,4 +1,4 @@
# Dapp Basics
# Ecosystem Basics
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 **Layer 1s**.
@ -20,4 +20,6 @@ Two notable limitations are that blockchains have no access to off-chain data an
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.
---
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.

View File

@ -1,10 +1,8 @@
# Cross-Chain Interoperability
# Blockchain Interoperability
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 refered to as centralized exchanges (CEXs).
Centralized exchanges play an important role in cryptocurrency, but they are not a complete solution for cross-chain interoperability for two primary reasons: they're centralized, which is counterproductive to creating a decentralized platform, and they deal only with tokens.
As blockchains move toward being general-purpose computing platforms, interoperability will require data structures that are more complex than tokens and operations that are more sophisticated than transfers.
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.
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 **bridge**.
@ -17,16 +15,14 @@ There are some other essential things you should know about bridges before going
- Bridges are all mutually incompatible with eachother. Using multiple bridges just makes 'double wrapped' tokens.
- 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.
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 userbases and wallet incompatibilities is the result.
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 userbases and wallet incompatibilities is the result.
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.
More ad-hoc solutions would only be short-term fixes for long-term problems, so it's critical to design new primatives and core infrastructure that will allow the next generation of decentralized applications to move beyond these lingering limitations.
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.
## Branded Terms
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 greatly differ from Wormhole definition, though Wormhole's definitions may be more narrow than general interpretations.
**xChain** - Across the Wormhole ecosystem, the full range of cross-blockchain interoperability is referred to under the term "xChain." "xChain" is the concept that houses other branded terms, like the Wormhole definitions of xAssets, xData and xApps.
---
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.

View File

@ -2,7 +2,7 @@
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.
That thought underpins the idea of an **xAsset**, which could be considered a next-generation _wrapped token_. In a sense, xAssets exist on a layer _outside_ 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.
That thought underpins the idea of an **xAsset**, which could be considered a next-generation _wrapped token_. In a sense, xAssets exist on a layer _outside_ 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.
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 **xData**.
@ -10,12 +10,6 @@ xData is akin to an xAsset in that it exists in its own layer independent of any
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 reservior, application design can take on innovative new dimensions.
## Branded Terms
---
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 greatly differ from Wormhole definition, though Wormhole's definitions may be more narrow than general interpretations.
**xData** - Wormhole defines xData as "data that exists in a layer outside of Layer 1 blockchains, which is accessible by all chains." The Wormhole definition of xData presents it as a branded element of xChain.
**xAssets** - Wormhole defines xAssets as 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." The Wormhole definition of xAssets presents itself as an element of xChain.
Later in this document, we'll delve deeper into how Wormhole implements this xData layer (also referred to as the 'Core' layer of Wormhole), but for now let's talk about how xData can be used to create xDapps.
Later in this document, we'll delve deeper into how Wormhole implements this [xData layer](../wormhole/3_coreLayerContracts.md) (also referred to as the 'Core' layer of Wormhole), but for now let's talk about how xData can be used to create xDapps.

View File

@ -4,16 +4,12 @@ The term **xDapp** is short for "Cross-Chain Decentralized Application". At firs
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.
Something we'll explore further in the upcoming xDapp Architecture chapter is the philosophy of **Protocol-First Design**. 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.
Something we'll explore further in the upcoming xDapp Architecture chapter is the philosophy of [**Protocol-First Design**](./architecture/3_protocolDesign.md). 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.
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.
Thus, a more fitting depiction of xDapps might be to see them as **Distributed Decentralized Applications** with multiple, specialized components working in unison to deliver a smooth, unified user experience across a variety of layer 1 ecosystems.
## Branded Terms
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 greatly differ from Wormhole definition, though Wormhole's definitions may be more narrow than general interpretations.
**xApp** - In the Wormhole xChain ecosystem, the term "xDapp" has been shortened to "xApp." These cross-chain applications are largely still decentralized, but for branding and simplicity purposes, the term "xApp" will be prioritized over "xDapp" when talking about Wormhole's xChain ecosystem.
---
In the next section, we'll summarize the concrete advantages which xDapps built on Wormhole have over traditional Dapps today.

View File

@ -8,11 +8,11 @@ Here are a few xDapp features that are making an impact across blockchain techno
- **Decentralization** - 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.
- **Increased Performance** - 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.
- **Increased Performance** - 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.
- **Broader Market Reach** - Because xAssets move freely through the ecosystem, they can be listed on a variety of exchanges and custodied on any blockchain.
- **Increased Extensibility and Composeability** - xDapps can utilize anything across the ecosystem, including other xDapps, expanding upon the composability and openness of smart contracts.
- **Increased Extensibility and Composability** - xDapps can utilize anything across the ecosystem, including other xDapps, expanding upon the composability and openness of smart contracts.
- **Futureproofing** - As new environments and protocols join the decentralized ecosystem, the connected nature of the Wormhole ecosystem allows existing protocols to expand and support them.

View File

@ -1,5 +1,5 @@
# xDapp Design
Now that we've established the concepts and major components underlying xDapps, let's dive into the process of designing one. This chapter will help guide you through the considerations you should make before developing an xDapp, including topics like network topology, protocol design, and more.
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.
By the end of this chapter, you should have all the tools you need to lay out a design for your xDapp and start building.
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.

View File

@ -1,19 +1,19 @@
# Key Considerations
Before we get started, we should outline the key considerations which will shape your xDapp. Over the course of the chapter, we'll elaborate on how decisions made about these key items impact the way you may want to structure your application as a whole.
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.
### Why?
Why you are building an xDapp is definitely the foremost consideration. In the first chapter, we went over the [advantages](../5_advantages.md) of cross-chain development. Which of these advantages 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 which only exist in certain ecosystems? Deciding what your key priorities are will help you make the correct technical decisions and tradeoffs when designing your xDapp.
The reason you're building an xDapp is the foremost consideration. Think about the [advantages](../5_advantages.md) 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.
### Target Ecosystems & Languages
Which blockchains do you intend to support? Due to the fact that blockchains utilize different virtual machines, supporting more blockchains often (but not always) requires writing smart contracts in more than one language.
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.
### Data Flows
Where does your data originate from and where does it have to go? Does all your data come from user-initiated transactions? Do you have governance messages which need to be emitted from a central governance contract? Do you have automated actions which need to happen periodically to synchronize your data?
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?
### Liquidity & Tokens
Not all xDapps deal with tokens, but certainly quite a few do. If your app is centered around tokens, you'll have to decide what tokens will be utilized, where liquidity is aggregated (or fractured), and how this liquidity can be best utilized across your application.
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.

View File

@ -1,10 +1,10 @@
# Ecosystems
At present, there are 3 ecosystems supported by Wormhole, though the number of supported ecosystems is always growing.
At present, there are 6 ecosystems supported by Wormhole, though the number of supported ecosystems is always growing.
### EVM
EVM is the most popular ecosystem, and most xDapps will have some support for this platform. These contracts are written in Solidity, and it is generally 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.
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.
Example chains:
@ -21,12 +21,24 @@ Example chains:
### Solana
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.
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.
### Cosmos
Cosmos is a network of blockchains which share a common ecosystem. Cosmos is a general purpose environment, but excels in certain areas such as application-specific blockchains, and having Cosmos-wide standards via its sdk 'modules'. It uses CosmWasm for its smart contract runtime, which is based in Rust.
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.
### Algorand
Algorand is a leading blockchain on the state proof front and repesents the bleeding edge of trustlessness. These contracts are written in Python.
### Aptos
Aptos is characterized by its optimisitic approach to computation parallelization to bring increased performance. These contracts are written in Move.
### NEAR
NEAR is characterized by its sharding technology that may allow for greater transaction capacity and security. These contracts are written in Rust.
### Read-Only Chains
Some chains in the Wormhole ecosystem are 'Read-Only'. These chains are able to verify messages which are emitted from other chains in the network, but are not able to emit messages themselves. For information about these chains, check the [contracts page](../../reference/contracts.md).
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 [contracts page](../../reference/contracts.md).

View File

@ -1,16 +1,18 @@
# Protocol Design
Wormhole's key feature is that it brings message passing to the world of blockchain. As such, it's worthwhile to take some inspiration from other areas of software development which are based on similar principles.
They 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.
Much of the traditional web stack is based on distributed systems, which rely on message passing to create well-defined interfaces and boundaries for disparate systems to work together. We should similarly think of xDapps as web3 distributed systems founded upon similar paradigms.
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.
## Protocol First Design
Protocol first design is a design philosophy where you first 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, and the code instantiating that protocol can be treated as an implementation detail when reasoning about the protocol itself.
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.
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? This stage of the process could be as simple as stating "people will want my NFT", or as difficult as designing an entire ecosystem with multiple competing interested parties. It depends on what your goals are.
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?
Once you have a clear idea of your core product, incentives, and users, you can begin to lay out your data model, and from there define your message types and operations.
Depending on your goals, this stage of the process can be as simple as stating "people will want my NFT" or as difficult as designing an entire ecosystem with multiple competing interested parties.
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.
## Common Strategies and Conventions
@ -18,14 +20,14 @@ Once you have a clear idea of your core product, incentives, and users, you can
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.
A Wormhole address is a tuple containing the 2 byte Wormhole chain ID, and also a 32 byte shim address. The Wormhole address format is therefore 34 bytes.
A Wormhole address is a tuple containing the 2 byte Wormhole chain ID and a 32 byte shim address, totalling 34 bytes.
For example, 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.
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.
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 this format when interacting with other parts of the Wormhole ecosystem, and adopting this format in your protocol will enhance your forward compatibility as you add more chains.
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.
### Trusted Contract Network
If your protocol has smart contracts deployed to multipe chains, it will be important for your contracts to know which other contracts are 'in network' for your protocol, as commonly the first check performed when receiving a message is to validate that it originates from a trusted source.
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 multipe chains, it will be important for your contracts to know which other contracts are 'in network' for your protocol.
Generally, this list of trusted contracts is stored in the state of each contract individually, and updating the trusted contracts is tied into the governance mechanism of the protocol.
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.

View File

@ -1,16 +1,22 @@
# Topology
Topology describes how data flows through your application, and the responsibilities of each component. The primary decision to be made with regard to topology is mainly where your smart contracts will live, and what responsibilities each contract holds.
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.
## Ultra-light Clients
![Ultra-light client](../../diagrams/images/ultralight.png "Ultra Light Clients")
![Ultra-light client](../../diagrams/images/ultralight_2.png "Ultra Light Clients")
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 **only having smart contracts on a single chain (!!!)**.
This works by deploying a single _hub_ contract (or just using an existing Dapp) onto the hub chain. You then add an entrypoint which supports _contract controlled transfers_ from the xAsset contracts on the hub chain. This allows your hub contract to receive both tokens **and instructions for what to do with them** from other chains in the Wormhole ecosystem.
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.
From there, the hub contract performs any necessary operations, and bridges any resultant tokens back to the wallet which iniated the contract controlled transfer. The only on-chain components are hub contract, and a relatively lightweight wrapper which allows the hub contract to send and receive tokens using the xAsset contracts. All other aspects of this topology are off-chain and untrusted. This pushes most of the development work out of smart contracts and into client-side typescript. This dramatically decreases smart contract risk, without altering the trust assumptions of your application.
You can think of the data flow across a xDapp with a Ultra-light Client as follows:
1. The end user's wallet interacts with Wormhole contracts on remote chain.
2. The Wormhole contracts on remote chain generate a VAA which is received by your xDapp contract on the hub chain.
3. Your xDapp contract on the hub chain performs all necessary operations.
4. Your xDapp contract interacts with Wormhole contracts on the hub chain.
5. The Wormhole contracts on hub chain generate a VAA which is sent back to the end user's wallet on the remote chain.
**_Advantages:_**
@ -20,20 +26,31 @@ From there, the hub contract performs any necessary operations, and bridges any
**_Disadvantages:_**
- Latency: Because all transactions have to bridge in and out of the hub chain, each transaction incurs the finality latency of both the remote and hub chain.
- Latency: Transactions incur latencies associated with bridging into and out of both the remote and hub chain.
- Transaction Fees: There are always a grand total of three transactions. Two on the remote chain, and one on the hub chain.
- 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).
## Hub and Spoke
## Hub-and-Spoke
Hub and Spoke models are somewhat of a 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.
![Hub and Spoke](../../diagrams/images/hub_and_spoke.PNG "Hub and Spoke")
Advantages:
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.
You can think of the data flow across a Hub-and-Spoke system as follows:
1. The end user's wallet interacts with your (lightweight) remote contracts.
2. The remote contracts perform any necessary trusted computation.
3. The remote contracts use Wormhole to generate a VAA, which is consumed by the hub contract.
4. The hub contract performs all necessary operations.
5. The hub contract uses Wormhole to send a message back to the original remote contract.
6. The remote contract takes whatever action is needed to finish the process.
**_Advantages:_**
- Remote contracts are lightweight and don't carry large amounts of risk.
- Can perform trusted checks on the remote chain. (Such as validating wallet balance, or any other piece of blockchain state)
Disadvantages:
**_Disadvantages:_**
- Latency (same as ultra-light clients)
- Transaction Fees
@ -41,14 +58,18 @@ Disadvantages:
## Mesh
A Mesh topology is one where each chain implements the full logic for a process, such that each contract is a peer of other contracts in the trusted network and can act autonomously.
![Mesh](../../diagrams/images/mesh.PNG "Mesh")
Advantages:
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.
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.
**_Advantages:_**
- Latency: Users can often perform their operation without waiting for other chains.
- Transaction Fees: Does not stack the transaction fees of multiple chains.
Disadvantages:
**_Disadvantages:_**
- Complexity: there are now quite a few contracts to manage, especially if they are implemented multiple times across different VMs.
- Data desync: because each blockchain acts independently, each chain will have independent state. This can open up unwanted arbitrage opportunities and other discrepancies.
@ -56,7 +77,9 @@ Disadvantages:
## Distributed
A distributed topology is one where different blockchains have different responsibilities.
![Distributed](../../diagrams/images/distributed.PNG "Distributed")
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.
Advantages:

View File

@ -1,30 +1,28 @@
# Relayers
In Chapter 2, we discussed the [general concepts associated with relayers in the Wormhole ecosystem](../../wormhole/6_relayers.md). In this section, we'll elaborate on what considerations need to be taken into account when using relayers for your xDapp.
In Chapter 2, we discussed the [general concepts associated with relayers in the Wormhole ecosystem](../../wormhole/6_relayers.md). In this section, we'll elaborate on the considerations that should be accounted for when using relayers in your xDapp.
## Fundamentals
The most important thing to remember about relayers is that they 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.
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.
Let's dive into a little more detail about _why_ relayers are untrusted, and what this means for you when designing a protocol.
Let's dive into a little more detail about why relayers are untrusted and what this means for you.
A few key properties of VAAs are that they:
A few key properties of VAAs:
- are publicly emitted from the Guardian Network
- They are publicly emitted from the Guardian Network.
- They need to be signed by the Guardian Network to be considered authentic.
- They can be verified as authentic by anyone and any Wormhole Core Contract.
- need to be signed by the Guardian Network to be considered authentic
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.
- can be verified as authentic by _any_ Wormhole Core Contract they are brought to _by anyone_.
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.
Relayers are untrusted as an inherent consequence of these properties. Anyone can pick up a VAA and deliver it anywhere they feel like, however, no one can alter the content of the VAA without invalidating the signatures.
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.
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.
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.
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.
There are more advanced strategies whereby the relayer performs **untrusted** off-chain computation which is passed into the destination contract. These strategies can optimize gas costs, but must be used carefully, as they can create attack vectors if not used correctly.
With this in mind, relayer design is mostly a matter of structuring the messages in your protocol in a manner such that there is a single, deterministic way that they can be processed. In a well designed protocol, relayers have a 'correct' implementation.
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.
Relayers are conceptually quite similar to "crank turner" 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.
@ -32,42 +30,42 @@ Relayers are conceptually quite similar to "crank turner" processes used elsewhe
## Generic Relayers
Generic Relayers are a decentralized relayer network which can deliver arbitrary VAAs, so long as the recipient contract is conformant with the generic relayer API.
Generic relayers are a decentralized relayer network which can deliver arbitrary VAAs as long as the recipient contract conforms with the generic relayer API.
### Advantages:
**_Advantages:_**
- Purely done on-chain. No need to develop, host, or maintain relayers
- Generic relayers are done purely on-chain, so there's no need to develop, host or maintain relayers.
### Disadvantages:
**_Disadvantages:_**
- Less room for optimization via features like conditional delivery, batching, off-chain calculations, etc.
- There's less room for optimization via features like conditional delivery, batching, off-chain calculations, etc.
---
## Specialized Relayers
Specialized Relayers are relayers which are purpose-built to relay messages for a certain application.
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.
### Advantages:
**_Advantages:_**
- Can perform off-chain untrusted computation
- Highly customizeable. Can perform batching, conditional delivery, multi-chain deliveries, etc.
- Can home-roll an incentive structure
- Specialized relayers can perform off-chain untrusted computation.
- They are highly customizeable and can perform batching, conditional delivery, multi-chain deliveries, etc.
- Can home-roll an incentive structure.
### Disadvantages
**_Disadvantages_**
- Requires development work, and requires relayer hosting
- Requires development work and relayer hosting.
In the future, there may be ways to customize generic relayers such that they will gain the advantages of today's specialized relayers.
---
### Relayer Incentives
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.
There are tons of strategies here, 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:
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:
- pay the relayer with a potion of the tokens being sent cross-chain
- collect a safe amount of gas money from the end user prior to performing any actions
- 'lazy' relaying, where relaying might only become profitable in certain, potentially rare, market conditions
- Pay the relayer with a potion of the tokens being sent cross-chain.
- Collect a safe amount of gas money from the end user prior to performing any actions.
- 'Lazy' relaying, where relaying might only be profitable in certain, potentially rare, market conditions.
Generic relayers have an incentive model built in to the network, so you do not need to design an incentive structure when using them.

View File

@ -2,14 +2,14 @@
Relaying Messages can be done one of three ways:
1. Manual Relaying
**_Manual Relaying_**
Manual Relaying is usally done on the front end. Manual relyaing requires the front end to fetch the VAA it just created and then submit on the target chain. This means the user ends up paying for the gas fee and has to go through the additional step to submit the tx on the target chain.
Manual relaying is usally done on the front end. Manual relaying requires the front end to fetch the VAA it just created and then submit on the target chain. This means the user ends up paying for the gas fee and has to go through the additional step to submit the tx on the target chain.
2. Protocol Specific Relayers
**_Protocol-Specific Relayers_**
Protocols and Apps can run their own relayers, listening to messages as they are created by the Core Bridge and submitting them to their application on the target chain. This is the ideal user experience but requires more work from the developer.
Protocols and apps can run their own relayers that listen to messages as they are created by the Core Bridge and submit them to their application on the target chain. This offers the ideal user experience, but requires more work from the developer.
3. Generic Relayers
**_Generic Relayers_**
Generic Relayers can pick up any app or protocol's messages and submit them to the target chain for a fee. This is the ideal developer and user experience, but is still being developed.
Generic relayers can pick up any app or protocol's messages and submit them to the target chain for a fee. This is the ideal developer and user experience, but is still evolving.

View File

@ -1,23 +1,24 @@
# Wormhole Development Overview
The general flow for a cross-chain message goes from an application deployed to chain A, to the Wormhole contract on chain A, to the Guardian network, then submitted to chain B.
Cross-chain messages typically flow from an application deployed to a blockchain to the Wormhole contract on that same chain, then to the Guardian network before finally being submitted to a host chain.
To get started and simulate this flow locally, you'll need a local environment to test your xdapp code. To test, we need to be able to deploy some chains, deploy the Wormhole contracts to these chains, and then run at least one Wormhole validator to pick up messages.
To test your xDapp code, you'll need a local environment to simulate this process. Your local environment needs to be able to deploy multiple chains, deploy Wormhole contracts to those chains and run at least one Wormhole validator to pick up messages.
Later, we can introduce a relayer to automatically submit messages, though that's currently supported for Mainnet Token Bridge native and stable coin transfers only. Developers currently have to use either a manual relayer method or an app-sepecific relayer (more on that in the Relayer section).
Later, we can introduce a relayer to automatically submit messages, though this is currently supported for Mainnet Token Bridge native and stable coin transfers only. Currently, developers need to use either a manual relayer method or an app-sepecific relayer. You can find more details on relayers in the Relayers section of this guide.
Before we setup an xdapp project, we'll need to choose a local environment to run the Wormhole Guardian Network. We can use either Wormhole Local Validator or Tilt.
Before we set up an xDapp project, we'll need to choose a local environment to run the Wormhole Guardian Network. Our best options are either Wormhole Local Validator or Tilt.
- [Wormhole Local Validator](./wormhole-local-validator.md): This is the simplest custom environment. It's BYOB (Bring your own Blockchain), where you can run your own local validator nodes and connect them to a single Guardian running on docker. Initial setup can take upwards of 500 seconds, but after the image is built, bringing it up and down is usually <1 minute. This environment requires installing the software for the validator nodes locally on your computer or somewhere to run them.
- [Tilt](./tilt/overview.md): A full-fledged Kubernetes deployment of *every* chain connected to Wormhole, along with a Guardian node. Usually takes 30 min to spin up fully, but comes with all chains running out of the box.
- [Wormhole Local Validator](./wormhole-local-validator.md): This is the simplest custom environment. It's BYOB (Bring your own Blockchain) with the ability to run your own local validator nodes and connect them to a single Guardian running on docker. Initial setup can take upwards of 500 seconds, but after the image is built, bringing it up and down should take less than a minute. This environment requires installing the software for the validator nodes locally.
- [Tilt](./tilt/overview.md): A full-fledged Kubernetes deployment of every chain connected to Wormhole, along with a Guardian node. Usually takes 30 min to spin up fully, but comes with all chains running out of the box.
### First Steps
To get started, first clone the a local host environment (WLV or Tilt), then proceed to the first project, the [evm-messenger](../projects/evm-messenger/overview.md).
### Testnet
If you want to test on the various test and devnets of existing connected chains, there's a single Guardian node watching for transactions on various test networks. You can find the contracts [here](../reference/contracts.md) and the rpc node [here](../reference/rpcnodes.md).
If you want to test on the various test and devnets of existing connected chains, there's a single Guardian node watching for transactions on various test networks. You can find the contracts at [../reference/contracts.md](../reference/contracts.md) and the rpc node at [../reference/rpcnodes.md](../reference/rpcnodes.md).
Because testnet only has a single Guardian, there's a small chance that your VAAs will not be processed. This rate is *not* indiciative of performance on mainnet, where there are 19 Guardians watching for transactions.
Because testnet only has a single Guardian, there's a small chance that your VAAs will not be processed. This rate is not indiciative of performance on mainnet, where there are 19 Guardians watching for transactions.
### Mainnet
When you're ready to deploy to mainnet, you can find the mainnet contracts [here](../reference/contracts.md) and the mainnet rpc nodes [here](../reference/rpcnodes.md).
## Next Steps
To get started, first clone the a local host environment (WLV or Tilt), then proceed to the first project, the [evm-messenger](../projects/evm-messenger/overview.md).
When you're ready to deploy to mainnet, you can find the mainnet contracts at [../reference/contracts.md](../reference/contracts.md) and the mainnet rpc nodes at [../reference/rpcnodes.md](../reference/rpcnodes.md).

View File

@ -1,22 +1,24 @@
# Portal Token Bridge Transfers
# Portal Token Bridge
Portal Token Bridge is one of the biggest applications built on Wormhole. It uses structured payloads to transfer tokens and NFTs from one wallet to another.
## Attesting a Token
Before a token can be transferred, the token need to be *attested* to another chain. To attest a token, you first create a AssetMeta VAA by calling the `attest()` function on Token Bridge. Then you take the VAA over to the receipient chain, where you call `createWrapped()` which deploys a wrapped version of the Token.
Before a token can be transferred, the token need to be attested to another chain. To attest a token, you first create a AssetMeta VAA by calling the `attest()` function on Token Bridge. Then, take the VAA over to the receipient chain where you'll call `createWrapped()` which deploys a wrapped version of the token.
This only needs to happen *once* per payload, and trying to attest a token a second time will simply result in the address of the already-created Wrapped Token Address.
This only needs to happen once per payload, and trying to attest a token a second time will simply result in the address of the already-created Wrapped Token Address.
## Transfering Tokens
To transfer tokens, the payer of tokens first authorizes the Token Bridge contract to move the tokens on their behalf, then locks them up with the Token Bridge, which then emits a VAA. This VAA can then be submitted on target chain's Token Bridge's `completeTransfer()` to mint the wrapped version of the Token.
To transfer tokens, the payer of tokens first authorizes the Token Bridge contract to move the tokens on their behalf, then locks them up with the Token Bridge, which emits a VAA. This VAA can then be submitted on target chain Token Bridge's `completeTransfer()` to mint the wrapped version of the token.
When transfering tokens from Chain A to B and beyond, the token is only "wrapped" once, as every time it's attested, it's always from the chain the token is natively located on. If the token being transferred is native to the chain it's being transfered to, you'll receive the original token back instread of a wrapped version.
When transfering tokens from Chain A to B and beyond, the token is only "wrapped" once, as every time it's attested, it's always from the chain where the token is natively located. If the token being transferred is native to the chain it's being transfered to, you'll receive the original token back instread of a wrapped version.
There are typically two functions for transfer: `transfer()` and `transferNative()`. This is because native currencies of most blockchains (ETH on Ethereum, SOL on Solana, etc) don't follow the Token spec of that chain, so to transfer native currencies, we wrap them first into a tokenized version and then transfer.
There are typically two functions for transfer: `transfer()` and `transferNative()`. This is because native currencies of most blockchains (ETH on Ethereum, SOL on Solana, etc) don't follow the token spec of that chain, so to transfer native currencies, we wrap them first into a tokenized version and then transfer.
For transfers, there's also an arbiterFee you can set. If this fee is set, when `completeTransfer()` is called, that amount of tokens are withheld from the release of tokens on the target chain and instead given to the submitter of the message (for example, a relayer). This allows third party to submit transactions on your behalf, for a fee.
For transfers, there's also an arbiterFee you can set. If this fee is set, when `completeTransfer()` is called, that amount of tokens are withheld from the release of tokens on the target chain and instead given to the submitter of the message (for example, a relayer). This allows a third party to submit transactions on your behalf, for a fee.
## Transfering with a Payload
Transfering with a Payload is much like transfering normal tokens, with two major differences.
Transfering with a Payload is much like transfering normal tokens, with two major differences:
First, as the name implies, you can attach a bytes payload to the transfer message. Secondly, the `completeTransfer()` function for Transfer with Payload can *only* be called by the receipient of that VAA. This means the flow is slightly different; instead of the user calling the `completeTransfer()` function on the Token Bridge, they call a function on the application they are interacting with which will check the payload, do any state changes it needs to make and then call `completeTransfer()` on Token Bridge to mint tokens to itself.
First, as the name implies, you can attach a bytes payload to the transfer message.
Secondly, the `completeTransfer()` function for Transfer with Payload can *only* be called by the receipient of that VAA. This means the flow is slightly different--instead of the user calling the `completeTransfer()` function on the Token Bridge, they call a function on the application they are interacting with which will check the payload, finalize any state changes and then call `completeTransfer()` on Token Bridge to mint tokens to itself.

View File

@ -1,6 +1,6 @@
# Portal JS SDK Overview
# Wormhole Typescript SDK Overview
For applications that only need to interact with the Core and Token Bridge contracts off-chain, there is a Wormhole JS SDK provided.
For applications that only need to interact with the Core and Token Bridge contracts off-chain, there is a Wormhole Typescript SDK provided.
It can be installed using npm:

View File

@ -1,10 +1,10 @@
# Polygon to Oasis with Relayers
In this example, well fetch the fee schedule and attach a relayer fee onto our transaction. This is a non-trivial example as well also use Polygon as a source chain, which has some quirks when it comes to gas estimation.
In this example, well fetch the fee schedule and attach a relayer fee onto our transaction. This is a non-trivial example as well also use Polygon as a source chain, which has some quirks when it comes to gas estimation.
NOTE: We're working on streamlining this process, so check back in the future for a much simpler version of this example.
To start, well need a couple of packages:
To start, well need a couple of packages:
```bash
npm i --save @certusone/wormhole-sdk ethers node-fetch
@ -16,15 +16,15 @@ Then, get started writing some code:
import { BigNumber, ethers } from "ethers";
import fetch from "node-fetch";
import {
getEmitterAddressEth,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
CHAIN_ID_POLYGON,
CHAIN_ID_OASIS,
transferFromEthNative,
getIsTransferCompletedEth,
setDefaultWasm
getEmitterAddressEth,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
CHAIN_ID_POLYGON,
CHAIN_ID_OASIS,
transferFromEthNative,
getIsTransferCompletedEth,
setDefaultWasm,
} from "@certusone/wormhole-sdk";
```
@ -34,56 +34,59 @@ Now, set up the two wallets well be sending and receiving from. While we are
```ts
const EmeraldWallet = new ethers.Wallet(
privatekey_emerald,
new ethers.providers.JsonRpcProvider("https://emerald.oasis.dev")
privatekey_emerald,
new ethers.providers.JsonRpcProvider("https://emerald.oasis.dev")
);
const PolygonWallet = new ethers.Wallet(
privatekey_polygon,
new ethers.providers.JsonRpcProvider("https://polygon-rpc.com/")
privatekey_polygon,
new ethers.providers.JsonRpcProvider("https://polygon-rpc.com/")
);
```
### Fetch the fee schedule
Fetch the fee schedule for the Portal Token Bridge relayer. This fee schedule outlines the minimum fee for each recipient chain that the relayer will accept. As long as we attach at least that fee in the relayer fee, we can be fairly confident that the relayer will 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.
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 in BigNumber format (no decimals).
Fetch the fee schedule for the token bridge relayers. This fee schedule outlines the minimum fee for each recipient chain that the relayer will accept. As long as we attach at least that fee in the relayer fee, we can be fairly confident that the relayer will 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.
For example, 1 MATIC on Polygon is 1e18 wei, or 1000000000000000000 wei. Because EVM has a hard time with floating point math, we have to do all our transactions in this small unit, to avoid decimal numbers.
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 in BigNumber format (no decimals).
For example, 1 MATIC on Polygon is 1e18 wei, or 1000000000000000000 wei. Because EVM has a hard time with floating point math, we have to do all our transactions in this small unit, to avoid decimal numbers.
```ts
const transferAmount = BigNumber.from("1000000000000000000"); // We are sending 1 MATIC over the wall to Oasis
const relayerFeeSchedule = await (await fetch(
"https://raw.githubusercontent.com/certusone/wormhole-relayer-list/main/relayer.json"
)).json();
const relayerFeeSchedule = await(
await fetch(
"https://raw.githubusercontent.com/certusone/wormhole-relayer-list/main/relayer.json"
)
).json();
```
The fee schedule has the following interface:
```ts
export interface RelayerFeeSchedule {
supportedTokens: ChainAddress[];
relayers: Relayer[];
feeSchedule: FeeSchedule;
supportedTokens: ChainAddress[];
relayers: Relayer[];
feeSchedule: FeeSchedule;
}
interface ChainAddress {
chainId: number;
address: string;
coingeckoId: string;
chainId: number;
address: string;
coingeckoId: string;
}
interface Relayer {
name: string;
url: string;
name: string;
url: string;
}
interface FeeSchedule {
[chainId: string]: {
type: "flat" | "percent";
feeUsd?: number;
feePercent?: number;
gasEstimate?: number;
};
[chainId: string]: {
type: "flat" | "percent";
feeUsd?: number;
feePercent?: number;
gasEstimate?: number;
};
}
```
@ -92,70 +95,65 @@ After fetching the fee schedule, find the fee in wei that needs to be paid to th
```ts
let feeWei: number;
if (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].type == "flat") {
const feeUsd = relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feeUsd
const MATIC_PRICE = (
await (
await fetch(
"https://api.coingecko.com/api/v3/simple/token_price/polygon-pos?contract_addresses=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270&vs_currencies=usd"
)
).json()
)["0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270"]["usd"];
const feeUsd = relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feeUsd;
const MATIC_PRICE = await(
await fetch(
"https://api.coingecko.com/api/v3/simple/token_price/polygon-pos?contract_addresses=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270&vs_currencies=usd"
)
).json()["0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270"]["usd"];
feeWei = (feeUsd / MATIC_PRICE) * 1e18;
feeWei = (feeUsd / MATIC_PRICE) * 1e18;
} else if (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].type == "percent") {
let feeWei = (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feePercent /100) * transferAmount.toNumber();
let feeWei =
(relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feePercent / 100) *
transferAmount.toNumber();
}
```
### Add override for gas estimation for Polygon
When the source chain is Polygon, there's an additional step to overestimate the gas. This is because Ethers library has some problems with fee estimation after EIP-1559.
```ts
let overrides;
let feeData = await PolygonWallet.provider.getFeeData();
overrides = {
maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
maxPriorityFeePerGas:
feeData.maxPriorityFeePerGas?.mul(50) || undefined,
maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(50) || undefined,
};
```
### Emit Portal Message
Now we have all the pieces we need to emit a Portal 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.
### Emit Token Bridge Message
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.
```ts
const POLYGON_TOKEN_BRIDGE = "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE";
const receipt = await transferFromEthNative(
POLYGON_TOKEN_BRIDGE,
PolygonWallet,
transferAmount,
CHAIN_ID_OASIS,
hexToUint8Array(
nativeToHexString(
await EmeraldWallet.getAddress(),
CHAIN_ID_OASIS
) || ""
),
BigNumber.from(feeWei.toString()),
overrides
POLYGON_TOKEN_BRIDGE,
PolygonWallet,
transferAmount,
CHAIN_ID_OASIS,
hexToUint8Array(
nativeToHexString(await EmeraldWallet.getAddress(), CHAIN_ID_OASIS) || ""
),
BigNumber.from(feeWei.toString()),
overrides
);
console.log("Receipt: ", receipt);
const POLYGON_CORE_BRIDGE_ADDRESS =
"0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7";
const sequence = parseSequenceFromLogEth(
receipt,
POLYGON_CORE_BRIDGE_ADDRESS
);
"0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7";
const sequence = parseSequenceFromLogEth(receipt, POLYGON_CORE_BRIDGE_ADDRESS);
const emitterAddress = getEmitterAddressEth(POLYGON_TOKEN_BRIDGE);
console.log("Sequence: ", sequence);
console.log("EmitterAddress: ", emitterAddress);
```
Lets walk through each of the arguments of this function and what they mean.
Lets walk through each of the arguments of this function and what they mean.
`POLYGON_TOKEN_BRIDGE` is the address of the Portal Token Bridge on the Polygon network. You can find it and other addresses on the Deployment Info page.
`POLYGON_TOKEN_BRIDGE` is the address of the token bridge module on the Polygon network. You can find it and other addresses on the Deployment Info page.
`PolygonWallet` is a signer you get from the Ethers library that holds a private key that can sign transactions.
@ -165,7 +163,7 @@ Lets walk through each of the arguments of this function and what they mean.
`hexToUint8Array()` translates the target publickey into a wormhole public key.
`BigNumber.from(feeWei.toString())` identifies the fee in smallest unit of the network for the relayer.
`BigNumber.from(feeWei.toString())` identifies the fee in smallest unit of the network for the relayer.
`overrides` are used if we need to override the gas cost, which we need to do for Polygon.
@ -178,17 +176,15 @@ await new Promise((r) => setTimeout(r, 900000)); //15m in seconds
const WORMHOLE_RPC = "https://wormhole-v2-mainnet-api.certus.one";
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) => setTimeout(r, 5000));
}
try {
vaaBytes = await(
await fetch(
`${WORMHOLE_RPC}/v1/signed_vaa/${CHAIN_ID_POLYGON}/${emitterAddress}/${sequence}`
)
).json().vaaBytes;
} catch (e) {
await new Promise((r) => setTimeout(r, 5000));
}
}
console.log("VAA Bytes: ", vaaBytes);
```
@ -201,17 +197,17 @@ In the final step, use the getIsTransferCompletedEth() method to check if the tr
setDefaultWasm("node"); //only needed if running in node.js
const EMERALD_TOKEN_BRIDGE = "0x5848C791e09901b40A9Ef749f2a6735b418d7564";
let transferCompleted = await getIsTransferCompletedEth(
EMERALD_TOKEN_BRIDGE,
EmeraldWallet.provider,
vaaBytes
);
while (!transferCompleted) {
await new Promise((r) => setTimeout(r, 5000));
transferCompleted = await getIsTransferCompletedEth(
EMERALD_TOKEN_BRIDGE,
EmeraldWallet.provider,
vaaBytes
);
while (!transferCompleted) {
await new Promise((r) => setTimeout(r, 5000));
transferCompleted = await getIsTransferCompletedEth(
EMERALD_TOKEN_BRIDGE,
EmeraldWallet.provider,
vaaBytes
);
);
}
console.log("VAA Relayed!");

View File

@ -1,21 +1,19 @@
# xDapp Scaffold
To help you get started with cross chain development, we've provided a template project in `projects/xdapp-starter`. All the sample projects will be made using this template, so check them out if you want to get a feel for how the various modules interact with each other.
To help you get started with cross-chain development, we've provided a template project in `projects/xdapp-starter`. All sample projects will be made using this template, so check them out if you want to get a feel for how various modules interact with each other.
The template uses npm workspaces to setup a main project with subdirectories for each chain you want to interact with. This allows you to initialize each subdirectory using whatever scaffolding tool you want for each individual chain, and orchestration code in a common directory.
The template uses npm workspaces to setup a main project with subdirectories for each supported chain. This allows you to initialize each subdirectory using any scaffolding tool for each individual chain along with orchestration code in a common directory.
Let's break down what's in the `xdapp-starter` project:
What's in the `xdapp-starter` project:
### chains/
- This folder contains the subdirectories for chain specific code. For example, I might use the `anchor` tool to `anchor init solana-project` within the chains/ directory.
This folder contains the subdirectories for chain-specific code. For example, you might use the `anchor` tool to `anchor init solana-project` within the chains/ directory.
### handlers/
The handlers folder contains the js client code to deal with each chain's specific needs. They expose a common API that we can consume in `starter.js` for code cleanliness.
They all take in a context object that's made up of the
The handlers/ folder contains the js client code to deal with each chain's specific needs. They expose a common API that we can consume in `starter.js` for code cleanliness.
### orchestrator.js
This file parses command line args and filters calls to chain management handlers.
### xdapp.config.json
The config file contains all the information about the network rpc nodes, accounts, and other constants used to communicate with contracts deployed to the selected chains.
The config file contains all the information about the network rpc nodes, accounts, and other constants used to communicate with contracts deployed to the selected chains.

View File

@ -1,19 +1,30 @@
# Contracts and Accounts
The devnet environment deploys the Wormhole and Portal contracts to each of the chains at the same addresses every time.
It also funds specific wallets with funds.
The devnet environment deploys the core layer and token bridge to each of the chains at the same addresses every time. It also provides specific wallets with funds.
## Tilt
## Guardian
- REST Port: 7071
- gRPC Port: 7070
## ETH0
- RPC Port: 8545
## ETH1
- RPC Port: 8546
## Solana
- RPC Port: 8899
## Algorand
- RPC Port:
- RPC Port:
## Terra
- RPC Port:
- RPC Port:

View File

@ -1,6 +1,16 @@
# Linux Devnet Setup
## Setup
### Experimental Setup Script
There's an experimental single command setup script that should install dependencies for you on Linux and configure everything properly. This is only recommended if you're running headless Linux and unable to use Docker Desktop, as you can enable Kubernetes from Docker.
```sh
curl $URL | sh install_linux.sh
cd wormhole/
./tilt.sh
```
## Regular Setup
### 1. Install Go
@ -13,7 +23,7 @@ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.1.linux-amd64.tar.gz
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 `minikube`. It's recommended to have at least 4 CPUs and 16GB RAM dedicated to Docker.
Also make sure that you set up docker as a NON ROOT USER!
Also, make absolutely sure that you set up Docker as a non-root user.
[https://docs.docker.com/engine/install/ubuntu/#installation-methods](https://docs.docker.com/engine/install/ubuntu/#installation-methods)
@ -23,9 +33,9 @@ Enable Kubernetes by going into Settings > Kubernetes
### 3. (Non Docker Desktop)
Install [`minikube`](https://minikube.sigs.k8s.io/docs/start/)
Install [`minikube`](https://minikube.sigs.k8s.io/docs/start/).
Configure minikube
Configure minikube:
```
minikube start --driver=docker --kubernetes-version=v1.23.3 --cpus=4 --memory=14G --disk-size=10G --namespace=wormhole
@ -35,7 +45,7 @@ If you reboot your VM you'll need to run the `minikube start` command again befo
### 4. Install Tilt
Install tilt by copy pasting this into the Terminal
Install Tilt by copy pasting this into the Terminal:
```sh
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
@ -47,14 +57,14 @@ curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/instal
git clone --branch dev.v2 https://github.com/wormhole-foundation/wormhole.git
```
If you're running tilt on your machine
If you're running Tilt on your machine:
```sh
cd wormhole/
tilt up
```
If you're running tilt in a VM, we need to pass in some extra flags to enable Tilt to listen to incoming traffic from external addresses.
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:
```sh
cd wormhole

View File

@ -1,36 +1,39 @@
# Tilt Development Environment
For a quicker development cycle, specially when developing your own blockchain programs that interact with Wormhole or Portal contracts, consider setting up the Tilt Devnet Environment.
For a faster development cycle, especially when developing blockchain programs that interact with Wormhole contracts, consider setting up the Tilt Devnet Environment.
Tilt is a kubernetes and docker orchestration tool that will spin up all the Wormhole supported chains in containers, alongside a Guardian node that will observe and store VAAs.
Tilt is a Kubernetes and Docker orchestration tool that will spin up all the Wormhole supported chains in containers, alongside a Guardian node that will observe and store VAAs.
This devnet environment can be set up on your computer or in a Linux VM that has at least 4 CPU cores and 16GB of RAM.
If you do decide to host the devnet in a remote VM, remember to pass in the host and webHost flags during the tilt up step and allow incoming traffic on your VM to be able to access the various RPC endpoints on the Pods.
If you do decide to host the devnet in a remote VM, remember to pass in the host and webHost flags during the tilt up step and allow incoming traffic on your VM to be able to access the various RPC endpoints on the pods.
```sh
tilt up --host=0.0.0.0 -- --webHost=0.0.0.0
```
While the exact commands for each environment might differ, the basic setup process for tilt is the following:
While the exact commands for each environment might differ, the basic setup process for Tilt is the following:
1. Install Go
2. Install Docker Desktop (Or Docker CE)
a. Install Minikube if Docker CE
2. Install Docker Desktop (Or Docker CE)
a. Install Minikube if Docker CE
3. Install Tilt
4. Clone Wormhole Repo and Tilt Up
## FAQ
## FAQ
### Where are Fantom/Celo/Polygon/...(insert other EVM chains)
For all chains that support EVM the smart contract development environment is effectively the same. For changes in gas costs and transaction times, consider testing contract logic on devnet and then using testnet environments to get chain specific answers.
### Where are Fantom/Celo/Polygon/other EVM chains?
### Solana is taking forever!
Unfortunately, due to Solana's architecture, it often takes 25-40min to build the Solana pod. Consider increasing CPU cores assigned to devnet for a faster build.
For all chains that support EVM, the smart contract development environment is effectively the same. For changes in gas costs and transaction times, consider testing contract logic on devnet and then using testnet environments to get chain-specific answers.
### Solana program deploy doesn't work
Kubernetes doesn't currently allow port forwarding for UDP ports, which is what Solana uses for `solana program deploy`. Instead, we recommend using [Solana Deployer](https://github.com/acheroncrypto/solana-deployer). Not only does this deploy programs over regular RPC thus bypassing UDP port requirements, it's also much faster than `solana program deploy`.
### Solana is taking forever
Due to Solana's architecture, it often takes 25-40min to build the Solana pod. Consider increasing CPU cores assigned to devnet for a faster build.
### Solana program deploy doesn't work
Kubernetes doesn't currently allow port forwarding for UDP ports, which is what Solana uses for `solana program deploy`. Instead, we recommend using [Solana Deployer](https://github.com/acheroncrypto/solana-deployer). Not only does this deploy programs over regular RPC (thus bypassing UDP port requirements), it's also much faster than `solana program deploy`.
### Reset state for a pod
If you want to quickly iterate and don't want to bring tilt down and back up, you can reset state for a pod by clicking the 🔄 button next to the pod name in Tilt UI.
If you want to quickly iterate and don't want to bring Tilt down and back up, you can reset state for a pod by clicking the 🔄 button next to the pod name in Tilt UI.

View File

@ -29,4 +29,4 @@ Simply run `npm run wormhole`, which will pull and run the Wormhole Guardian doc
### FAQ & Common Problems
- Anvil isn't working
While we recommend Foundry's Forge tool for compiling and deploying code elsewhere in these docs, we _do not_ at this time recommend using anvil for guardiand; this is because guardiand is spec'd against go-ethereum, and anvil is out of spec for how it reports block headers (non left padding to normalize length), which means go-ethereum reacts abnormally and can't read anvil headers.
While we recommend Foundry's Forge tool for compiling and deploying code elsewhere in these docs, we do not at this time recommend using anvil for guardiand; this is because guardiand is spec'd against go-ethereum, and anvil is out of spec for how it reports block headers (non left padding to normalize length), which means go-ethereum reacts abnormally and can't read anvil headers.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -1,7 +1,7 @@
# Messenger.sol
Messenger.sol is an application contract on EVM capable of communicating with the Wormhole core bridge.
Messenger.sol is an application contract on EVM capable of communicating with the Wormhole Core Bridge.
Start by hard coding the Wormhole core bridge address, and creating a interfaced link to it.
Start by hard coding the Wormhole core bridge address and creating an interfaced link to it.
```solidity
//SPDX-License-Identifier: Unlicense
@ -26,7 +26,7 @@ contract Messenger {
```
## Constructor
This sets the owner of the contract to the deployer. The owner is used later to register sibling contracts on foreign chains.
This sets the owner of the contract to the deployer. The owner is used later to register sibling contracts on other chains.
```solidity
@ -39,10 +39,10 @@ constructor(){
## SendMsg
This takes in a bytes payload and calls the Wormhole Core Bridge to publish the bytes as a message.
The `publishMessage()` function of the core_bridge take three arguements:
- Nonce: a number to uniquely identify this message, used to make sure that the target chain doesn't double process the same message
- Payload: the bytes payload
- Confirmations: the number of blocks the guardians should wait before signing this VAA. For low security applications, this number can be low, but if you're on a chain that often reorgs a high number of blocks (like Polygon) you might want to set this number high enough to ensure your transaction from the source chain doesn't get lost after the guardians sign it.
The `publishMessage()` function of the core_bridge takes three arguments:
- Nonce: A number to uniquely identify this message, used to make sure that the target chain doesn't double process the same message
- Payload: The bytes payload
- Confirmations: The number of blocks the Guardians should wait before signing this VAA. For low security applications, this number can be low, but if you're on a chain that often reorgs a high number of blocks (like Polygon) you might want to set this number high enough to ensure your transaction from the source chain doesn't get lost after the Guardians sign it.
```solidity
function sendMsg(bytes memory str) public returns (uint64 sequence) {
@ -52,7 +52,7 @@ function sendMsg(bytes memory str) public returns (uint64 sequence) {
```
## ReceiveEncodedMsg
The receive encoded message takes in a VAA as bytes. Then it calls the Core Bridge to verify the signatures match those of the gaurdians, check that it's from a contract on a foreign chain that we actually want to listen to and that the message hasn't been processed already. If all those checks pass, we can decode the payload (in this case we know it's a string) and set the current_msg for the contract to that payload.
The receive encoded message takes in a VAA as bytes. Then it calls the Core Bridge to verify the signatures match those of the Guardians, check that it's from a contract on a foreign chain that we actually want to listen to and that the message hasn't been processed already. If all those checks pass, we can decode the payload (in this case we know it's a string) and set the current_msg for the contract to that payload.
```solidity

View File

@ -12,17 +12,19 @@ The `chains/` folder contains the source code that's actually being deployed to
The IWormhole file is the Wormhole Core Bridge interface, and is required if your app wants to talk to the Wormhole Core Bridge. It outlines the functions and return values you can expect from the Wormhole contract.
The second file, Messenger, is covered in our breakdown of the EVM code [here](./messenger.md).
The second file, Messenger, is covered in our breakdown of the EVM code at [./messenger.md](./messenger.md).
### Tests
We have a very simple test script written in bash, but it's less of a test script and more of a happy path walkthrough. It makes uses of Orchestrator.js (see below) to call the functions on our EVM contract in order.
We have a very simple test script written in bash, but it's less of a test script and more of a happy path walkthrough. It makes uses of Orchestrator.js (see below) to call the functions on our EVM contract in order.
To start, deploy the code, register the applications on each chain and then send a message.
### Orchestrator
Orchestrator is a js client that takes arguments from the command line to call various functions on our contract. We'll break down everything orchestator does [here](./client.md).
Orchestrator is a js client that takes arguments from the command line to call various functions on our contract. We'll break down everything Orchestator does at [./client.md](./client.md).
### xdapp.config.json

View File

@ -1,28 +1,30 @@
# Contracts
Here you can find the addresses for the deployed contracts on all the chains that Wormhole supports, including Testnet.
The SDK makes these addresses available in the `CONTRACTS` constant.
Here you can find the addresses for the deployed contracts on all the chains that Wormhole supports, including testnet.
The [constants](https://github.com/wormhole-foundation/wormhole/blob/dev.v2/sdk/js/src/utils/consts.ts) 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.
## Mainnet
### Core Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :-------------------- | :---------------- | :----------- | :------ |
| Solana | 1 | mainnet-beta | worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth |
| Ethereum | 2 | 1 | 0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B |
| Terra Classic | 3 | columbus-5 | terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5 |
| Binance Smart Chain | 4 | 56 | 0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B |
| Polygon | 5 | 137 | 0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7 |
| Avalanche (C-Chain) | 6 | 43114 | 0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c |
| Oasis (Emerald) | 7 | 4262 | 0xfE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585 |
| Aurora | 9 | 1313161554 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| Fantom | 10 | 250 | 0x126783A6Cb203a3E35344528B26ca3a0489a1485 |
| Karura | 11 | 686 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| Acala | 12 | 787 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| Klaytn | 13 | 8217 | 0x0C21603c4f3a6387e241c0091A7EA39E43E90bb7 |
| Celo | 14 | 42220 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| Terra | 18 | phoenix-1 | terra12mrnzvhx3rpej6843uge2yyfppfyd3u9c3uq223q8sl48huz9juqffcnh |
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :------------------ | :---------------- | :----------- | :-------------------------------------------------------------- |
| Solana | 1 | mainnet-beta | worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth |
| Ethereum | 2 | 1 | 0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B |
| Terra Classic | 3 | columbus-5 | terra1dq03ugtd40zu9hcgdzrsq6z2z4hwhc9tqk2uy5 |
| Binance Smart Chain | 4 | 56 | 0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B |
| Polygon | 5 | 137 | 0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7 |
| Avalanche (C-Chain) | 6 | 43114 | 0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c |
| Oasis (Emerald) | 7 | 4262 | 0xfE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585 |
| Aurora | 9 | 1313161554 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| Fantom | 10 | 250 | 0x126783A6Cb203a3E35344528B26ca3a0489a1485 |
| Karura | 11 | 686 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| Acala | 12 | 787 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| Klaytn | 13 | 8217 | 0x0C21603c4f3a6387e241c0091A7EA39E43E90bb7 |
| Celo | 14 | 42220 | 0xa321448d90d4e5b0A732867c18eA198e75CAC48E |
| NEAR | 15 | | contract.wormhole_crypto.near |
| Terra | 18 | phoenix-1 | terra12mrnzvhx3rpej6843uge2yyfppfyd3u9c3uq223q8sl48huz9juqffcnh |
#### Core Bridge - Read Only
@ -37,101 +39,168 @@ These chains can *verify* Wormhole messages submitted to them, but cannot *emit*
### Token Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :-------------------- | :---------------- | :----------- | :------ |
| Solana | 1 | mainnet-beta | wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb |
| Ethereum | 2 | 1 | 0x3ee18B2214AFF97000D974cf647E7C347E8fa585 |
| Terra | 3 | columbus-5 | terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf |
| Binance Smart Chain | 4 | 56 | 0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7 |
| Polygon | 5 | 137 | 0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE |
| Avalanche (C-Chain) | 6 | 43114 | 0x0e082F06FF657D94310cB8cE8B0D9a04541d8052 |
| Oasis (Emerald) | 7 | 4262 | 0xfE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585 |
| Aurora | 9 | 1313161554 | 0x51b5123a7b0F9b2bA265f9c4C8de7D78D52f510F |
| Fantom | 10 | 250 | 0x7C9Fc5741288cDFdD83CeB07f3ea7e22618D79D2 |
| Karura | 11 | 686 | 0xae9d7fe007b3327AA64A32824Aaac52C42a6E624 |
| Acala | 12 | 787 | 0xae9d7fe007b3327AA64A32824Aaac52C42a6E624 |
| Klaytn | 13 | 8217 | 0x5b08ac39EAED75c0439FC750d9FE7E1F9dD0193F |
| Celo | 14 | 42220 | 0x796Dff6D74F3E27060B71255Fe517BFb23C93eed |
| Terra | 18 | phoenix-1 | terra153366q50k7t8nn7gec00hg66crnhkdggpgdtaxltaq6xrutkkz3s992fw9 |
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :------------------ | :---------------- | :----------- | :--------------------------------------------------------------- |
| Solana | 1 | mainnet-beta | wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb |
| Ethereum | 2 | 1 | 0x3ee18B2214AFF97000D974cf647E7C347E8fa585 |
| Terra | 3 | columbus-5 | terra10nmmwe8r3g99a9newtqa7a75xfgs2e8z87r2sf |
| Binance Smart Chain | 4 | 56 | 0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7 |
| Polygon | 5 | 137 | 0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE |
| Avalanche (C-Chain) | 6 | 43114 | 0x0e082F06FF657D94310cB8cE8B0D9a04541d8052 |
| Oasis (Emerald) | 7 | 4262 | 0xfE8cD454b4A1CA468B57D79c0cc77Ef5B6f64585 |
| Aurora | 9 | 1313161554 | 0x51b5123a7b0F9b2bA265f9c4C8de7D78D52f510F |
| Fantom | 10 | 250 | 0x7C9Fc5741288cDFdD83CeB07f3ea7e22618D79D2 |
| Karura | 11 | 686 | 0xae9d7fe007b3327AA64A32824Aaac52C42a6E624 |
| Acala | 12 | 787 | 0xae9d7fe007b3327AA64A32824Aaac52C42a6E624 |
| Klaytn | 13 | 8217 | 0x5b08ac39EAED75c0439FC750d9FE7E1F9dD0193F |
| Celo | 14 | 42220 | 0x796Dff6D74F3E27060B71255Fe517BFb23C93eed |
| NEAR | 15 | | contract.portalbridge.near |
| Terra | 18 | phoenix-1 | terra153366q50k7t8nn7gec00hg66crnhkdggpgdtaxltaq6xrutkkz3s992fw9 |
### NFT Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :-------------------- | :---------------- | :----------- | :------ |
| Solana | 1 | mainnet-beta | WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD |
| Ethereum | 2 | 1 | 0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE |
| Binance Smart Chain | 4 | 56 | 0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE |
| Polygon | 5 | 137 | 0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf |
| Avalanche (C-Chain) | 6 | 43114 | 0xf7B6737Ca9c4e08aE573F75A97B73D7a813f5De5 |
| Oasis (Emerald) | 7 | 4262 | 0x04952D522Ff217f40B5Ef3cbF659EcA7b952a6c1 |
| Aurora | 9 | 1313161554 | 0x6dcC0484472523ed9Cdc017F711Bcbf909789284 |
| Fantom | 10 | 250 | 0xA9c7119aBDa80d4a4E0C06C8F4d8cF5893234535 |
| Karura | 11 | 686 | 0xb91e3638F82A1fACb28690b37e3aAE45d2c33808 |
| Acala | 12 | 787 | 0xb91e3638F82A1fACb28690b37e3aAE45d2c33808 |
| Klaytn | 13 | 8217 | 0x3c3c561757BAa0b78c5C025CdEAa4ee24C1dFfEf |
| Celo | 14 | 42220 | 0xA6A377d75ca5c9052c9a77ED1e865Cc25Bd97bf3 |
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :------------------ | :---------------- | :----------- | :------------------------------------------ |
| Solana | 1 | mainnet-beta | WnFt12ZrnzZrFZkt2xsNsaNWoQribnuQ5B5FrDbwDhD |
| Ethereum | 2 | 1 | 0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE |
| Binance Smart Chain | 4 | 56 | 0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE |
| Polygon | 5 | 137 | 0x90BBd86a6Fe93D3bc3ed6335935447E75fAb7fCf |
| Avalanche (C-Chain) | 6 | 43114 | 0xf7B6737Ca9c4e08aE573F75A97B73D7a813f5De5 |
| Oasis (Emerald) | 7 | 4262 | 0x04952D522Ff217f40B5Ef3cbF659EcA7b952a6c1 |
| Aurora | 9 | 1313161554 | 0x6dcC0484472523ed9Cdc017F711Bcbf909789284 |
| Fantom | 10 | 250 | 0xA9c7119aBDa80d4a4E0C06C8F4d8cF5893234535 |
| Karura | 11 | 686 | 0xb91e3638F82A1fACb28690b37e3aAE45d2c33808 |
| Acala | 12 | 787 | 0xb91e3638F82A1fACb28690b37e3aAE45d2c33808 |
| Klaytn | 13 | 8217 | 0x3c3c561757BAa0b78c5C025CdEAa4ee24C1dFfEf |
| Celo | 14 | 42220 | 0xA6A377d75ca5c9052c9a77ED1e865Cc25Bd97bf3 |
## Testnet
### Core Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :--------------------- | :---------------- | :----------- | :------ |
| Solana | 1 | devnet | 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 |
| Ethereum (Goerli) | 2 | 5 | 0x706abc4E45D419950511e474C7B9Ed348A4a716c |
| Ethereum (Ropsten) | 10001 | 3 | 0x210c5F5e2AF958B4defFe715Dc621b7a3BA888c5 |
| Terra | 3 | bombay-12 | terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v |
| Binance Smart Chain | 4 | 97 | 0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D |
| Polygon (Mumbai) | 5 | 80001 | 0x0CBE91CF822c73C2315FB05100C2F714765d5c20 |
| Avalanche (Fuji) | 6 | 43113 | 0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C |
| Oasis (Emerald Testnet)| 7 | 42261 | 0xc1C338397ffA53a2Eb12A7038b4eeb34791F8aCb |
| Algorand (Testnet) | 8 | | 86525623 |
| Aurora | 9 | 1313161555 | 0xBd07292de7b505a4E803CEe286184f7Acf908F5e |
| Fantom | 10 | 4002 | 0x1BB3B4119b7BA9dfad76B0545fb3F531383c3bB7 |
| Karura | 11 | 686 | 0xE4eacc10990ba3308DdCC72d985f2a27D20c7d03 |
| Acala | 12 | 787 | 0x4377B49d559c0a9466477195C6AdC3D433e265c0 |
| Klaytn | 13 | 1001 | 0x1830CC6eE66c84D2F177B94D544967c774E624cA |
| Celo | 14 | 44787 | 0x88505117CA88e7dd2eC6EA1E13f0948db2D50D56 |
| Terra | 18 | pisco-1 | terra19nv3xr5lrmmr7egvrk2kqgw4kcn43xrtd5g0mpgwwvhetusk4k7s66jyv0 |
| Injective | 19 | testnet | inj1xx3aupmgv3ce537c0yce8zzd3sz567syuyedpg |
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :---------------------- | :---------------- | :--------- | :----------------------------------------------------------------- |
| Solana | 1 | devnet | 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 |
| Ethereum (Goerli) | 2 | 5 | 0x706abc4E45D419950511e474C7B9Ed348A4a716c |
| Ethereum (Ropsten) | 10001 | 3 | 0x210c5F5e2AF958B4defFe715Dc621b7a3BA888c5 |
| Terra | 3 | bombay-12 | terra1pd65m0q9tl3v8znnz5f5ltsfegyzah7g42cx5v |
| Binance Smart Chain | 4 | 97 | 0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D |
| Polygon (Mumbai) | 5 | 80001 | 0x0CBE91CF822c73C2315FB05100C2F714765d5c20 |
| Avalanche (Fuji) | 6 | 43113 | 0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C |
| Oasis (Emerald Testnet) | 7 | 42261 | 0xc1C338397ffA53a2Eb12A7038b4eeb34791F8aCb |
| Algorand (Testnet) | 8 | | 86525623 |
| Aurora | 9 | 1313161555 | 0xBd07292de7b505a4E803CEe286184f7Acf908F5e |
| Fantom | 10 | 4002 | 0x1BB3B4119b7BA9dfad76B0545fb3F531383c3bB7 |
| Karura | 11 | 686 | 0xE4eacc10990ba3308DdCC72d985f2a27D20c7d03 |
| Acala | 12 | 787 | 0x4377B49d559c0a9466477195C6AdC3D433e265c0 |
| Klaytn | 13 | 1001 | 0x1830CC6eE66c84D2F177B94D544967c774E624cA |
| Celo | 14 | 44787 | 0x88505117CA88e7dd2eC6EA1E13f0948db2D50D56 |
| NEAR | 15 | | wormhole.wormhole.testnet |
| Terra | 18 | pisco-1 | terra19nv3xr5lrmmr7egvrk2kqgw4kcn43xrtd5g0mpgwwvhetusk4k7s66jyv0 |
| Injective | 19 | testnet | inj1xx3aupmgv3ce537c0yce8zzd3sz567syuyedpg |
| Aptos | 22 | | 0x1b1752e26b65fc24971ee5ec9718d2ccdd36bf20486a10b2973ea6dedc6cd197 |
### Token Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :--------------------- | :---------------- | :----------- | :------ |
| Solana | 1 | devnet | DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe |
| Ethereum (Goerli) | 2 | 5 | 0xF890982f9310df57d00f659cf4fd87e65adEd8d7 |
| Ethereum (Ropsten) | 10001 | 3 | 0xF174F9A837536C449321df1Ca093Bb96948D5386 |
| Terra | 3 | bombay-12 | terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a |
| Binance Smart Chain | 4 | 97 | 0x9dcF9D205C9De35334D646BeE44b2D2859712A09 |
| Polygon (Mumbai) | 5 | 80001 | 0x377D55a7928c046E18eEbb61977e714d2a76472a |
| Avalanche (Fuji) | 6 | 43113 | 0x61E44E506Ca5659E6c0bba9b678586fA2d729756 |
| Oasis (Emerald Testnet)| 7 | 42261 | 0x88d8004A9BdbfD9D28090A02010C19897a29605c |
| Algorand (Testnet) | 8 | | 86525641 |
| Aurora | 9 | 1313161555 | 0xD05eD3ad637b890D68a854d607eEAF11aF456fba |
| Fantom | 10 | 4002 | 0x599CEa2204B4FaECd584Ab1F2b6aCA137a0afbE8 |
| Karura | 11 | 686 | 0xd11De1f930eA1F7Dd0290Fe3a2e35b9C91AEFb37 |
| Acala | 12 | 787 | 0xebA00cbe08992EdD08ed7793E07ad6063c807004 |
| Klaytn | 13 | 1001 | 0xC7A13BE098720840dEa132D860fDfa030884b09A |
| Celo | 14 | 44787 | 0x05ca6037eC51F8b712eD2E6Fa72219FEaE74E153 |
| Injective | 19 | testnet | inj1q0e70vhrv063eah90mu97sazhywmeegp7myvnh |
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :---------------------- | :---------------- | :--------- | :----------------------------------------------------------------- |
| Solana | 1 | devnet | DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe |
| Ethereum (Goerli) | 2 | 5 | 0xF890982f9310df57d00f659cf4fd87e65adEd8d7 |
| Ethereum (Ropsten) | 10001 | 3 | 0xF174F9A837536C449321df1Ca093Bb96948D5386 |
| Terra | 3 | bombay-12 | terra1pseddrv0yfsn76u4zxrjmtf45kdlmalswdv39a |
| Binance Smart Chain | 4 | 97 | 0x9dcF9D205C9De35334D646BeE44b2D2859712A09 |
| Polygon (Mumbai) | 5 | 80001 | 0x377D55a7928c046E18eEbb61977e714d2a76472a |
| Avalanche (Fuji) | 6 | 43113 | 0x61E44E506Ca5659E6c0bba9b678586fA2d729756 |
| Oasis (Emerald Testnet) | 7 | 42261 | 0x88d8004A9BdbfD9D28090A02010C19897a29605c |
| Algorand (Testnet) | 8 | | 86525641 |
| Aurora | 9 | 1313161555 | 0xD05eD3ad637b890D68a854d607eEAF11aF456fba |
| Fantom | 10 | 4002 | 0x599CEa2204B4FaECd584Ab1F2b6aCA137a0afbE8 |
| Karura | 11 | 686 | 0xd11De1f930eA1F7Dd0290Fe3a2e35b9C91AEFb37 |
| Acala | 12 | 787 | 0xebA00cbe08992EdD08ed7793E07ad6063c807004 |
| Klaytn | 13 | 1001 | 0xC7A13BE098720840dEa132D860fDfa030884b09A |
| Celo | 14 | 44787 | 0x05ca6037eC51F8b712eD2E6Fa72219FEaE74E153 |
| Near | 15 | | token.wormhole.testnet |
| Injective | 19 | testnet | inj1q0e70vhrv063eah90mu97sazhywmeegp7myvnh |
| Aptos | 22 | | 0xdd0a2618dc5564ccf38d0eca7877198fef51157fea74a6bc2e5e40b52c2a0a08 |
### NFT Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :--------------------- | :---------------- | :----------- | :------ |
| Solana | 1 | devnet | 2rHhojZ7hpu1zA91nvZmT8TqWWvMcKmmNBCr2mKTtMq4 |
| Ethereum (Goerli) | 2 | 5 | 0xD8E4C2DbDd2e2bd8F1336EA691dBFF6952B1a6eB |
| Ethereum (Ropsten) | 10001 | 3 | 0x2b048Da40f69c8dc386a56705915f8E966fe1eba |
| Binance Smart Chain | 4 | 97 | 0xcD16E5613EF35599dc82B24Cb45B5A93D779f1EE |
| Polygon (Mumbai) | 5 | 80001 | 0x51a02d0dcb5e52F5b92bdAA38FA013C91c7309A9 |
| Avalanche (Fuji) | 6 | 43113 | 0xD601BAf2EEE3C028344471684F6b27E789D9075D |
| Oasis (Emerald Testnet)| 7 | 42261 | 0xC5c25B41AB0b797571620F5204Afa116A44c0ebA |
| Aurora | 9 | 1313161555 | 0x8F399607E9BA2405D87F5f3e1B78D950b44b2e24 |
| Fantom | 10 | 4002 | 0x63eD9318628D26BdCB15df58B53BB27231D1B227 |
| Karura | 11 | 686 | 0x0A693c2D594292B6Eb89Cb50EFe4B0b63Dd2760D |
| Acala | 12 | 787 | 0x96f1335e0AcAB3cfd9899B30b2374e25a2148a6E |
| Klaytn | 13 | 1001 | 0x94c994fC51c13101062958b567e743f1a04432dE |
| Celo | 14 | 44787 | 0xaCD8190F647a31E56A656748bC30F69259f245Db |
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :---------------------- | :---------------- | :--------- | :------------------------------------------- |
| Solana | 1 | devnet | 2rHhojZ7hpu1zA91nvZmT8TqWWvMcKmmNBCr2mKTtMq4 |
| Ethereum (Goerli) | 2 | 5 | 0xD8E4C2DbDd2e2bd8F1336EA691dBFF6952B1a6eB |
| Ethereum (Ropsten) | 10001 | 3 | 0x2b048Da40f69c8dc386a56705915f8E966fe1eba |
| Binance Smart Chain | 4 | 97 | 0xcD16E5613EF35599dc82B24Cb45B5A93D779f1EE |
| Polygon (Mumbai) | 5 | 80001 | 0x51a02d0dcb5e52F5b92bdAA38FA013C91c7309A9 |
| Avalanche (Fuji) | 6 | 43113 | 0xD601BAf2EEE3C028344471684F6b27E789D9075D |
| Oasis (Emerald Testnet) | 7 | 42261 | 0xC5c25B41AB0b797571620F5204Afa116A44c0ebA |
| Aurora | 9 | 1313161555 | 0x8F399607E9BA2405D87F5f3e1B78D950b44b2e24 |
| Fantom | 10 | 4002 | 0x63eD9318628D26BdCB15df58B53BB27231D1B227 |
| Karura | 11 | 686 | 0x0A693c2D594292B6Eb89Cb50EFe4B0b63Dd2760D |
| Acala | 12 | 787 | 0x96f1335e0AcAB3cfd9899B30b2374e25a2148a6E |
| Klaytn | 13 | 1001 | 0x94c994fC51c13101062958b567e743f1a04432dE |
| Celo | 14 | 44787 | 0xaCD8190F647a31E56A656748bC30F69259f245Db |
## Devnet / Tilt
### Core Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :---------------------- | :---------------- | :--------- | :--------------------------------------------------------------- |
| Solana | 1 | | Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o |
| Ethereum | 2 | | 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550 |
| Terra | 3 | | terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5 |
| Binance Smart Chain | 4 | | 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550 |
| Algorand | 8 | | 4 |
| NEAR | 15 | | wormhole.test.near |
| Terra2 | 18 | | terra14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9ssrc8au |
| Aptos | 22 | | |
| Wormholechain | 3104 | | wormhole1ap5vgur5zlgys8whugfegnn43emka567dtq0jl |
### Token Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :---------------------- | :---------------- | :--------- | :--------------------------------------------------------------- |
| Solana | 1 | | B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE |
| Ethereum | 2 | | 0x0290FB167208Af455bB137780163b7B7a9a10C16 |
| Terra | 3 | | terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4 |
| Binance Smart Chain | 4 | | 0x0290FB167208Af455bB137780163b7B7a9a10C16 |
| Algorand | 8 | | 6 |
| NEAR | 15 | | token.test.near |
| Terra2 | 18 | | terra1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrquka9l6 |
| Aptos | 22 | | |
| Wormholechain | 3104 | | wormhole1zugu6cajc4z7ue29g9wnes9a5ep9cs7yu7rn3z |
### NFT Bridge
| Chain Name | Wormhole Chain ID | Network ID | Address |
| :---------------------- | :---------------- | :--------- | :--------------------------------------------------------------- |
| Solana | 1 | | NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA |
| Ethereum | 2 | | 0x26b4afb60d6c903165150c6f0aa14f8016be4aec |
| Terra | 3 | | terra1plju286nnfj3z54wgcggd4enwaa9fgf5kgrgzl |
| Binance Smart Chain | 4 | | 0x26b4afb60d6c903165150c6f0aa14f8016be4aec |
## Blockchain Finality Recommendations
The goal of Wormhole is to provide high confidence that only _finalized_ messages are observed and attested. Different chains use different consensus mechanisms and so there are different finality assumptions with each one.
Below is a table of suggested finality recommendations for each of the chains supported by the Wormhole ecosystem to have the highest confidence of finality.
However, these are just suggestions and developers are free to define their own finality windows for their applications. Ultimately, the tradeoff is between speed and security.
| Chain Name | Wormhole Chain ID | Suggested Number of Block Confirmations |
| :------------------ | :---------------- | :---------------------------------------- |
| Solana | 1 | 32 |
| Ethereum | 2 | 15 |
| Terra Classic | 3 | Instant |
| Binance Smart Chain | 4 | 15 |
| Polygon | 5 | 512 |
| Avalanche (C-Chain) | 6 | 1 |
| Oasis (Emerald) | 7 | 1 |
| Aurora | 9 | 1 |
| Fantom | 10 | 1 |
| Karura | 11 | 1 |
| Acala | 12 | 1 |
| Klaytn | 13 | 1 |
| Celo | 14 | 1 |
| Terra | 18 | Instant |

View File

@ -1,7 +0,0 @@
# Repository
The Wormhole core repository can be found [here](https://github.com/wormhole-foundation/wormhole).
# Design Documents
Wormhole's component design specifications can be found [here](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/whitepapers). These outline the reasoning behind design decisions with added technical depth.

21
src/reference/glossary.md Normal file
View File

@ -0,0 +1,21 @@
# Glossary
In some instances, Wormhole uses general terms fo decentralized, cross-chain elements as branded verbiage. In most casese, the definition of the general term does not differ from Wormhole's definition though Wormhole's definitions may be more narrow than general interpretations.
**Guardian** - One of the 19 validators in the Guardian Network that Contributes to the VAA multisig.
[**Guardian Network**](../wormhole/5_guardianNetwork.md) - 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.
[**Relayer**](../wormhole/6_relayers.md) - Any process which delivers VAAs to a destination.
[**VAA**](../wormhole/4_vaa.md) - 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.
[**Wormchain**](../wormhole/8_wormchain.md) - A purpose-built cosmos blockchain which aids the Guardian Network and allows for formal interaction with the Guardians.
[**xAssets**](../dapps/3_xdataxassets.md) - 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) [Token Bridge Module](../technical/evm/xassetLayer.md) and (2) [NFT Bridge Module](../technical/evm/nftLayer.md)
**xChain** - Term that referrs to the full range of cross-blockchain interoperability.
[**xDapp**](../dapps/4_whatIsanXdapp.md) - Decentralized application that enables users to create and/or use xData.
[**xData**](../dapps/3_xdataxassets.md) - Data that exists in a layer outside of Layer 1 blockchains, which is accessible by all chains.

10
src/reference/overview.md Normal file
View File

@ -0,0 +1,10 @@
# Other Resources
Here is a collection of other resources and reference sources which you're likely to find helpful.
- [Glossary & Terms](./glossary.md)
- [Tools & Helpful Links](./tools.md)
- [Github](./github.md)
- [Contract Addresses & Environment Information](./contracts.md)
- [RPC Info](./rpcnodes.md)
- [Block Finality Suggestions](./finality.md)

View File

@ -1,23 +0,0 @@
# Tools
There are various tools in the Wormhole ecosystem that can help you in developing xDapps. Here are a few of the most notable:
# Testnet
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 [here](./contracts.md). There's only a single Guardian that oversees the testnets, so you might get a higher rate of missed VAAs than you would on mainnet.
# Wormhole Explorer
Wormhole Explorer is a tool that will help you parse VAAs after they've been picked up the Guardian network. It's available [here](https://wormhole.com/explorer).
# Testnet Bridge UI
If you'd like to try out Portal Bridge on Testnet, there's a UI you can use to attest and transfer tokens for testnet, hosted [here](https://wormhole-foundation.github.io/example-token-bridge-ui/).
# Tilt
Tilt is a Kubernetes based tool that runs a copy of every chain along side a guardian node to create a simulated testing environment. To set it up and test against it, start [here](../development/tilt/overview.md).
# Wormhole SDK
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 NPM [here](https://www.npmjs.com/package/@certusone/wormhole-sdk).

View File

@ -0,0 +1,31 @@
# Useful Links
Below are a variety of useful links to tools and information in the Wormhole ecosystem that can help you develop xDapps.
### Design Documents
Wormhole's component design specifications can be found [here](https://github.com/certusone/wormhole/tree/dev.v2/whitepapers). These outline the reasoning behind design decisions with added technical depth.
### Testnet
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 [here](./contracts.md). There's only a single Guardian that oversees the testnets, so you might get a higher rate of missed VAAs than you would on mainnet.
### Testnet Bridge UI
If you'd like to try out bridging tokens on testnet, there's a UI you can use to attest and transfer tokens for testnet, hosted [here](https://wormhole-foundation.github.io/example-token-bridge-ui/#/transfer).
### Tilt
Tilt is a Kubernetes-based tool that runs a copy of every chain along side a Guardian node to create a simulated testing environment. To set it up and test against it, start [here](../development/tilt/overview.md).
### Wormhole Core Repository
The Wormhole core repository can be found at [https://github.com/wormhole-foundation/wormhole](https://github.com/wormhole-foundation/wormhole).
### Wormhole Explorer
[Wormhole Explorer](https://wormholenetwork.com/en/explorer) is a tool that will help you parse VAAs after they've been picked up the Guardian network.
### Wormhole SDK
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 [NPM](https://www.npmjs.com/package/@certusone/wormhole-sdk).

View File

@ -0,0 +1,9 @@
# Algorand
The implementation contracts for Wormhole's official Algorand integration can be found [here](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/algorand).
An overview of the contracts and how they work is outlined [here](https://github.com/wormhole-foundation/wormhole/blob/dev.v2/algorand/README.md).
The deployed contract addresses can be found on the [Contracts page](../../reference/contracts.md).
The Algorand integration is also fully supported by the [Wormhole Typescript SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/js).

View File

@ -0,0 +1,9 @@
# Aptos
Aptos is currently only supported in testnet.
The official Wormhole integration can be found [here](https://github.com/wormhole-foundation/wormhole/tree/aptos/integration).
Information for how to integrate with these contracts can be found [here](https://github.com/wormhole-foundation/wormhole/tree/aptos/integration/aptos/README.md).
The deployed contract addresses can be found on the [Contracts page](../../reference/contracts.md).

View File

@ -0,0 +1,9 @@
# Cosmos
The implementation contracts for Wormhole's official Cosmos integration can be found [here](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/cosmwasm).
The deployed contract addresses can be found on the [Contracts page](../../reference/contracts.md).
Cosmos is a rust-based programming environment. You'll likely find the [Wormhole Rust SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/rust) useful.
The Cosmos integration is also fully supported by the [Wormhole Typescript SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/js).

23
src/technical/env/environments.md vendored Normal file
View File

@ -0,0 +1,23 @@
# Environment Setup
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.
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.
### What components do I need now? What components will I need in the future?
- 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.
### What are my dependencies?
- If your smart contracts have no dependencies, it may be possible for you to develop in a vacuum.
- If your smart contracts does have dependencies, there are several options that range from deploying your dependencies in [Tilt](./tilt.md) to simulating an existing testnet/mainnet in [Foundry](https://github.com/foundry-rs/foundry) to working directly in [testnet](./testnet.md) alongside other teams.
### How am I going to collaborate?
- You should consider how your teammates or collaborators are going to work in this environment from the start. There are some basic considerations like "how will they access it", 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).
---
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.

1
src/technical/env/overview.md vendored Normal file
View File

@ -0,0 +1 @@
# Introduction

28
src/technical/env/testnet.md vendored Normal file
View File

@ -0,0 +1,28 @@
# Testnet
Wormhole testnet is spread across many of the most popular testnet blockchains.
### Is Testnet right for you?
The primary reason to use Wormhole testnet is to simplify the management of your dependent contracts. This will vary from blockchain to blockchain.
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 [foundry](https://github.com/foundry-rs/foundry), which allow you to hardfork the mainnet ecosystem into a local development node.
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.
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.
**Pros**
- Many other projects deploy their contracts to testnet.
- In ecosystems without extensive local tooling, this may be the preferred development environment.
**Cons**
- Many testnets are somewhat unstable and have outages or partitioning events.
- Wormhole Testnet sometimes misses VAAs due to testnet instabilities.
- Testnet tokens are often difficult to acquire.
## Using Testnet
If you elect to use testnet, the Wormhole contracts addresses can be found in the [Contracts](../../reference/contracts.md) page.

78
src/technical/env/tilt.md vendored Normal file
View File

@ -0,0 +1,78 @@
# Tilt (Devnet)
## What is Tilt?
[Tilt](https://tilt.dev/) is part of the official Docker ecosystem. It's a tool which allows developers to easily configure a Kubernetes environment for development.
However, in the context of Wormhole, 'Tilt' refers to the development environment used by the [Wormhole Core Repository](https://github.com/wormhole-foundation/wormhole). This environment stands up docker images for all the tools necessary to build across multiple blockchains, including:
- All the Wormhole supported blockchains / ecosystems
- A Guardian Node
- Relayers
- Databases, Redis
- Utility frontends
The 'Tilt' environment is meant to provide an entire cross-chain development stack right out of the box.
_Note: Tilt is often referred to as 'Devnet' in the Wormhole ecosystem so any information that is labelled as 'devnet' also applies to Tilt._
### Is Tilt Right for You?
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.
**Pros**
- Out-of-the-box support for the many components needed to develop across the heterogenous blockchain spaces.
- Consistent development environment, where contracts deploy deterministically and everything is already linked up.
- Ability to easily enable/disable components as needed.
- Regularly updated as new components join the Wormhole ecosystem.
**Cons**
- Relatively high system requirements but this can be mitigated by disabling components.
- 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.
- Spin-up and rebuild times can be slow which can result in a slow workflow.
## Setting up Tilt
Tilt functions best in a UNIX-style environment. For Windows users, a WSL environment is recommended.
In order to run the Tilt environment, make sure you have [Tilt](https://docs.tilt.dev/install.html) and [Go](https://go.dev/doc/install) installed.
Once you've installed these two dependencies, just clone the Wormhole Core Repository and start Tilt.
```
git clone --branch dev.v2 https://github.com/wormhole-foundation/wormhole.git
cd wormhole
tilt up
```
Be sure to check out the [**Tiltfile**](https://github.com/wormhole-foundation/wormhole/blob/dev.v2/Tiltfile), which has much of the configuration and arguments for the development environment. It's relatively straightforward to enable and disable components.
For example, you can disable blockchains by setting them to false at startup
```
tilt up -- --algorand=false --near=false --solana=false terra_classic=false terra2=false
```
## Using Tilt
Tilt can pretty much be treated as an external environment / testnet that you can easily spin up and tear down.
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 [http://localhost:10350/overview](http://localhost:10350/overview).
All the deployed contract addresses can be found under the 'Devnet / Tilt' section of [contracts](../../reference/contracts.md).
Useful information pertaining to funded wallets & private keys can also be found in the [devnet.md](https://github.com/wormhole-foundation/wormhole/blob/dev.v2/docs/devnet.md) file of the docs.
Additional helpful resources can be found in the [Tooling](./tooling.md) page.
## Shutting down Tilt
In order to shut down Tilt, simply run
```
tilt down
```

60
src/technical/env/tooling.md vendored Normal file
View File

@ -0,0 +1,60 @@
# Tooling
Regardless of the development environment that you use, there are a few wormhole-specific tools you should know about.
### [Wormhole Core Repository](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/)
- 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.
### [Worm CLI tool](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/clients/js)
- Swiss-Army Knife Utility CLI tool. Excellent for creating one-off VAAs, parsing VAAs, reading Wormhole contract configurations, and more.
### [Orchestrator](https://github.com/wormhole-foundation/xdapp-book/blob/main/projects/evm-messenger/orchestrator.js)
- Small WIP tool which comes as part of the [Wormhole Local Validator](./wlv.md) environment. Used to quickly deploy and redeploy multiple smart contracts. Will eventually become part of a larger deployment management tool.
### [Wormhole SDKs](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk)
- Libraries in various languages to help with interacting with Wormhole contracts.
### [Wormhole Typescript SDK](https://www.npmjs.com/package/@certusone/wormhole-sdk)
- Typescript SDK destributed on npm. Can greatly aid in writing frontend code for xDapps and utilizing the Wormhole Token Bridge directly.
### [Wormhole Spy SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/spydk/js)
- The Wormhole Spy SDK allows you to listen to all of the activity on the Guardian Network.
### [Reference Bridge UI](https://github.com/wormhole-foundation/example-token-bridge-ui)
- An example GUI which can be used to perform token transfers around the ecosystem.
### [Explorer](https://wormhole.com/explorer/)
- Resource for looking at individual transfers statuses on mainnet and testnet.
<!--
how to use on tilt?
-->
<!--
Example projects
-->
## Example Projects
### [Basic Examples](https://github.com/wormhole-foundation/xdapp-book/tree/main/projects)
- Several example projects are bundled here. They show minimum-code examples for how to send messages, tokens, and other common functions.
### [ICCO](https://github.com/certusone/wormhole-icco)
- Productionized, audited xDapp which does cross-chain token launches. Great example of what a robust xDapp, written across multiple ecosystems looks like.
### [Native Swap](https://github.com/certusone/wormhole-nativeswap-example)
- Example cross-chain dex, utilizing the stablecoin highway model.
### [Wormhole Examples](https://github.com/certusone/wormhole-examples)
- More example components. Has a mix of relayers, xDapps, NFT projects, and more.

119
src/technical/env/troubleshooting.md vendored Normal file
View File

@ -0,0 +1,119 @@
# Troubleshooting
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.
# macOS Install
## Prerequisites
Install [Homebrew](https://brew.sh) if you don't already have it.
You can grab it with:
```sh
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
From there, all the other installs are one-liners.
### 1. Install Go
```sh
brew install go
```
### 2. Install Docker
```sh
brew install docker
```
After installation, go into Docker settings and switch ON `kubernetes`. Also configure Docker to have 4 CPUs and ~16GB of RAM.
### 3. Install Tilt
```sh
brew install tilt
```
### 4. Clone Wormhole Repo and Start Tilt
```sh
git clone --branch dev.v2 https://github.com/wormhole-foundation/wormhole.git
cd wormhole/
tilt up
```
You'll be able to access the Tilt UI at
`localhost:10350`
# Linux & WSL Install
## Script Setup
If you're using a Debian distro, you should run the [dev-setup.sh](https://github.com/wormhole-foundation/wormhole/blob/dev.v2/scripts/dev-setup.sh) script. Even if you're not using Debian, this script still contains the main steps for setup.
## Regular Setup
### 1. Install Go
```sh
wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.1.linux-amd64.tar.gz
```
### 2. Install Docker
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 `minikube`. It's recommended to have at least 4 CPUs and 16GB RAM dedicated to Docker.
Also, make absolutely sure that you set up Docker as a non-root user.
[https://docs.docker.com/engine/install/ubuntu/#installation-methods](https://docs.docker.com/engine/install/ubuntu/#installation-methods)
### 3a. (Docker Desktop Install)
Enable Kubernetes by going into Settings > Kubernetes
### 3b. (Non Docker Desktop)
Install [`minikube`](https://minikube.sigs.k8s.io/docs/start/).
Configure minikube:
```
minikube start --driver=docker --kubernetes-version=v1.23.3 --cpus=4 --memory=14G --disk-size=10G --namespace=wormhole
```
Minikube needs to be running for tilt to work, so always make sure to run `minikube start` before you bring up tilt.
### 4. Install Tilt
Install Tilt by copy pasting this into the Terminal:
```sh
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
```
### 5. Clone the Wormhole Repo and start Tilt
```sh
git clone --branch dev.v2 https://github.com/wormhole-foundation/wormhole.git
```
If you're running Tilt on your machine:
```sh
cd wormhole/
tilt up
```
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:
```sh
cd wormhole
tilt up --host=0.0.0.0 -- --webHost=0.0.0.0
```
You can now access the Tilt UI at either `localhost:10350` or `vm_external_ip:10350`.
If the VM's external IP doesn't work, check firewall and port settings to make sure your VM allows incoming traffic.

33
src/technical/env/wlv.md vendored Normal file
View File

@ -0,0 +1,33 @@
# Wormhole Local Validator
## What is Wormhole Local Validator (WLV)?
[Wormhole Local Validator](https://github.com/wormhole-foundation/xdapp-book/tree/main/projects/wormhole-local-validator) 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.
### Is WLV Right for You?
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.
**Pros**
- Lightweight, low system resource demand.
- Fast iteration times.
- Can be added into an existing blockchain development setup.
**Cons**
- You may end up reinventing the tilt/testnet environment as you add more components.
## Setting up Wormhole Local Validator
You will need Docker running in order to set up WLV. If you're on desktop, [Docker Desktop](https://docs.docker.com/get-docker/) is generally the best choice, though [Docker Engine](https://docs.docker.com/engine/) works fine too.
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.
Further information can be found in the project's [README](https://github.com/wormhole-foundation/xdapp-book/blob/main/projects/wormhole-local-validator/README.md).
## Troubleshooting
Q: Anvil isn't working
- 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.

View File

@ -0,0 +1,140 @@
# Best Practices
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.
# Sending Messages
When sending messages, you should follow the same paradigm as is used by the Wormhole modules, namely
- Don't couple the message emission to the message delivery
- Pass through all the optional arguments (like nonce)
- Always return the sequence
### Good Example
```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 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.
// 1 is the consistency level,
// this message will be emitted after only 1 block
sequence = core_bridge.publishMessage(nonce, "My Message to " + intendedRecipient, 1);
// 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 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 application.
// Do whatever you need to here.
function sendMyMessage() private {
// First, emit a message intended for MY_OTHER_CONTRACT with nonce zero.
// 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, 0);
// 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 0, 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,
0, null);
// Lastly, I request that the batch for nonce 0 be delivered to MY_OTHER_CONTRACT
relayer_contract.requestDelivery(OTHER_CHAIN, MY_OTHER_CONTRACT, 0, getRelayerFeeAmount());
}
```
# Receiving Messages
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.
### **_Critical!_**
- Always verify that the emitterAddress of the VAA comes from a contract you trust.
- If the message should not be allowed to be 'replayed', immediately mark its hash as processed.
- 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.
### Composability
- When processing a VAA, always treat the messages as single VAAs. Destructuring batch VAAs is the responsibility of the integrator.
- Once you have the function written to verify your message, pretend you are an external integrator.
### Good Example
```
// 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, "Invalid Emitter Address!");
// Check that the VAA hasn't already been processed (replay protection)
require(!processedMessages[vm.hash], "Message already processed");
// 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
processedMessages[vm.hash] = true
// The message content can now be trusted.
doBusinessLogic(vm.payload)
}
//This is the function which would receive the the VAA from the relayer
function receiveVAA(bytes32 memory batchVAA) public {
// First, call the core bridge to verify the batchVAA
// All the individual VAAs inside the batchVAA will be cached,
// and you will receive headless VAAs inside the VM2 object.
// Headless VAAs are verifiable by parseAndVerifyVM.
(IWormhole.VM2 memory vm2, bool valid, string memory reason) =
core_bridge.parseAndVerifyBatchVM(batchVAA, true);
// I know from sendMyMessage that the first VAA is a token bridge VAA,
// so let's hand that off to the token bridge module.
bytes vaaData = token_bridge.completeTransferWithPayload(vm2.payloads[0]);
// The second VAA is my message, let's hand that off to my module.
processMyMessage(vm2.payloads[1]);
// Lastly, uncache the headless VAAs from the core bridge.
// This refunds a significant amount of gas.
core_bridge.clearBatchCache(vm2.hashes);
}
```
<!--
TODO these are not actually functioning examples and some of the interactions are incorrect. Demonstrates the concept.
>

View File

@ -0,0 +1,91 @@
# Core Message Layer
This section will explain how to properly interact with the Wormhome Core Message Layer in an EVM ecosystem.
Messages in Wormhole take the form of a Verified Action Approval (VAA) and both terms can be used interchangably. The rest of this section will only use the term VAA.
## Configuring the Interface
[Here](https://github.com/wormhole-foundation/wormhole/blob/dev.v2/ethereum/contracts/interfaces) is the interface for applications to interact with Wormhole's Core Contract to publish VAAs or verify and parse a received VAAs.
Instantiating the interface will depend on the contract address of your development ecosystem and blockchain.
Below is an example line of code to instantiate the interface for mainnet Ethereum:
```
address private wormhole_core_bridge_address = address(0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B);
IWormhole core_bridge = IWormhole(wormhole_core_bridge_address);
```
## Primary functions
The Wormhole Core Layer has two important interactions -- (1) emit VAAs, and (2) parse and verify VAAs that originated from other chains.
### Emitting a VAA
There are two forms of VAAs that can be emitted:
- Single VAA: all messages will be emitted in this format
- 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.
To emit a VAA, always use `publishMessage` which takes in the following arguments:
1. `nonce` (uint32): a number assigned to each message
- The `nonce` provides a mechanism by which to group messages together within a Batch VAA. How the `nonce` is used is described below.
2. `Consistency` (uint8): the number of blocks that Guardians will wait before signing a message
- Each blockchain has different finality periods based on the consensus mechanism. In general, higher consistency values provides more security against blockchain reorgs. [Here](../../reference/contracts.md) are the consistency levels by blockchain that are used by the xAsset layer to have a high level of guarantee against reorgs.
3. `Payload` (bytes[]): raw bytes to emit
- It is up to the emitting contract to properly define this arbitrary set of bytes.
`publishMessage` will output a `sequence` (uint64) that is used in conjunction with `emitterChainID` and `emitterAddress` to retrive the generated VAA from the Guardian Network.
> How Batch VAAs are generated
>
> There are two mechanisms that allow messages to be Batched together that represent a base and more advanced level of composability.
>
> 1. All messages originating from the same transaction will be batched together.
> 2. Messages that originate from the same transaction and are assigned the same nonce are additionally batched together.
>
> _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._
>
> Here is an example of how messages generated from the same transaction may be batched together:
>
> A transaction X that generates 6 messages [A, B, C, D, E, F] that are assigned `nonce` [1, 2, 2, 3, 3, 4] respectively will generate the following VAAs:
>
> - (1) full transaction batch VAA
> - [A, B, C, D, E, F]
> - (2) smaller batch VAA
> - [B, C]
> - [D, E]
> - (6) single VAA
> - [A]
> - [B]
> - [C]
> - [D]
> - [E]
> - [F]
### Parsing and Verifying a VAA
Parsing and Verifying a VAA will depend on the type of VAA that your application expects: a Single VAA or a Batch VAA.
For either VAA type, remember to collect gas fees associated with submitting them on-chain after all VAAs have been verified.
**Single VAA**
To properly parse and verify a single VAA, always use `parseAndVerifyVM` which takes in one argument: `encodedVM` (bytes). This function will return three arguments:
1. `vm` (VM): Structured data that reflects the content of the VAA.
- A breakdown of this message format is described in the [VAA](../../wormhole/4_vaa.md) 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.
2. `valid` (bool): Boolean that reflects whether or not the VAA was properly signed by the Guardian Network
3. `reason` (string): Explanatory error message if a VAA is invalid, or an empty string if it is valid.
**Batch VAA**
To properly parse and verify a batch VAA, always use `parseAndVerifyBatchVM` which takes in two arguments: `encodedVM` (bytes) and `cache` (bool).
In most scenarios, you'll want to set `cache` equal to true.
This will return a VM2 object, containing all the 'headless' VAAs contained inside the batch VAA. These headless VAAs can be verified by `parseAndVerifyVM`, 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.
The [Best Practices](./bestPractices.md) section goes into more depth of how to interact with the coreLayer.

View File

@ -0,0 +1,63 @@
# NFT Bridge
This section will explain how to properly interact with the NFT Bridge Module in an EVM ecosystem.
## Configuring the interface
[Here](https://github.com/wormhole-foundation/wormhole/tree/wonge97/evm-interface/ethereum/contracts/nft/interfaces) is the interface for applications to interact with Wormhole's NFT Bridge.
<!---
TODO
merge down the interface PR and link to actual file
-->
Instantiating the interface will depend on the contract address of your development ecosystem and blockchain.
Below is an example line of code to instantiate the interface for mainnet Ethereum:
```
address private wormhole_NFT_bridge_address = address(0x6FFd7EdE62328b3Af38FCD61461Bbfc52F5651fE);
INFTBridge NFT_bridge = INFTBridge(wormhole_nft_bridge_address);
```
## Transferring a NFT
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.
**Note**: Unlike tokens, there is no attestation required for bridging NFTs.
To transfer a NFT, there are three steps:
1. Initiate the NFT transfer
- This function call will return a `sequence` (uint64) that is used in the VAA retrieval step
```
transferNFT(tokenAddress, tokenID, recipientChain, recipient, nonce);
```
2. Retrieve the emitted VAA from the Guardian Network. (Usually done by a relayer)
- NFT Transfer VAAs are retrieved from the Guardian Network by the `emitterChainID`, `emitterAddress`, and `sequence`.
```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("VAA not found, retrying in 5s!");
await new Promise((r) => setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
```
3. Complete the NFT transfer by submitting the resultant VAA to the target chain.
```
completeTransfer(VAA);
```
<!---
TODO
additional usecases, most specifically how to grab the origin address of the wrapped NFT
-->

View File

@ -0,0 +1,18 @@
# EVM
**Disclaimer**: 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 [this](https://ethereum.org/en/developers/docs/intro-to-ethereum/).
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.
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 [here](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/ethereum).
### Recommended Tooling for EVM
**Frontend Development**
- [Ethers](https://docs.ethers.io/v5/) an excellent, widely used library for using web-based wallets to interact with EVM blockchains.
**Contract Development and Testing**
- [Foundry](https://github.com/foundry-rs/foundry) 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.
- [Truffle](https://trufflesuite.com/) and [Hardhat](https://hardhat.org/) are also viable alternatives.

View File

@ -0,0 +1,89 @@
# Relayer Module
Note: this module is only available in devnet, and is subject to change while still in development.
In order to integrate with the relayer module (which enables generic relaying), there are two requirements placed on the integrator.
1. The integrator must implement the `wormholeReceiver` interface, which will be called by the relayer to deliver the requested messages. If the recipient contract does not implement this function on their contract, the delivery will automatically fail.
2. The integrator must request delivery to the target chain via the `requestDelivery(DeliveryInstructions instructions)` function on the relayer module.
## Receiving Messages
Receiving messages through the relayer module is almost trivial. Simply implement this public function in your contract.
```
function wormholeReceiver(
bytes[] memory vaas,
uint16 sourceChain,
bytes32 sourceAddress,
bytes memory payload
)
```
This is the function which will be invoked by the relayer module to deliver messages.
- `vaas` are the VAAs which were requested for delivery.
- `sourceChain` is the Wormhole chain ID of the chain the messages were sent from.
- `sourceAddress` is the address which requested delivery. (In Wormhole format!)
- `payload` an additional payload which is at the top level.
There are only a few noteworthy items here:
- your `wormholeReceiver` function should never throw an exception. Throwing an exception here will just cause a delivery failure and _will not revert the transaction(!!!)_.
- `wormholeReceiver` will only be called with as much gas as was specified by the compute budget specified by the contract which requested delivery.
- Batch VAAs are always used by the relayer module. `vaas` is an array of all the headless VAAs for which delivery was requested. You still always need to call `core_bridge.parseAndVerifyVM`! The VAAs aren't verified until you have VM objects. (More on this in [Best Practices](./bestPractices.md))
- The generic relay VAA will be included in the `vaas` array you receive. Usually this VAA is ignored, but you can use it if it's useful to you.
## Sending Messages
In order to send a message to another contract, you must call `requestDelivery(DeliveryInstructions instructions)`. There are a few different things you can accomplish with this call.
First let's lay out the DeliveryInstructions object, which is part of the relayer module structs.
```
struct DeliveryParameters {
uint16 targetChain;
bytes32 targetAddress;
bytes payload;
VAAId[] deliveryList;
bytes relayParameters;
bytes chainPayload;
uint32 nonce;
uint8 consistencyLevel;
}
```
- `targetChain` is the chain Id of the chain this should be delivered to.
- `targetAddress` contract address (in Wormhole format) to deliver to.
- `payload` an additional payload which will be included in the delivery.
- `deliveryList` optional. The relayer will also deliver these (already existing) VAAs. This is the mechanism for re-delivery
- `relayParameters` information required to relay to the target env. Contains compute budget.
- `chainPayload` information which can be used for computation efficiency when relaying to other ecosystems.
- `nonce` optional. If included, only messages with this nonce will be relayed.
- `consistencyLevel` how long to wait before emitting the relay request.
- `msg.value` you must send enough native currency with this call to cover the compute budget specified in the relayer parameters.
## Compute Budget
Part of the relay parameters is 'computeBudget'. This specifies the maximum amount of computation which can be spent executing delivery on the destination contract. This is effectively a 'gasLimit' in the EVM ecosystem, but due to the relayer network supporting blockchains which don't utilize the concept of gas, we instead need the more generalizable concept of 'computation budget'.
When requesting delivery, the caller must specify and pay for the compute budget upfront. Compute budget which is not utilized will be refunded on the target chain. If the compute budget is exhausted during execution of the delivery, a delivery failure occurs. When a delivery failure occurs, the computation budget from the source chain is not refunded, as the relayer used it to process the failed transaction.
The computation 'rate' is specified by the relayer module and is different for each blockchain. The quote provided by the relayer module contains not only the fee for the requested compute budget, but also the fixed overheads of the computation which is done by the relayer contract.
## Delivery Failures
'Delivery Failure' is a technical term in the case of the the relayer module. It does not mean 'something went wrong', but rather that the relayer attempted to deliver the VAA, and was unsuccessful. There are only 3 causes of a delivery failure.
- The `wormholeReceiver` function is either missing or otherwise uncallable on the recipient contract.
- The `wormholeReceiver` function encountered an exception while processing.
- The `wormholeReceiver` function exhausted the gasLimit that was specified by the delivery requester.
All three of these scenarios are controllable by the integrator. In order to avoid delivery failures, the integrators should have a top-level try-catch, such that the wormholeReceiver never reverts, and should always request a worst-case compute budget, because excess budget will be refunded.
## Delivery Retries
In the unfortunate scenario of a delivery failure, the VAAs can be re-delivered by requesting their delivery a second time. To accomplish this, simply list their VAA IDs in the `deliveryList` in the call.
**More info and features to come. This module is still in development.**

View File

@ -0,0 +1,134 @@
# Token Bridge
This section will explain how to properly interact with the Wormhome Token Bridge Module in an EVM ecosystem.
## Configuring the interface
[Here](https://github.com/wormhole-foundation/wormhole/tree/wonge97/evm-interface/ethereum/contracts/bridge/interfaces) is the interface for applications to interact with Wormhole's Token Bridge.
Instantiating the interface will depend on your development ecosystem and blockchain. The Wormhole Token Bridge contract address is usually stored in your contract address.
Below is an example line of code to instantiate the interface for mainnet Ethereum:
```
address private wormhole_token_bridge_address = address(0x3ee18B2214AFF97000D974cf647E7C347E8fa585);
ITokenBridge token_bridge = ITokenBridge(wormhole_token_bridge_address);
```
## Registering New Tokens
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.
It is not advised to attest tokens on-chain for most usecases. 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 [Portal](https://www.portalbridge.com/#/register)) or using the [Typescript SDK](https://www.npmjs.com/package/@certusone/wormhole-sdk).
_[Here](../../development/portal/evm/attestingToken.md) is an example of how to attest a token using the Typescript SDK._
## Basic Transfer
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).
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.
To transfer a token, there are four steps:
1. Approve the Token Bridge to spend that token on our behalf.
- _Note: Tokens in EVM usually denote up to 18 decimals places. However. Wormhole normalizes this to **8** decimals._
```
contractAddress.approve(token_bridge_address, amt);
```
2. Transfer the token to create the transfer VAA.
- This function call will return a `sequence` (uint64) that is used in the VAA retrieval step.
- _Note: For the recipient address, Wormhole addresses are 32 bytes for standardization across the different blockchains within the Wormhole ecosystem._
```
// 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);
```
3. Retrieve the emitted VAA.
- Basic Transfer VAAs are retrieved from the Guardian Network by the `emitterChainID`, `emitterAddress`, and `sequence`.
```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("VAA not found, retrying in 5s!");
await new Promise((r) => setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
```
4. Complete the transfer using the VAA.
```
// To complete transfer of normal ERC-20s
token_bridge.completeTransfer(VAA);
// To complete transfer of native currency
completeTransferAndUnwrapETH(VAA);
```
## Contract Controlled Transfer
For any token transfers where the destination is a contract, you should always use Contract Controlled Transfers.
There are a few main differences between Contract Controlled Transfers and Basic Transfers:
- messages contains both tokens and an arbitrary payload
- messages can only be redeemed by a specified contract address
- messages do not have a relayer fee field because of the redemption restriction above
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.
The process of sending a Contract Controlled Transfer is very similar to that of a Basic Transfer:
1. Approve the Token Bridge to spend that token on our behalf.
- _Note: Tokens in EVM usually denote up to 18 decimals places. However. Wormhole normalizes this to **8** decimals._
```
contractAddress.approve(token_bridge_address, amt);
```
2. Transfer the token to create the transfer VAA.
- This function call will return a `sequence` (uint64) that is used in the VAA retrieval step.
- _Note: For the recipient addres, Wormhole addresses are 32 bytes for standardization across the different blockchains within the Wormhole ecosystem._
```
// To initiate transfer of normal ERC-20s
token_bridge.transferTokesWithPayload(tokenAddress, amount, recipientChain, recipient, nonce, payload);
// To initiate transfer of native currency
token_bridge.wrapAndTransferETHWithPayload(recipientChain, recipient, nonce, payload);
```
3. Retrieve the emitted VAA.
- Contract Controlled Transfer VAAs are retrieved from the Guardian Network by the `emitterChainID`, `emitterAddress`, and `sequence`.
```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("VAA not found, retrying in 5s!");
await new Promise((r) => setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
```
4. Complete the transfer using the VAA.
```
// To complete transfer of normal ERC-20s
token_bridge.completeTransferWithPayload(VAA);
// To complete transfer of native currency
completeTransferAndUnwrapETHWithPayload(VAA);
```

View File

@ -0,0 +1,9 @@
# NEAR
The implementation contracts for Wormhole's official NEAR integration can be found [here](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/near).
The deployed contract addresses can be found on the [Contracts page](../../reference/contracts.md).
NEAR is a rust-based programming environment. You'll likely find the [Wormhole Rust SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/rust) useful.
The NEAR integration is also fully supported by the [Wormhole Typescript SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/js).

View File

@ -0,0 +1,7 @@
# Contract Development
This section should help you get off the ground with contract development in the Wormhole ecosystem.
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.
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.

View File

@ -0,0 +1,29 @@
# Generic Relayers
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.
The implementation details vary by blockchain, and you should reference the `relayer module` documentation for each ecosystem. The general strategy is the same however.
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
```
receiveVAA(byte[] batchVAA)
```
This is the entrypoint on your contract which will be called by the relayer.
You are able to request delivery of a VAA via calling the `relayer module` 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.
This interface is generally along the lines of:
```
requestDelivery(
targetChain,
targetAddress,
computeBudget,
nonce,
consistencyLevel,
)
```
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 -->

View File

@ -0,0 +1,5 @@
# Relayer Development
This chapter will help you with relayer development in the Wormhole ecosystem.
The two broad categorizations of relayers are [Generic Relayers](./genericRelayer.md) and [Specialized Relayers](./specializedRelayers.md).

View File

@ -0,0 +1,17 @@
# Specialized Relayers
- Link to Plugin relayer codebase
- Recommend plugin relayers as the starting point for anyone developing specialized relayers
- plugin relayers provide a kernel for relayer development. Handles management of all the necessary hotwallets, provides the necessary typescript interfaces for dealing with each ecosystems, and provides an easy integration point to connect to the guardian network
- Follow the instructions provided in the codebase in order to get it running
- to develop a plugin, simply implement the interface provided at [here]
- diagram explaining the flow
- Listener component listens to either incoming REST calls or the guardian network for relevant VAAs,
- When a relevant VAA is detected, your listener code is responsible for producing an action.
- This action is stored in a redis instance
- The executor portion of the interface is responsible for consuming interactions which are provided by the listener.
- The executor is handed an action and is responsible for consuming that action and (optionally) queuing up more actions
- additional configuration info can be found in the README of the codebase.

View File

@ -0,0 +1,9 @@
# Specialized Relayers
Rather than home-rolling a relayer, it's recommended that integrators start from the existing [Spy Relayer](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/relayer/spy_relayer) provided in the Wormhole Core Repository.
There's additionally an extensible relayer (called the [Plugin Relayer](https://github.com/wormhole-foundation/wormhole/tree/feat/plugin_relayer/relayer/plugin_relayer)) currently in development.
<!-- 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. -->

View File

View File

View File

@ -0,0 +1,9 @@
# Solana
The implementation contracts for Wormhole's official Solana integration can be found [here](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/solana).
The deployed contract addresses can be found on the [Contracts page](../../reference/contracts.md).
Solana is a rust-based programming environment. You'll likely find the [Wormhole Rust SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/rust) useful.
The Solana integration is also fully supported by the [Wormhole Typescript SDK](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/js).

View File

View File

@ -0,0 +1,64 @@
# Registering Tokens
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 is typically done via a UI (such as [Portal](portalbridge.com)) rather than done on-chain.
If you need to do it programmatically, you can also use the Typescript SDK to attest a token.
There are three steps to registerring a token:
1. Create an AttestMeta VAA by calling `attest()` function from the SDK and passing in the Token Bridge address, and the address of the Token we want to attest.
For example, here is the code to produce an attestation VAA using ethers:
```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
);
```
The attestation transaction will produce a signed VAA. This signed VAA is necessary in order to register the tokens on other chains.
2. Retrieve the VAA with the `emitterAddress` of the Token Bridge and the `sequence` from the logs of the transaction receipt.
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.
Here is a relatively compact piece of code which is able to fetch **any** VAA, given an emitter address and sequence number.
```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("Searching for: ", vaaURL);
let vaaBytes = await (await fetch(vaaURL)).json();
while (!vaaBytes.vaaBytes) {
console.log("VAA not found, retrying in 5s!");
await new Promise((r) => setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
```
3. Submit the VAA onto the target chain to create a wrapped version of the token by calling `createWrapped()`.
You can get the new wrapped token address by calling the `wrappedAsset()` function of the TokenBridge.
Here is how this can be accomplished using Ethers:
```js
await targetTokenBridge.createWrapped(
Buffer.from(vaaBytes.vaaBytes, "base64"),
{
gasLimit: 2000000,
}
);
await new Promise((r) => setTimeout(r, 5000)); //Time out to let block propogate
const wrappedTokenAddress = await targetTokenBridge.wrappedAsset(
network.wormholeChainId,
Buffer.from(tryNativeToHexString(network.testToken, "ethereum"), "hex")
);
console.log("Wrapped token created at: ", wrappedTokenAddress);
```

View File

@ -0,0 +1,109 @@
# Cross-Ecosystem Token Transfer
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.
To demonstrate, lets do a simple programmatic transfer from Ethereum to Solana.
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.
```ts
import {
Token,
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import {
getForeignAssetSolana,
hexToUint8Array,
nativeToHexString,
CHAIN_ID_ETH,
} from "@certusone/wormhole-sdk";
const SOLANA_TOKEN_BRIDGE_ADDRESS =
"wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb";
// 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) || "")
)) || ""
);
const recipientAddress = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
solanaMintKey,
recipientWalletAddress
);
```
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.
```ts
import {
trasnferFromEth,
parseSequenceFromLogEth,
getEmitterAddressEth,
CHAIN_ID_SOLANA,
} from "@certusone/wormhole-sdk";
const ETH_TOKEN_BRIDGE_ADDRESS = "0x3ee18B2214AFF97000D974cf647E7C347E8fa585";
// 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);
```
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.
```ts
import { getSignedVAA } from "@certusone/wormhole-sdk";
// 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
);
```
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.
```ts
const SOL_BRIDGE_ADDRESS = "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth";
await postVaaSolana(
connection, // Solana Mainnet Connection
wallet, //Solana Wallet Signer
SOL_BRIDGE_ADDRESS,
payerAddress,
signedVAA
);
```
Finally, claim the tokens:
```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);
```

View File

@ -0,0 +1,15 @@
# Wormhole Typescript SDK
A Wormhole Typescript SDK provided for applications that only need to interact with the Core and Token Bridge contracts off-chain.
It can be installed using npm:
```sh
npm i @certusone/wormholesdk
```
The following sections will explain and provide examples of how to perform key functions with Wormhole using the Typescript SDK.
For more examples with a more exhaustive coverage of all the supported blockchains in Wormhole, be sure to check the [official codebase](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/sdk/js) for the Typescript SDK.
Virtually all functions of the SDK are demonstrated in the [reference bridge UI](https://github.com/wormhole-foundation/example-token-bridge-ui), which makes it an excellent source of example code as well.

View File

@ -0,0 +1,61 @@
# Token Transfers
<!-- //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. -->
Before transferring tokens, you should ensure that the token is [registered](./attestingToken.md) 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.
There are four steps to transferring a token:
1. If not already done, complete a standard ERC-20 token approval prior to performing a bridge action if you're in the EVM ecosystem.
```js
// Here we are approving and transfering 50 tokens. The ERC20 token we are transfering has 18 decimal places.
const bridgeAmt = ethers.utils.parseUnits("50", "18");
await treasury.approveTokenBridge(bridgeAmt, {
gasLimit: 2000000,
});
```
2. Initate a transfer by calling `transfer` on the token bridge module which will create a transfer VAA.
_Note that the target receipient is a Wormhole-format address (referred to as 'hex' format in the Typescript SDK)._
```js
const targetRecepient = Buffer.from(
tryNativeToHexString(targetDeployment.deployedAddress, "ethereum"),
"hex"
);
const tx = await (
await treasury.bridgeToken(
bridgeAmt,
targetNetwork.wormholeChainId,
targetRecepient
)
).wait();
```
3. Retrieve the VAA with the `emitterAddress` of the Token Bridge and the `sequence` from the logs of the transaction receipt. (This is the same code as shown in the previous section.)
```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("VAA not found, retrying in 5s!");
await new Promise((r) => setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
vaaBytes = await (await fetch(vaaURL)).json();
}
```
4. Submit the VAA to the target chain by calling `completeTransfer()`.
If you're not using a relayer, you'll have to submit the target chain transaction yourself. [This section](./polygon-oasis-relayer.md) outlines how to use relayers.
```js
const completeTransferTx = await targetTokenBridge.completeTransfer(
Buffer.from(vaaBytes.vaaBytes, "base64")
);
```

View File

@ -0,0 +1,210 @@
# Using Relayers
In this example, well utilize the token bridge relayer network to complete a token transfer from Polygon and to Oasis.
This code is written for a browser environment. If you're working in node, consider using node-fetch:
```bash
npm i --save @certusone/wormhole-sdk ethers node-fetch
```
```ts
import { BigNumber, ethers } from "ethers";
import fetch from "node-fetch";
import {
getEmitterAddressEth,
hexToUint8Array,
nativeToHexString,
parseSequenceFromLogEth,
CHAIN_ID_POLYGON,
CHAIN_ID_OASIS,
transferFromEthNative,
getIsTransferCompletedEth,
setDefaultWasm,
} from "@certusone/wormhole-sdk";
```
### Setup the Polygon and Oasis Wallets
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.
```ts
const EmeraldWallet = new ethers.Wallet(
privatekey_emerald,
new ethers.providers.JsonRpcProvider("https://emerald.oasis.dev")
);
const PolygonWallet = new ethers.Wallet(
privatekey_polygon,
new ethers.providers.JsonRpcProvider("https://polygon-rpc.com/")
);
```
### Fetch the fee schedule
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.
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).
```ts
const transferAmount = BigNumber.from("1000000000000000000"); // We are sending 1 MATIC over the wall to Oasis
const relayerFeeSchedule = await(
await fetch(
"https://raw.githubusercontent.com/certusone/wormhole-relayer-list/main/relayer.json"
)
).json();
```
The fee schedule has the following interface:
```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: "flat" | "percent";
feeUsd?: number;
feePercent?: number;
gasEstimate?: number;
};
}
```
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:
```ts
let feeWei: number;
if (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].type == "flat") {
const feeUsd = relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feeUsd;
const MATIC_PRICE = await(
await fetch(
"https://api.coingecko.com/api/v3/simple/token_price/polygon-pos?contract_addresses=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270&vs_currencies=usd"
)
).json()["0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270"]["usd"];
feeWei = (feeUsd / MATIC_PRICE) * 1e18;
} else if (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].type == "percent") {
let feeWei =
(relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].feePercent / 100) *
transferAmount.toNumber();
}
```
### Overrides & Quirks
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.
```ts
let overrides;
let feeData = await PolygonWallet.provider.getFeeData();
overrides = {
maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(50) || undefined,
};
```
### Emit Token Bridge Message
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.
```ts
const POLYGON_TOKEN_BRIDGE = "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE";
const receipt = await transferFromEthNative(
POLYGON_TOKEN_BRIDGE,
PolygonWallet,
transferAmount,
CHAIN_ID_OASIS,
hexToUint8Array(
nativeToHexString(await EmeraldWallet.getAddress(), CHAIN_ID_OASIS) || ""
),
BigNumber.from(feeWei.toString()),
overrides
);
console.log("Receipt: ", receipt);
const POLYGON_CORE_BRIDGE_ADDRESS =
"0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7";
const sequence = parseSequenceFromLogEth(receipt, POLYGON_CORE_BRIDGE_ADDRESS);
const emitterAddress = getEmitterAddressEth(POLYGON_TOKEN_BRIDGE);
console.log("Sequence: ", sequence);
console.log("EmitterAddress: ", emitterAddress);
```
Lets walk through each of the arguments of this function and what they mean.
`POLYGON_TOKEN_BRIDGE` is the address of the token bridge module on the Polygon network. You can find it and other addresses on the [contracts](../../reference/contracts.md) page.
`PolygonWallet` is a signer you get from the Ethers library that holds a private key that can sign transactions.
`transferAmount` is a BigNumber that contains the amount to transfer in the smallest unit of the network.
`CHAIN_ID_OASIS` is a constant that identifies the target chain.
`hexToUint8Array()` translates the target publickey into a wormhole public key.
`BigNumber.from(feeWei.toString())` identifies the fee in smallest unit of the network for the relayer.
`overrides` are used if we need to override the gas cost, which we need to do for Polygon.
### Check VAA was signed
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.
```ts
await new Promise((r) => setTimeout(r, 900000)); //15m in seconds
const WORMHOLE_RPC = "https://wormhole-v2-mainnet-api.certus.one";
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) => setTimeout(r, 5000));
}
}
console.log("VAA Bytes: ", vaaBytes);
```
### Check if the transfer was completed
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.
```ts
setDefaultWasm("node"); //only needed if running in node.js
const EMERALD_TOKEN_BRIDGE = "0x5848C791e09901b40A9Ef749f2a6735b418d7564";
let transferCompleted = await getIsTransferCompletedEth(
EMERALD_TOKEN_BRIDGE,
EmeraldWallet.provider,
vaaBytes
);
while (!transferCompleted) {
await new Promise((r) => setTimeout(r, 5000));
transferCompleted = await getIsTransferCompletedEth(
EMERALD_TOKEN_BRIDGE,
EmeraldWallet.provider,
vaaBytes
);
}
console.log("VAA Relayed!");
```
Success! You've programmatically relayed a transaction!

View File

@ -1,6 +1,6 @@
# What is Wormhole?
Wormhole was introduced in 2020 by Certus One, and was initially conceived as a traditional token bridge between Ethereum and Solana. The Wormhole v1 Token Bridge was the first bridge on Solana and was responsible for bootstrapping a large amount of the liquidity in the early Solana and Serum ecosystems.
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.
However, despite its beginnings as a token bridge, Wormhole quickly grew beyond Solana and token transfers.
@ -10,4 +10,6 @@ Over the past year, Wormhole has evolved to support an ever-growing list of bloc
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 a increasing number of composible applications built by numerous teams.
---
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.

View File

@ -2,36 +2,42 @@
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.
![Architecture Diagram](../diagrams/images/architecture-2022-01-02-1935.png)
![Architecture Diagram](../diagrams/images/architecture.PNG)
### On-Chain Components
- **xDapp Contracts** - 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.
- **xDapp Contracts** - 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.
- **Ecosystem Contracts** - Contracts subject to Wormhole governance which live inside the Wormhole Ecosystem. Their job is to provide the feature suite of Wormhole to xDapp developers.
- **Ecosystem Contracts** - Contracts subject to Wormhole governance which live inside the Wormhole Ecosystem. Their job is to provide the feature suite of Wormhole to xDapp developers.
- **Core Contracts** - Primary ecosystem contracts. These are the contracts which the Guardians observe and which fundamentally allow for cross-chain communication.
- **Portal xAsset Contracts** - Contracts that allow normal tokens to be converted to xAssets and enable these xAssets to be bridged.
- **xAsset Contracts** - Contracts that allow normal tokens to be converted to xAssets and enable these xAssets to be bridged.
- **Relay Contracts** - _in development\*_ - Contracts that allow xDapps to send messages to a specific blockchain via the decentralized Generic Relayer network.
- **Gas Oracle** - _in development\*_ - Oracle for recommended fair gas prices across the ecosystem.
- **Worm Router Contracts** - _in development\*_ - Contracts that allow developers to make their Dapp an xDapp that users on any Wormhole supported chain can interac with purely through clientside code.
### Off-Chain Components
- **Guardian Network** - 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.
- **Guardian Network** - 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.
- **Guardian** - One of 19 validators in the Guardian Network that contributes to the VAA multisig.
- **Guardian** - One of 19 validators in the Guardian Network that contributes to the VAA multisig.
- **Spy** - 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.
- **Spy** - 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.
- **Wormchain** - _in development\*_ - A purpose-built cosmos blockchain which aids the Guardian Network and allows for formal interaction with the Guardians.
- **VAAs** - 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.
- **Specialized Relayers** - Relayers that only handle VAAs for a specific protocol or xDapp. This allows them to 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.
- **Specialized Relayers** - 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.
- **Generic Relayers** - _in development\*_ - A decentralized relayer network which delivers messages that are requested on-chain via the Wormhole Relay Contract.
- **Generic Relayers** - _in development\*_ - A decentralized relayer network which delivers messages that are requested on-chain via the Wormhole Relay Contract.
- **VAAs** - 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.
- **Wormchain** - _in development\*_ - A purpose-built cosmos blockchain which aids the Guardian Network and allows for formal interaction with the Guardians.
\*\*Features listed as _in development_ are not yet available in mainnet.
\*Features listed as _in development_ are not yet available.
---
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.

View File

@ -4,11 +4,11 @@ The Core Contracts are the mechanism by which all Wormhole messages are emitted.
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.
In general, Core Contracts are simple with only a few public-facing functions, which we'll define next.
In general, Core Contracts are simple and can be broekn down to a **sending** and **receiving** side, which we'll define next.
---
### Sending
First, we have the 'sending' side of the Core Contract:
Below is the mechanism by which Wormhole messages (aka Verified ACtion Approval, VAA) are emitted:
publishMessage(
int nonce,
@ -16,23 +16,23 @@ First, we have the 'sending' side of the Core Contract:
int consistencyLevel
) returns int sequenceNumber
This is the foundational mechanism by which Wormhole Messages are emitted. Let's break it down a bit:
Let's break it down a bit:
- **payload** - 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.
- **consistencyLevel** - The number of blocks which the Guardians should wait prior to emitting a VAA for this message. This number is usually either 1 or equal to the chain's finality period. This is a defense against transactions being orphaned.
- **nonce** - If multiple messages in the same transaction have the same nonce, a batch VAA will be produced alongside the individual VAAs on chains that allow them. This reduces gas costs and simplifies composeability.
- **nonce** - An index number for the message that is used to produce Batch VAAs. How this is generated is elaborated in the [CoreLayer](../technical/evm/coreLayer.md) section.
- **sequenceNumber** - 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.
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 elapsed, the Guardian Network will produce the requested VAAs.
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 elapsed and the message passes all of the Guardians' optional checks, the Guardian Network will produce the requested VAAs.
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.
---
### Receiving
Next, we have the 'receiving' side of the Core Contract.
Below is the mechanism by which VAAs are received:
parseAndVerifyVAA( byte[] VAA )
@ -48,8 +48,10 @@ VAAs simply attest that "this contract on this chain said this thing." Therefore
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.
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 Portal & the Relaying contract) handle this on behalf of downstream consumers.
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 & the Relaying contract) handle this on behalf of downstream consumers.
Lastly, because the VAA creation is separate from relaying, there is _no additional cost_ to the multicast model when a single chain is being targetted. If the data isn't needed on a certain blockchain, don't relay it there, and it won't cost anything.
---
In our next section, we'll dive into the technical specfications of the VAA.

View File

@ -27,13 +27,31 @@ The Body is the relevant information for consumers and is handed back from parse
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.
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, and it is entirely the responsibility of relayers to deliver VAAs to the appropriate place.
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 destionation, it is entirely the responsibility of relayers to complete that delivery appropriately.
## Batch VAAs
Certain blockchains support version 2 VAAs, also referred to as **Batch VAAs**. When multiple messages with the same nonce are emitted in the same transaction, a batch VAA will be created in addition to the individual VAAs. The Batch VAA contains the body of each individual VAA, but only has a single header. This reduces the gas cost of verifying the VAA, and simplifies the process of relaying and consuming multiple VAAs.
Certain blockchains support version 2 VAAs, also referred to as **Batch VAAs** 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.
Batch VAAs are not currently live on mainnet, but will have initial support on all EVM chains when they launch.
Batch VAAs are designed to be automatically generated for all messages that come from a single transaction.
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 `nonce` to _additionally_ group them.
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.
Go [here](../technical/evm/coreLayer.md) for a more detailed description of how Batch VAAs are generated.
_Note: Batch VAAs are not currently live on mainnet, but will have initial support on all EVM chains when they launch._
> How to leverage Batch VAAs
>
> Imagine a transaction generates three messages (A, B, C) that a consuming contract needs to know about.
>
> If each message is independent of each other, the consuming contract can handle and validate each of these VAAs individually like [A], [B], [C].
>
> 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].
>
> If only two of the messages are related to each other, say A and C, the same `nonce` 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].
---

View File

@ -48,7 +48,7 @@ Wormhole can expand to new ecosystems as quickly as a Core Contract can be devel
## Scalability
Wormhole scales well, as demonstrated by Portal's ability to handle huge TVL and transaction volume--even during tumultuous events.
Wormhole scales well, as demonstrated by its ability to handle huge TVL and transaction volume--even during tumultuous events.
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.
@ -58,4 +58,6 @@ However, once all the full nodes are running, the actual computation and network
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 eachother. 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.
---
In the next section, we will talk about the role and responsbilities of relayers in the Wormhole ecosystem.

View File

@ -1,6 +1,12 @@
# Relayers
Relayers are a major part of the Wormhole Ecosystem. Where the Guardian Network is effectively the 'read' portion of interoperability, relayers are the 'write' portion.
All simple cross-chain processes on Wormhole essentially boil down to a three step process:
1. Perform an action on chain A.
2. Retrieve the resulting VAA from the Guardian Network.
3. Perform an action on chain B using the VAA.
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.
The definition of a _Relayer_ in the context of Wormhole is: Any process which delivers VAAs to a destination.
@ -18,31 +24,27 @@ Next, we'll go over a few of the most common relaying strategies.
## Client-side Relaying
All simple processes on Wormhole essentially boil down to a three step process:
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.
1. Perform an action on chain A.
2. Retrieve the resulting VAA from the Guardian Network.
3. Perform an action on chain B using the VAA.
Considering that the first step of this process is almost always initiated from a user-facing frontend like a webpage or a wallet, it is possible to also perform steps 2 and 3 in the frontend as well. This is referred to as 'client-side relaying', and it has two major benefits:
There are two major benefits of this approach:
- Low cost. Users pay exactly the transaction fee for the second transaction.
- No backend relaying infrastructure.
That makes client-side relaying a tempting prospect, especially if you're just interested in getting an MVP running. However, client-side relaying also has two notable drawbacks:
However, client-side relaying also has two notable drawbacks:
- Users must sign all transactions required with their own wallet.
- Users must have funds to pay the transaction fees on every chain involved.
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.
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.
## Specialized Relayers
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.
In this model, relayers either listen directly to the Guardian Network via a spy (This is called **Spy Relaying**), or will simply provide a REST endpoint to accept a VAA which should be relayed (called **REST Relaying**). Once a relayer has the VAA, it simply performs any necessary off-chain calculations and submits the VAA to the required destination.
In this model, relayers either listen directly to the Guardian Network via a spy (called **Spy Relaying**), or will simply provide a REST endpoint to accept a VAA which should be relayed (called **REST Relaying**). Once a relayer has the VAA, it simply performs any necessary off-chain calculations and submits the VAA to the required destination.
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 the off-chain relayer should not do any computation which is considered "trusted." However, doing things like deterministic data transforms, waiting for gas prices to drop, or various forms of 'batching' can be very useful cost-reduction strategies that do not impact security.
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 "trusted." 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.
Specialized Relayers have the following advantages:
@ -55,11 +57,19 @@ However, they also have a couple notable downsides
- 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.
Because relayers are responsible for liveness, they become another dependency component for the xDapp. If the relayers are all down, your application has an outage. This is similar to how dependencies like the frontend, blockchain nodes, blockchains, third party APIs, etc, can also cause outages.
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.
To mitigate this, multiple relayers can be run in order to provide redundancy. It's also possible to design specialized relaying solutions which are entirely decentralized, such that there are a network of relayers which run based off economic incentives. However, creating a robust model for decentralized relaying is generally application-specific and complex.
If you plan to develop a specialized relayer, consider starting from the plugin relayer [found here](https://github.com/wormhole-foundation/wormhole/tree/dev.v2/relayer).
Due to specialized relayers being such a common solution, there is a reference implementation provided in the main Wormhole repository which stands up the infrastructure needed to provide a Spy interface, a REST interface and the ability to interact with each blockchain in the ecosystem. If you plan to develop a specialized relayer, consider starting from the reference implementation and add modifications as needed.
<!--
TODO link to plugin relayer once it has been merged down
-->
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.
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. _However, creating a robust model for decentralized relaying is generally application-specific and complex._
Overall, Specialized Relayers add a backend component that is responsible for liveness, but can simplify the user experience. It's generally recommend if your goal is a highly-polished user experience and you want to have better control over message delivery.
# Generic Relayers
@ -77,9 +87,11 @@ Generic relayers have the following benefits:
And potential downsides:
- They require all calculations to be done on-chain
- They have less gas efficiency
- They sometimes have less gas efficiency
- They may not be supported on all chains
Overall, Generic Relayers simplify both the developer and user experience. They're a great choice if they cover all your usecases.
---
In the next section, we'll discuss the Portal Ecosystem contracts that allow xAssets to be created and moved freely around the ecosystem.
In the next section, we'll discuss the xAsset module, which allows xAssets to be created and moved freely around the ecosystem.

View File

@ -1,53 +0,0 @@
# Portal xAsset Bridge
Portal is a set of ecosystem contracts that provision Wormhole's xAsset layer. These contracts allow tokens to be bridged around the Wormhole Ecosystem in a **path-independent** fashion, and are easily composeable with other functions in the Wormhole ecosystem.
This section provides a high-level overview of how to interact with the Portal contracts. If you're looking to interact with Portal directly from a typescript client or backend, you should start with the [Wormhole Typescript SDK](https://www.npmjs.com/package/@certusone/wormhole-sdk). If you'd prefer to look at code examples, they are provided in the **Portal Examples** section.
## Creating xAssets
xAssets always have an **origin chain**. This is where the token is initially minted via the standard of that chain (ERC-20, SPL, etc).
To convert this asset into an xAsset, an **attestation** must first be created. To create an attestation, simply call the **attest** function on the Portal contract of the origin chain.
function attestToken(
address tokenAddress,
uint32 nonce)
returns (uint64 sequence)
The Guardian Network will then produce an **attestation VAA**, which can be retrieved using the sequence number returned by the attestToken function.
The attestation VAA must then be submitted to the **createWrapped** function of every other chain, referred to as **foreign chains** for this token.
function createWrapped(
bytes memory encodedVm)
returns (address token)
Calling this function will deploy a new contract for the token on the foreign chain, creating a **Wormhole-Wrapped Token**. The wrapped token will use the same symbol as the origin asset, and will append (Wormhole) to the end of the name.
These assets are all **fungible** with each other. This means the Wormhole-wrapped token can be exchanged for the original token or wrapped tokens from other chains.
## Transferring Assets
function transferTokens(
address token,
uint256 amount,
uint16 recipientChain,
bytes32 recipient,
uint256 arbiterFee,
uint32 nonce) returns (uint64 sequence)
Initiating token transfers is a straightforward affair. Once the transfer is initiated, the Guardians will produce a transfer VAA when finality has been reached on the **source chain**. The VAA must then be relayed to the **target chain**.
All tokens managed by Portal are backed by the origin asset, allowing tokens to be transferred in a path-independent fashion. Regardless of what chain the tokens are passed to, a 'double-wrapped' asset will never be created for a single backing asset. Additionally, there are no liquidity limitations.
## Contract-Controlled Transfers
Basic transfers are intended to transfer tokens from one wallet to another, whereas Contract Controlled Transfers (CCTs) are meant to transfer tokens from one smart contract to another. If you're writing an xDapp, CCTs will likely be a large component.
CCTs allow xDapp contracts to easily perform Portal transfers. Contract controlled transfers are quite similar to simple transfers, but have two additional features:
- An arbitrary byte array can be appended to the transfer and can be used to easily pass additional information to the recipient contract.
- 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.
In the next section, we'll discuss Wormchain and some of the upcoming features it will enable.

View File

@ -0,0 +1,79 @@
# xAsset Layer
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 **path-independent** fashion, and are easily composable with other functions in the Wormhole ecosystem.
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.
If you're looking to interact with the Token Bridge directly from a typescript client or backend, you should start with the [Wormhole Typescript SDK](https://www.npmjs.com/package/@certusone/wormhole-sdk).
## Creating xAssets
xAssets always have an **origin chain**. 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).
xAssets are all **fungible** with each other. This means the Wormhole-wrapped asset can be exchanged for the original asset or a wrapped asset from other chains.
**Tokens**
To convert tokens into an xAsset, an **attestation** must first be created. To create an attestation, simply call the **attest** function on the token bridge contract of the origin chain.
function attestToken(
address tokenAddress,
uint32 nonce)
returns (uint64 sequence)
The Guardian Network will then produce an **attestation VAA**, which can be retrieved using the sequence number returned by the attestToken function.
The attestation VAA must then be submitted to the **createWrapped** function of every other chain, referred to as **foreign chains** for this token.
function createWrapped(
bytes memory encodedVm)
returns (address token)
Calling this function will deploy a new contract for the token on the foreign chain, creating a **Wormhole-Wrapped Token**. The wrapped token will use the same symbol as the origin asset, and will append (Wormhole) to the end of the name.
**NFTs**
NFTs do not need need to be attested before they can be created into a xAsset.
## Transferring xAssets
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 **source chain**. The VAA must then be relayed to the **target chain**.
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.
**Tokens**
```
function transferTokens(
address token,
uint256 amount,
uint16 recipientChain,
bytes32 recipient,
uint256 arbiterFee,
uint32 nonce) returns (uint64 sequence)
```
**NFTs**
```
function transferNFT(
address token,
uint256 tokenID,
uint16 recipientChain,
bytes32 recipient,
uint32 nonce) returns (uint64 sequence)
)
```
## Contract-Controlled Transfers
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.
CCTs allow xDapp contracts to easily perform simple xAsset transfers, but have two additional features:
- An arbitrary byte array can be appended to the transfer and can be used to easily pass additional information to the recipient contract.
- 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.
---
In the next section, we'll discuss Wormchain and some of the upcoming features it will enable.

View File

@ -13,4 +13,6 @@ Wormchain is built to provide things like:
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.
---
In the next section, we'll get into the key concepts that underpin xDapp design.