aptos working in messenger v1

This commit is contained in:
spacemandev 2022-12-19 00:27:59 -06:00
parent 4dcb18a929
commit 1c957b0b04
32 changed files with 7275 additions and 1638 deletions

View File

@ -27,4 +27,4 @@ If you see transactions failing due to nonce mismatch errors, restart your local
This is intermittent and likely caused by the js lib and evm chain being out of sync.
#### Transaction Underpriced Issues
Transactions failing with the message `transaction underpriced` is an intermittent issue. Restart the evm chains and rerun `npm run test`. If the issue persists, you may need to manually set gas prices when calling the Messenger contract in the `send_msg` logic in `orchestrator.js`.
Transactions failing with the message `transaction underpriced` is an intermittent issue. Restart the evm chains and rerun `npm run test`. If the issue persists, you may need to manually set gas prices when calling the Messenger contract in the `send_msg` logic in `orchestrator.js`.

View File

@ -1,18 +0,0 @@
/*
module core_messages::counter {
struct Counter has key, store {
count: u64
}
public entry fun init_counter(counter: &signer) {
//use aptos_framework::account;
//let (counter_acc, _) = account::create_resource_account(messenger, b"Counter");
move_to(counter, Counter { count: 0} );
}
public entry fun increment() acquires Counter {
let counter = borrow_global_mut<Counter>(@core_messages);
counter.count = counter.count + 1;
}
}
*/

View File

@ -2,22 +2,67 @@
module core_messages::messenger {
use wormhole::wormhole;
use aptos_framework::coin;
use std::table::{Self, Table};
use wormhole::u16::U16;
use wormhole::external_address::ExternalAddress;
use aptos_framework::signer;
use aptos_framework::account;
use std::string::{Self, String};
struct State has key {
emitter_cap: wormhole::emitter::EmitterCapability,
const E_ONLY_CORE_MESSAGES_CAN_INIT:u64 = 0;
const E_ONLY_ADMIN:u64 = 1;
const E_VAA_ALREADY_CONSUMED:u64 = 2;
const E_VAA_EMMITTER_NOT_REGISTERED:u64 = 3;
struct MessengerAdminCapability has key {
// The ability to make changes to the State Resource Account
resource_signer_cap: account::SignerCapability,
}
public entry fun init_messenger(core_messages: &signer) {
struct State has key {
// Admin Address
admin: address,
// The ability to send wormhole messages
emitter_cap: wormhole::emitter::EmitterCapability,
// Mapping of bridge contracts on other chains
registered_emitters: Table<U16, ExternalAddress>,
// Current Message
current_message: String,
// Consumed VAAs
consumed_vaas: Table<vector<u8>, bool>,
}
public entry fun init_messenger(core_messages: &signer, admin: address) {
// Requires the private key of Core Messages to have signed this message
// This is to make sure the state get stored under core messages resource account
assert!(signer::address_of(core_messages) == @core_messages, E_ONLY_CORE_MESSAGES_CAN_INIT);
let (resource_signer, signer_cap) = account::create_resource_account(core_messages, b"messenger_state");
// Register ourselves as a wormhole emitter. This gives back an
// `EmitterCapability` which will be required to send messages through
// wormhole.
let emitter_cap = wormhole::register_emitter();
move_to(core_messages, State { emitter_cap });
move_to(&resource_signer, State {
admin,
emitter_cap,
registered_emitters: table::new(),
current_message: string::utf8(b"uninitalized"),
consumed_vaas: table::new()
});
move_to(core_messages, MessengerAdminCapability { resource_signer_cap: signer_cap } );
}
public entry fun send_message(user: &signer, payload: vector<u8>) acquires State {
// Fetch the Resource Account that has state
let state_resource_account = account::create_resource_address(&@core_messages, b"messenger_state");
// Retrieve emitter capability from the state
let emitter_cap = &mut borrow_global_mut<State>(@core_messages).emitter_cap;
let emitter_cap = &mut borrow_global_mut<State>(state_resource_account).emitter_cap;
// Set nonce to 0 (this field is not interesting for regular messages,
// only batch VAAs)
@ -33,4 +78,44 @@ module core_messages::messenger {
fee_coins
);
}
public entry fun register_emitter(user:&signer, chainID: u64, external_address:vector<u8>) acquires State {
let chain_id = wormhole::u16::from_u64(chainID);
let foreign_address = wormhole::external_address::from_bytes(external_address);
// Fetch the Resource Account that has state
let state_resource_account = account::create_resource_address(&@core_messages, b"messenger_state");
// Retrieve emitter capability from the state
let admin_address = &borrow_global<State>(state_resource_account).admin;
assert!(*admin_address == signer::address_of(user), E_ONLY_ADMIN);
//Get Registered Emitters
let state = borrow_global_mut<State>(state_resource_account);
table::upsert(&mut state.registered_emitters, chain_id, foreign_address);
}
public entry fun receive_message(vaa_bytes: vector<u8>) acquires State {
// Verify the wormhole message
let vaa = wormhole::vaa::parse_and_verify(vaa_bytes);
// Fetch the Resource Account that has state
let state_resource_account = account::create_resource_address(&@core_messages, b"messenger_state");
let state = borrow_global_mut<State>(state_resource_account);
// Check the VAA has not been consumed already
assert!(!table::contains(&state.consumed_vaas, wormhole::vaa::get_hash(&vaa)), E_VAA_ALREADY_CONSUMED);
// Check the emitter chain is registered
assert!(*table::borrow(&state.registered_emitters, wormhole::vaa::get_emitter_chain(&vaa)) == wormhole::vaa::get_emitter_address(&vaa), E_VAA_EMMITTER_NOT_REGISTERED);
// Set the current message to the vaa message
state.current_message = string::utf8(wormhole::vaa::get_payload(&vaa));
// Store the hash of the vaa in the consumed VAAs
table::upsert(&mut state.consumed_vaas, wormhole::vaa::get_hash(&vaa), true);
wormhole::vaa::destroy(vaa);
}
}

View File

@ -1,86 +0,0 @@
/*
/// A simple contracts that demonstrates how to send messages with wormhole.
module core_messages::sender {
use wormhole::wormhole;
use aptos_framework::coin;
struct State has key {
emitter_cap: wormhole::emitter::EmitterCapability,
}
entry fun init_module(core_messages: &signer) {
// Register ourselves as a wormhole emitter. This gives back an
// `EmitterCapability` which will be required to send messages through
// wormhole.
let emitter_cap = wormhole::register_emitter();
move_to(core_messages, State { emitter_cap });
}
#[test_only]
/// Initialise module for testing.
public fun init_module_test() {
use aptos_framework::account;
// recover the signer for the module's account
let signer_cap = account::create_test_signer_cap(@core_messages);
let signer = account::create_signer_with_capability(&signer_cap);
// then call the initialiser
init_module(&signer)
}
public entry fun send_message(user: &signer, payload: vector<u8>) acquires State {
// Retrieve emitter capability from the state
let emitter_cap = &mut borrow_global_mut<State>(@core_messages).emitter_cap;
// Set nonce to 0 (this field is not interesting for regular messages,
// only batch VAAs)
let nonce: u64 = 0;
let message_fee = wormhole::state::get_message_fee();
let fee_coins = coin::withdraw(user, message_fee);
let _sequence = wormhole::publish_message(
emitter_cap,
nonce,
payload,
fee_coins
);
}
}
#[test_only]
module core_messages::sender_test {
use wormhole::wormhole;
use core_messages::sender;
use aptos_framework::account;
use aptos_framework::aptos_coin::{Self, AptosCoin};
use aptos_framework::coin;
use aptos_framework::signer;
use aptos_framework::timestamp;
#[test(aptos_framework = @aptos_framework, user = @0x111)]
public fun test_send_message(aptos_framework: &signer, user: &signer) {
let message_fee = 100;
timestamp::set_time_has_started_for_testing(aptos_framework);
wormhole::init_test(
22,
1,
x"0000000000000000000000000000000000000000000000000000000000000004",
x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe",
message_fee
);
sender::init_module_test();
let (burn_cap, mint_cap) = aptos_coin::initialize_for_test(aptos_framework);
// create user account and airdrop coins
account::create_account_for_test(signer::address_of(user));
coin::register<AptosCoin>(user);
coin::deposit(signer::address_of(user), coin::mint(message_fee, &mint_cap));
sender::send_message(user, b"hi mom");
coin::destroy_mint_cap(mint_cap);
coin::destroy_burn_cap(burn_cap);
}
}
*/

View File

@ -1,13 +1,21 @@
import fs from 'fs';
import { AptosClient } from "aptos";
import { AptosAccount, AptosClient, Types } from "aptos";
import { Command } from 'commander';
import { AptosMessenger } from './sdk';
import { AptosMessenger, EVMMessenger } from './sdk';
import {
tryNativeToUint8Array,
generateSignAndSubmitEntryFunction,
hexToUint8Array,
parseSequenceFromLogAptos,
getEmitterAddressSolana,
getEmitterAddressEth,
} from '@certusone/wormhole-sdk';
import fetch from 'node-fetch';
import ethers from 'ethers';
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString())
const program = new Command();
const aptosModuleAddress = "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b"; // Unlike EVM, this is hardcoded
const solanaProgramAddress = ""; // Unlike EVM, this is hardcoded
program
.name('xMessenger')
@ -27,24 +35,164 @@ program
switch(config.networks[network].type){
case "aptos":
let aptosMessenger = new AptosMessenger(config.networks[network].rpc, aptosModuleAddress);
let aptosMessenger = new AptosMessenger(config.networks[network].rpc, config.networks[network].deployedAddress);
const txnHash = await aptosMessenger.deploy(config.networks[network].privateKey);
console.log(`Deployed @ ${txnHash}`);
break;
case "solana":
console.log("Not supported yet");
break;
case "evm":
let evmMesenger = new EVMMessenger(config.networks[network].rpc, config.networks[network].bridgeAddress);
await evmMesenger.deploy(network, config.networks[network].privateKey);
break;
}
console.log(`Deploy finished!`);
});
// register network
program
.command('register-network')
.argument("<network>", "The source network")
.argument("<network>", "The foreign network to be registered")
.action(async (src, target) => {
if(!config.networks[src]){
console.error(`ERROR: ${src} not found in xdapp.config.json`);
return;
}
switch(config.networks[src].type){
case "aptos":
let aptosMessenger = new AptosMessenger(config.networks[src].rpc, config.networks[src].deployedAddress);
let targetAddress = tryNativeToUint8Array(config.networks[target].deployedAddress, config.networks[target].wormholeChainId)
const payload = aptosMessenger.registerNetwork(config.networks[src].bridgeAddress, config.networks[target].wormholeChainId, targetAddress);
const tx = await generateSignAndSubmitEntryFunction(
aptosMessenger.client,
new AptosAccount(hexToUint8Array(config.networks[src].privateKey)),
payload
);
const res = await aptosMessenger.client.waitForTransactionWithResult(tx.hash);
console.log(`Registered @ ${tx.hash}`);
break;
case "solana":
console.log("Not supported yet");
break;
case "evm":
console.log("Not supported yet")
break;
}
});
// send msg
program
.command('send-msg')
.argument("<network>", "The source network")
.argument("<msg>", "The message to be emitted")
.action(async (src, message) => {
if(!config.networks[src]){
console.error(`ERROR: ${src} not found in xdapp.config.json`);
return;
}
switch(config.networks[src].type){
case "aptos":
let aptosMessenger = new AptosMessenger(config.networks[src].rpc, config.networks[src].deployedAddress);
const payload = aptosMessenger.sendMessage(message);
const tx = (await generateSignAndSubmitEntryFunction(
aptosMessenger.client,
new AptosAccount(hexToUint8Array(config.networks[src].privateKey)),
payload
) as Types.UserTransaction);
const res = await aptosMessenger.client.waitForTransactionWithResult(tx.hash);
console.log(`Sent Messagage @ ${tx.hash}`);
let seq = parseSequenceFromLogAptos(config.networks[src].bridgeAddress, tx);
let emitter = await aptosMessenger.getEmitterAddress();
console.log(await getEmitterAddressSolana(config.networks['solana'].deployedAddress));
console.log(
"Searching for: ",
`${config.wormhole.restAddress}/v1/signed_vaa/${config.networks[src].wormholeChainId}/${emitter}/${seq}`
);
let vaaBytes = undefined;
while (!vaaBytes) {
vaaBytes = (await (
await fetch(
`${config.wormhole.restAddress}/v1/signed_vaa/${config.networks[src].wormholeChainId}/${emitter}/${seq}`
)
).json()).vaaBytes;
await new Promise((r) => setTimeout(r, 1000)); // Poll guardiand every sec for message
}
console.log("VAA: ", vaaBytes);
break;
case "solana":
console.log("Not supported yet");
break;
case "evm":
console.log("Not supported yet");
break;
}
console.log(`Deploy finished!`);
});
// register network
// send msg
// submit msg
// get msg
//AQAAAAABALtgDWjywCNRnmIfNU+IkWYUqbzl730v55syJzPmlCwvf3lz9V6Ax9p7ZhMX7kMufwqxNBCkDHlatY5mh5w0cuUAY5+tlgAAAAAAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAaGVsbG8=
program
.command('submit-vaa')
.argument("<network>", "The source network")
.argument("<vaa>", "The message to be emitted")
.action(async (src, vaa) => {
if(!config.networks[src]){
console.error(`ERROR: ${src} not found in xdapp.config.json`);
return;
}
program.parse();
switch(config.networks[src].type){
case "aptos":
let aptosMessenger = new AptosMessenger(config.networks[src].rpc, config.networks[src].deployedAddress);
const payload = aptosMessenger.submitMessage(vaa);
const tx = (await generateSignAndSubmitEntryFunction(
aptosMessenger.client,
new AptosAccount(hexToUint8Array(config.networks[src].privateKey)),
payload
) as Types.UserTransaction);
const res = await aptosMessenger.client.waitForTransactionWithResult(tx.hash);
console.log(`Submitted @ ${tx.hash}`);
break;
case "solana":
console.log("Not supported yet");
break;
case "evm":
console.log("Not supported yet");
break;
}
});
// get msg
program
.command('get-msg')
.argument('<network>', 'The network to fetch the message from')
.action(async (network) => {
if(!config.networks[network]){
console.error(`ERROR: ${network} not found in xdapp.config.json`);
return;
}
console.log(`Fetching current message on ${network}...`);
switch(config.networks[network].type){
case "aptos":
let aptosMessenger = new AptosMessenger(config.networks[network].rpc, config.networks[network].deployedAddress);
console.log(await aptosMessenger.getMessage());
break;
case "solana":
console.log("Not supported yet");
break;
case "evm":
console.log("Not supported yet");
break;
}
});
program.parse();

View File

@ -8,7 +8,10 @@
"dependencies": {
"@certusone/wormhole-sdk": "^0.9.4",
"@types/node": "^18.11.10",
"@types/node-fetch": "^2.6.2",
"aptos": "^1.4.0",
"commander": "^9.4.1"
"commander": "^9.4.1",
"ethers": "^5.7.2",
"node-fetch": "2"
}
}

View File

@ -2,7 +2,11 @@ import {
generateSignAndSubmitEntryFunction,
hexToUint8Array,
} from "@certusone/wormhole-sdk";
import { AptosAccount, AptosClient } from "aptos";
import {
AptosAccount,
AptosClient,
Types,
} from "aptos";
import { promisify } from "util";
const exec = promisify(require("child_process").exec);
@ -16,6 +20,7 @@ export class AptosMessenger {
max_gas_amount: "30000",
};
coreMessages = "";
bridgeAddress = "";
constructor(nodeUrl: string, coreMessages: string) {
this.client = new AptosClient(nodeUrl);
@ -31,22 +36,92 @@ export class AptosMessenger {
console.log("Initalizing Aptos Messenger module...");
// Initialize the module to register its emitter capability
const APTOS_NODE_URL = "http://localhost:8080/";
const client = new AptosClient(APTOS_NODE_URL);
const sender = new AptosAccount(hexToUint8Array(privateKey));
const payload = {
const payload:Types.EntryFunctionPayload = {
function: `${this.coreMessages}::messenger::init_messenger`,
type_arguments: [],
arguments: [],
arguments: [
sender.address()
],
};
const tx = await generateSignAndSubmitEntryFunction(
client,
this.client,
sender,
payload
);
const res = await client.waitForTransactionWithResult(tx.hash);
console.log(JSON.stringify(res, null, 2));
const res = await this.client.waitForTransactionWithResult(tx.hash);
//console.log(JSON.stringify(res, null, 2));
return res.hash;
}
public async getEmitterAddress(): Promise<string> {
let resourceAccAddress = AptosAccount.getResourceAccountAddress(
`${this.coreMessages}`,
Buffer.from("messenger_state")
);
let state = await this.client.getAccountResource(
resourceAccAddress,
`${this.coreMessages}::messenger::State`
);
console.log(state);
return Number((state.data as any).emitter_cap.emitter).toString(16).padStart(64, '0');
}
// Regster Network
public registerNetwork(bridgeAddress:string, chainID: number, foreignAddress: Buffer | Uint8Array):Types.EntryFunctionPayload {
const payload = {
function: `${this.coreMessages}::messenger::register_emitter`,
type_arguments: [],
arguments: [
chainID,
Buffer.from(foreignAddress)
],
}
return payload;
}
// Emit Message
public sendMessage(message:String) {
const payload = {
function: `${this.coreMessages}::messenger::send_message`,
type_arguments: [],
arguments: [
Buffer.from(message)
],
}
return payload;
}
// Submit Message
public submitMessage(message: string) {
const payload = {
function: `${this.coreMessages}::messenger::receive_message`,
type_arguments: [],
arguments: [
Buffer.from(message, 'base64')
],
}
return payload;
}
// Get Message
public async getMessage() {
let resourceAccAddress = AptosAccount.getResourceAccountAddress(
`${this.coreMessages}`,
Buffer.from("messenger_state")
);
let state = await this.client.getAccountResource(
resourceAccAddress,
`${this.coreMessages}::messenger::State`
);
return (state.data as any).current_message;
}
}

View File

@ -1 +1,3 @@
export * from './aptos';
export * from './evm';
export * from './solana';

View File

@ -1,35 +1,38 @@
{
"networks": {
"evm0": {
"type": "evm",
"wormholeChainId": 2,
"rpc": "http://localhost:8545",
"privateKey": "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
"bridgeAddress": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
},
"evm1": {
"type": "evm",
"wormholeChainId": 4,
"rpc": "http://localhost:8546",
"privateKey": "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
"bridgeAddress": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
},
"sol0": {
"type": "solana",
"wormholeChainId": 1,
"rpc": "http://localhost:8899",
"privateKey": "J2D4pwDred8P9ioyPEZVLPht885AeYpifsFGUyuzVmiKQosAvmZP4EegaKFrSprBC5vVP1xTvu61vYDWsxBNsYx",
"bridgeAddress": "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
},
"aptos": {
"type": "aptos",
"wormholeChainId": 22,
"rpc": "http://localhost:8080",
"privateKey": "537c1f91e56891445b491068f519b705f8c0f1a1e66111816dd5d4aa85b8113d",
"bridgeAddress": "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017"
}
"networks": {
"eth": {
"type": "evm",
"wormholeChainId": 2,
"rpc": "http://localhost:8545",
"privateKey": "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
"bridgeAddress": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550",
"deployedAddress": "0xeea2Fc1D255Fd28aA15c6c2324Ad40B03267f9c5"
},
"wormhole": {
"restAddress": "http://localhost:7071"
"bsc": {
"type": "evm",
"wormholeChainId": 4,
"rpc": "http://localhost:8546",
"privateKey": "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
"bridgeAddress": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
},
"solana": {
"type": "solana",
"wormholeChainId": 1,
"rpc": "http://localhost:8899",
"privateKey": "J2D4pwDred8P9ioyPEZVLPht885AeYpifsFGUyuzVmiKQosAvmZP4EegaKFrSprBC5vVP1xTvu61vYDWsxBNsYx",
"bridgeAddress": "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o",
"deployedAddress": "24FoTeX7BKbhTh3UF3feWusoAVKDPWZneiEqhXLVzZPL"
},
"aptos": {
"type": "aptos",
"wormholeChainId": 22,
"rpc": "http://localhost:8080",
"privateKey": "537c1f91e56891445b491068f519b705f8c0f1a1e66111816dd5d4aa85b8113d",
"bridgeAddress": "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017",
"deployedAddress": "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b"
}
},
"wormhole": {
"restAddress": "http://localhost:7071"
}
}

View File

@ -937,6 +937,14 @@
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
"@types/node-fetch@^2.6.2":
version "2.6.2"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da"
integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*", "@types/node@>=13.7.0", "@types/node@^18.0.3", "@types/node@^18.11.10":
version "18.11.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.10.tgz#4c64759f3c2343b7e6c4b9caf761c7a3a05cee34"
@ -1655,7 +1663,7 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1:
ethjs-util "0.1.6"
rlp "^2.2.3"
ethers@5.7.2, ethers@^5.6.4:
ethers@5.7.2, ethers@^5.6.4, ethers@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
@ -1741,6 +1749,15 @@ form-data@4.0.0, form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"

View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,18 @@
[package]
name = 'messenger'
version = '1.0.0'
[dependencies.AptosFramework]
git = 'https://github.com/aptos-labs/aptos-core.git'
rev = 'main'
subdir = 'aptos-move/framework/aptos-framework'
[dependencies.Wormhole]
git = 'https://github.com/wormhole-foundation/wormhole'
rev = 'main'
subdir = 'aptos/wormhole'
[addresses]
wormhole = "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017"
deployer = "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b"
core_messages = "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b"

View File

@ -0,0 +1,121 @@
/// A simple contracts that demonstrates how to send messages with wormhole.
module core_messages::messenger {
use wormhole::wormhole;
use aptos_framework::coin;
use std::table::{Self, Table};
use wormhole::u16::U16;
use wormhole::external_address::ExternalAddress;
use aptos_framework::signer;
use aptos_framework::account;
use std::string::{Self, String};
const E_ONLY_CORE_MESSAGES_CAN_INIT:u64 = 0;
const E_ONLY_ADMIN:u64 = 1;
const E_VAA_ALREADY_CONSUMED:u64 = 2;
const E_VAA_EMMITTER_NOT_REGISTERED:u64 = 3;
struct MessengerAdminCapability has key {
// The ability to make changes to the State Resource Account
resource_signer_cap: account::SignerCapability,
}
struct State has key {
// Admin Address
admin: address,
// The ability to send wormhole messages
emitter_cap: wormhole::emitter::EmitterCapability,
// Mapping of bridge contracts on other chains
registered_emitters: Table<U16, ExternalAddress>,
// Current Message
current_message: String,
// Consumed VAAs
consumed_vaas: Table<vector<u8>, bool>,
}
public entry fun init_messenger(core_messages: &signer, admin: address) {
// Requires the private key of Core Messages to have signed this message
// This is to make sure the state get stored under core messages resource account
assert!(signer::address_of(core_messages) == @core_messages, E_ONLY_CORE_MESSAGES_CAN_INIT);
let (resource_signer, signer_cap) = account::create_resource_account(core_messages, b"messenger_state");
// Register ourselves as a wormhole emitter. This gives back an
// `EmitterCapability` which will be required to send messages through
// wormhole.
let emitter_cap = wormhole::register_emitter();
move_to(&resource_signer, State {
admin,
emitter_cap,
registered_emitters: table::new(),
current_message: string::utf8(b"uninitalized"),
consumed_vaas: table::new()
});
move_to(core_messages, MessengerAdminCapability { resource_signer_cap: signer_cap } );
}
public entry fun send_message(user: &signer, payload: vector<u8>) acquires State {
// Fetch the Resource Account that has state
let state_resource_account = account::create_resource_address(&@core_messages, b"messenger_state");
// Retrieve emitter capability from the state
let emitter_cap = &mut borrow_global_mut<State>(state_resource_account).emitter_cap;
// Set nonce to 0 (this field is not interesting for regular messages,
// only batch VAAs)
let nonce: u64 = 0;
let message_fee = wormhole::state::get_message_fee();
let fee_coins = coin::withdraw(user, message_fee);
let _sequence = wormhole::publish_message(
emitter_cap,
nonce,
payload,
fee_coins
);
}
public entry fun register_emitter(user:&signer, chainID: u64, external_address:vector<u8>) acquires State {
let chain_id = wormhole::u16::from_u64(chainID);
let foreign_address = wormhole::external_address::from_bytes(external_address);
// Fetch the Resource Account that has state
let state_resource_account = account::create_resource_address(&@core_messages, b"messenger_state");
// Retrieve emitter capability from the state
let admin_address = &borrow_global<State>(state_resource_account).admin;
assert!(*admin_address == signer::address_of(user), E_ONLY_ADMIN);
//Get Registered Emitters
let state = borrow_global_mut<State>(state_resource_account);
table::upsert(&mut state.registered_emitters, chain_id, foreign_address);
}
public entry fun receive_message(vaa_bytes: vector<u8>) acquires State {
// Verify the wormhole message
let vaa = wormhole::vaa::parse_and_verify(vaa_bytes);
// Fetch the Resource Account that has state
let state_resource_account = account::create_resource_address(&@core_messages, b"messenger_state");
let state = borrow_global_mut<State>(state_resource_account);
// Check the VAA has not been consumed already
assert!(!table::contains(&state.consumed_vaas, wormhole::vaa::get_hash(&vaa)), E_VAA_ALREADY_CONSUMED);
// Check the emitter chain is registered
assert!(*table::borrow(&state.registered_emitters, wormhole::vaa::get_emitter_chain(&vaa)) == wormhole::vaa::get_emitter_address(&vaa), E_VAA_EMMITTER_NOT_REGISTERED);
// Set the current message to the vaa message
state.current_message = string::utf8(wormhole::vaa::get_payload(&vaa));
// Store the hash of the vaa in the consumed VAAs
table::upsert(&mut state.consumed_vaas, wormhole::vaa::get_hash(&vaa), true);
wormhole::vaa::destroy(vaa);
}
}

View File

@ -2,6 +2,7 @@
"_format": "ethers-rs-sol-cache-3",
"paths": {
"artifacts": "out",
"build_infos": "out/build-info",
"sources": "src",
"tests": "test",
"scripts": "script",
@ -11,8 +12,8 @@
},
"files": {
"src/Messenger.sol": {
"lastModificationDate": 1659683988145,
"contentHash": "b21dd2ab010d42c4abc588eed32658bd",
"lastModificationDate": 1671431118697,
"contentHash": "17b03174437201be7b7f56e2bd0d92e4",
"sourceName": "src/Messenger.sol",
"solcConfig": {
"settings": {
@ -32,7 +33,8 @@
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers"
"evm.methodIdentifiers",
"metadata"
]
}
},
@ -52,7 +54,7 @@
}
},
"src/Wormhole/IWormhole.sol": {
"lastModificationDate": 1659683988146,
"lastModificationDate": 1665515840999,
"contentHash": "7c930d4b68538e731af28a8d1979f44e",
"sourceName": "src/Wormhole/IWormhole.sol",
"solcConfig": {
@ -73,7 +75,8 @@
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers"
"evm.methodIdentifiers",
"metadata"
]
}
},
@ -92,7 +95,7 @@
}
},
"src/Wormhole/Structs.sol": {
"lastModificationDate": 1659683988147,
"lastModificationDate": 1665515841000,
"contentHash": "4e824cc204959f4fcab06c4e806448c4",
"sourceName": "src/Wormhole/Structs.sol",
"solcConfig": {
@ -113,7 +116,8 @@
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers"
"evm.methodIdentifiers",
"metadata"
]
}
},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,19 +11,64 @@
"linkReferences": {}
},
"methodIdentifiers": {},
"rawMetadata": "{\"compiler\":{\"version\":\"0.8.10+commit.fc410830\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/Wormhole/Structs.sol\":\"Structs\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"src/Wormhole/Structs.sol\":{\"keccak256\":\"0xf6067f3a7ac8ccd239a50aa2ef5620ffe31bc86a3757571e13e57a3a0f476b37\",\"license\":\"Apache 2\",\"urls\":[\"bzz-raw://813db91e92feacdf21da3986e1a59d38befc34103cd09e61d30471c228e3a6f2\",\"dweb:/ipfs/QmWeRrqvbite8YCHu97Lsta1Y3TR9WzQ9k2spaF6S3VQZZ\"]}},\"version\":1}",
"metadata": {
"compiler": {
"version": "0.8.10+commit.fc410830"
},
"language": "Solidity",
"output": {
"abi": [],
"devdoc": {
"kind": "dev",
"methods": {},
"version": 1
},
"userdoc": {
"kind": "user",
"methods": {},
"version": 1
}
},
"settings": {
"remappings": [],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"compilationTarget": {
"src/Wormhole/Structs.sol": "Structs"
},
"libraries": {}
},
"sources": {
"src/Wormhole/Structs.sol": {
"keccak256": "0xf6067f3a7ac8ccd239a50aa2ef5620ffe31bc86a3757571e13e57a3a0f476b37",
"urls": [
"bzz-raw://813db91e92feacdf21da3986e1a59d38befc34103cd09e61d30471c228e3a6f2",
"dweb:/ipfs/QmWeRrqvbite8YCHu97Lsta1Y3TR9WzQ9k2spaF6S3VQZZ"
],
"license": "Apache 2"
}
},
"version": 1
},
"ast": {
"absolutePath": "src/Wormhole/Structs.sol",
"id": 331,
"id": 333,
"exportedSymbols": {
"Structs": [
330
332
]
},
"nodeType": "SourceUnit",
"src": "63:551:2",
"nodes": [
{
"id": 282,
"id": 284,
"nodeType": "PragmaDirective",
"src": "63:23:2",
"literals": [
@ -34,24 +79,24 @@
]
},
{
"id": 330,
"id": 332,
"nodeType": "ContractDefinition",
"src": "88:526:2",
"nodes": [
{
"id": 289,
"id": 291,
"nodeType": "StructDefinition",
"src": "109:96:2",
"canonicalName": "Structs.Provider",
"members": [
{
"constant": false,
"id": 284,
"id": 286,
"mutability": "mutable",
"name": "chainId",
"nameLocation": "136:7:2",
"nodeType": "VariableDeclaration",
"scope": 289,
"scope": 291,
"src": "129:14:2",
"stateVariable": false,
"storageLocation": "default",
@ -60,7 +105,7 @@
"typeString": "uint16"
},
"typeName": {
"id": 283,
"id": 285,
"name": "uint16",
"nodeType": "ElementaryTypeName",
"src": "129:6:2",
@ -73,12 +118,12 @@
},
{
"constant": false,
"id": 286,
"id": 288,
"mutability": "mutable",
"name": "governanceChainId",
"nameLocation": "154:17:2",
"nodeType": "VariableDeclaration",
"scope": 289,
"scope": 291,
"src": "147:24:2",
"stateVariable": false,
"storageLocation": "default",
@ -87,7 +132,7 @@
"typeString": "uint16"
},
"typeName": {
"id": 285,
"id": 287,
"name": "uint16",
"nodeType": "ElementaryTypeName",
"src": "147:6:2",
@ -100,12 +145,12 @@
},
{
"constant": false,
"id": 288,
"id": 290,
"mutability": "mutable",
"name": "governanceContract",
"nameLocation": "183:18:2",
"nodeType": "VariableDeclaration",
"scope": 289,
"scope": 291,
"src": "175:26:2",
"stateVariable": false,
"storageLocation": "default",
@ -114,7 +159,7 @@
"typeString": "bytes32"
},
"typeName": {
"id": 287,
"id": 289,
"name": "bytes32",
"nodeType": "ElementaryTypeName",
"src": "175:7:2",
@ -128,23 +173,23 @@
],
"name": "Provider",
"nameLocation": "116:8:2",
"scope": 330,
"scope": 332,
"visibility": "public"
},
{
"id": 295,
"id": 297,
"nodeType": "StructDefinition",
"src": "208:66:2",
"canonicalName": "Structs.GuardianSet",
"members": [
{
"constant": false,
"id": 292,
"id": 294,
"mutability": "mutable",
"name": "keys",
"nameLocation": "241:4:2",
"nodeType": "VariableDeclaration",
"scope": 295,
"scope": 297,
"src": "231:14:2",
"stateVariable": false,
"storageLocation": "default",
@ -154,7 +199,7 @@
},
"typeName": {
"baseType": {
"id": 290,
"id": 292,
"name": "address",
"nodeType": "ElementaryTypeName",
"src": "231:7:2",
@ -164,7 +209,7 @@
"typeString": "address"
}
},
"id": 291,
"id": 293,
"nodeType": "ArrayTypeName",
"src": "231:9:2",
"typeDescriptions": {
@ -176,12 +221,12 @@
},
{
"constant": false,
"id": 294,
"id": 296,
"mutability": "mutable",
"name": "expirationTime",
"nameLocation": "256:14:2",
"nodeType": "VariableDeclaration",
"scope": 295,
"scope": 297,
"src": "249:21:2",
"stateVariable": false,
"storageLocation": "default",
@ -190,7 +235,7 @@
"typeString": "uint32"
},
"typeName": {
"id": 293,
"id": 295,
"name": "uint32",
"nodeType": "ElementaryTypeName",
"src": "249:6:2",
@ -204,23 +249,23 @@
],
"name": "GuardianSet",
"nameLocation": "215:11:2",
"scope": 330,
"scope": 332,
"visibility": "public"
},
{
"id": 304,
"id": 306,
"nodeType": "StructDefinition",
"src": "277:81:2",
"canonicalName": "Structs.Signature",
"members": [
{
"constant": false,
"id": 297,
"id": 299,
"mutability": "mutable",
"name": "r",
"nameLocation": "306:1:2",
"nodeType": "VariableDeclaration",
"scope": 304,
"scope": 306,
"src": "298:9:2",
"stateVariable": false,
"storageLocation": "default",
@ -229,7 +274,7 @@
"typeString": "bytes32"
},
"typeName": {
"id": 296,
"id": 298,
"name": "bytes32",
"nodeType": "ElementaryTypeName",
"src": "298:7:2",
@ -242,12 +287,12 @@
},
{
"constant": false,
"id": 299,
"id": 301,
"mutability": "mutable",
"name": "s",
"nameLocation": "319:1:2",
"nodeType": "VariableDeclaration",
"scope": 304,
"scope": 306,
"src": "311:9:2",
"stateVariable": false,
"storageLocation": "default",
@ -256,7 +301,7 @@
"typeString": "bytes32"
},
"typeName": {
"id": 298,
"id": 300,
"name": "bytes32",
"nodeType": "ElementaryTypeName",
"src": "311:7:2",
@ -269,12 +314,12 @@
},
{
"constant": false,
"id": 301,
"id": 303,
"mutability": "mutable",
"name": "v",
"nameLocation": "330:1:2",
"nodeType": "VariableDeclaration",
"scope": 304,
"scope": 306,
"src": "324:7:2",
"stateVariable": false,
"storageLocation": "default",
@ -283,7 +328,7 @@
"typeString": "uint8"
},
"typeName": {
"id": 300,
"id": 302,
"name": "uint8",
"nodeType": "ElementaryTypeName",
"src": "324:5:2",
@ -296,12 +341,12 @@
},
{
"constant": false,
"id": 303,
"id": 305,
"mutability": "mutable",
"name": "guardianIndex",
"nameLocation": "341:13:2",
"nodeType": "VariableDeclaration",
"scope": 304,
"scope": 306,
"src": "335:19:2",
"stateVariable": false,
"storageLocation": "default",
@ -310,7 +355,7 @@
"typeString": "uint8"
},
"typeName": {
"id": 302,
"id": 304,
"name": "uint8",
"nodeType": "ElementaryTypeName",
"src": "335:5:2",
@ -324,23 +369,23 @@
],
"name": "Signature",
"nameLocation": "284:9:2",
"scope": 330,
"scope": 332,
"visibility": "public"
},
{
"id": 329,
"id": 331,
"nodeType": "StructDefinition",
"src": "361:251:2",
"canonicalName": "Structs.VM",
"members": [
{
"constant": false,
"id": 306,
"id": 308,
"mutability": "mutable",
"name": "version",
"nameLocation": "381:7:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "375:13:2",
"stateVariable": false,
"storageLocation": "default",
@ -349,7 +394,7 @@
"typeString": "uint8"
},
"typeName": {
"id": 305,
"id": 307,
"name": "uint8",
"nodeType": "ElementaryTypeName",
"src": "375:5:2",
@ -362,12 +407,12 @@
},
{
"constant": false,
"id": 308,
"id": 310,
"mutability": "mutable",
"name": "timestamp",
"nameLocation": "399:9:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "392:16:2",
"stateVariable": false,
"storageLocation": "default",
@ -376,7 +421,7 @@
"typeString": "uint32"
},
"typeName": {
"id": 307,
"id": 309,
"name": "uint32",
"nodeType": "ElementaryTypeName",
"src": "392:6:2",
@ -389,12 +434,12 @@
},
{
"constant": false,
"id": 310,
"id": 312,
"mutability": "mutable",
"name": "nonce",
"nameLocation": "419:5:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "412:12:2",
"stateVariable": false,
"storageLocation": "default",
@ -403,7 +448,7 @@
"typeString": "uint32"
},
"typeName": {
"id": 309,
"id": 311,
"name": "uint32",
"nodeType": "ElementaryTypeName",
"src": "412:6:2",
@ -416,12 +461,12 @@
},
{
"constant": false,
"id": 312,
"id": 314,
"mutability": "mutable",
"name": "emitterChainId",
"nameLocation": "435:14:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "428:21:2",
"stateVariable": false,
"storageLocation": "default",
@ -430,7 +475,7 @@
"typeString": "uint16"
},
"typeName": {
"id": 311,
"id": 313,
"name": "uint16",
"nodeType": "ElementaryTypeName",
"src": "428:6:2",
@ -443,12 +488,12 @@
},
{
"constant": false,
"id": 314,
"id": 316,
"mutability": "mutable",
"name": "emitterAddress",
"nameLocation": "461:14:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "453:22:2",
"stateVariable": false,
"storageLocation": "default",
@ -457,7 +502,7 @@
"typeString": "bytes32"
},
"typeName": {
"id": 313,
"id": 315,
"name": "bytes32",
"nodeType": "ElementaryTypeName",
"src": "453:7:2",
@ -470,12 +515,12 @@
},
{
"constant": false,
"id": 316,
"id": 318,
"mutability": "mutable",
"name": "sequence",
"nameLocation": "486:8:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "479:15:2",
"stateVariable": false,
"storageLocation": "default",
@ -484,7 +529,7 @@
"typeString": "uint64"
},
"typeName": {
"id": 315,
"id": 317,
"name": "uint64",
"nodeType": "ElementaryTypeName",
"src": "479:6:2",
@ -497,12 +542,12 @@
},
{
"constant": false,
"id": 318,
"id": 320,
"mutability": "mutable",
"name": "consistencyLevel",
"nameLocation": "504:16:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "498:22:2",
"stateVariable": false,
"storageLocation": "default",
@ -511,7 +556,7 @@
"typeString": "uint8"
},
"typeName": {
"id": 317,
"id": 319,
"name": "uint8",
"nodeType": "ElementaryTypeName",
"src": "498:5:2",
@ -524,12 +569,12 @@
},
{
"constant": false,
"id": 320,
"id": 322,
"mutability": "mutable",
"name": "payload",
"nameLocation": "530:7:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "524:13:2",
"stateVariable": false,
"storageLocation": "default",
@ -538,7 +583,7 @@
"typeString": "bytes"
},
"typeName": {
"id": 319,
"id": 321,
"name": "bytes",
"nodeType": "ElementaryTypeName",
"src": "524:5:2",
@ -551,12 +596,12 @@
},
{
"constant": false,
"id": 322,
"id": 324,
"mutability": "mutable",
"name": "guardianSetIndex",
"nameLocation": "549:16:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "542:23:2",
"stateVariable": false,
"storageLocation": "default",
@ -565,7 +610,7 @@
"typeString": "uint32"
},
"typeName": {
"id": 321,
"id": 323,
"name": "uint32",
"nodeType": "ElementaryTypeName",
"src": "542:6:2",
@ -578,42 +623,42 @@
},
{
"constant": false,
"id": 326,
"id": 328,
"mutability": "mutable",
"name": "signatures",
"nameLocation": "581:10:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "569:22:2",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {
"typeIdentifier": "t_array$_t_struct$_Signature_$304_storage_$dyn_storage_ptr",
"typeIdentifier": "t_array$_t_struct$_Signature_$306_storage_$dyn_storage_ptr",
"typeString": "struct Structs.Signature[]"
},
"typeName": {
"baseType": {
"id": 324,
"id": 326,
"nodeType": "UserDefinedTypeName",
"pathNode": {
"id": 323,
"id": 325,
"name": "Signature",
"nodeType": "IdentifierPath",
"referencedDeclaration": 304,
"referencedDeclaration": 306,
"src": "569:9:2"
},
"referencedDeclaration": 304,
"referencedDeclaration": 306,
"src": "569:9:2",
"typeDescriptions": {
"typeIdentifier": "t_struct$_Signature_$304_storage_ptr",
"typeIdentifier": "t_struct$_Signature_$306_storage_ptr",
"typeString": "struct Structs.Signature"
}
},
"id": 325,
"id": 327,
"nodeType": "ArrayTypeName",
"src": "569:11:2",
"typeDescriptions": {
"typeIdentifier": "t_array$_t_struct$_Signature_$304_storage_$dyn_storage_ptr",
"typeIdentifier": "t_array$_t_struct$_Signature_$306_storage_$dyn_storage_ptr",
"typeString": "struct Structs.Signature[]"
}
},
@ -621,12 +666,12 @@
},
{
"constant": false,
"id": 328,
"id": 330,
"mutability": "mutable",
"name": "hash",
"nameLocation": "604:4:2",
"nodeType": "VariableDeclaration",
"scope": 329,
"scope": 331,
"src": "596:12:2",
"stateVariable": false,
"storageLocation": "default",
@ -635,7 +680,7 @@
"typeString": "bytes32"
},
"typeName": {
"id": 327,
"id": 329,
"name": "bytes32",
"nodeType": "ElementaryTypeName",
"src": "596:7:2",
@ -649,7 +694,7 @@
],
"name": "VM",
"nameLocation": "368:2:2",
"scope": 330,
"scope": 332,
"visibility": "public"
}
],
@ -660,11 +705,11 @@
"contractKind": "interface",
"fullyImplemented": true,
"linearizedBaseContracts": [
330
332
],
"name": "Structs",
"nameLocation": "98:7:2",
"scope": 331,
"scope": 333,
"usedErrors": []
}
],

View File

@ -5,7 +5,7 @@ import "./Wormhole/IWormhole.sol";
contract Messenger {
string private current_msg;
address private wormhole_core_bridge_address;
IWormhole core_bridge;
uint32 nonce = 0;
mapping(uint16 => bytes32) _applicationContracts;
address owner;
@ -13,10 +13,9 @@ contract Messenger {
constructor(address _core_bridge_address){
owner = msg.sender;
wormhole_core_bridge_address = _core_bridge_address;
core_bridge = IWormhole(_core_bridge_address);
}
IWormhole core_bridge = IWormhole(wormhole_core_bridge_address);
function sendMsg(bytes memory str) public returns (uint64 sequence) {
sequence = core_bridge.publishMessage(nonce, str, 1);

View File

@ -0,0 +1,242 @@
import {
generateSignAndSubmitEntryFunction,
getEmitterAddressEth,
getEmitterAddressSolana,
hexToUint8Array,
parseSequenceFromLogAptos,
} from "@certusone/wormhole-sdk";
import {
AptosAccount,
AptosClient,
Types,
} from "aptos";
import { promisify } from "util";
const exec = promisify(require("child_process").exec);
import * as fs from 'fs';
import fetch from "node-fetch";
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString());
const coreMessages = "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b";
export async function deploy(src:string){
const privateKey = config.networks[src].privateKey;
const rpc = config.networks[src].rpc;
let cmd = `cd chains/aptos && aptos move compile --save-metadata && aptos move publish --private-key ${privateKey} --url ${rpc} --assume-yes`;
const { stdout, stderr } = await exec(cmd);
//console.log(stdout);
console.log("Initalizing Aptos Messenger module...");
// Initialize the module to register its emitter capability
const sender = new AptosAccount(hexToUint8Array(privateKey));
const payload:Types.EntryFunctionPayload = {
function: `${coreMessages}::messenger::init_messenger`,
type_arguments: [],
arguments: [
sender.address()
],
};
const client = new AptosClient(rpc);
const tx = await generateSignAndSubmitEntryFunction(
client,
sender,
payload
);
const res = await client.waitForTransactionWithResult(tx.hash);
//console.log(JSON.stringify(res, null, 2));
fs.writeFileSync(`./deployinfo/${src}.deploy.json`, JSON.stringify({
address: coreMessages,
vaas: []
}, null, 4))
return res.hash;
}
export async function registerApp(src:string, target:string){
const srcNetwork = config.networks[src];
const targetNetwork = config.networks[target];
let srcDeploymentInfo;
let targetDeploymentInfo;
let targetEmitter;
try{
srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
} catch (e){
throw new Error(`${src} is not deployed yet`);
}
try{
targetDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${target}.deploy.json`).toString());
} catch (e){
throw new Error(`${target} is not deployed yet`);
}
switch (targetNetwork['type']){
case 'evm':
targetEmitter = getEmitterAddressEth(targetDeploymentInfo['address']);
break;
case 'solana':
targetEmitter = await getEmitterAddressSolana(targetDeploymentInfo['address']);
break;
case 'aptos':
targetEmitter = await getEmitterAddress(config.networks[src].rpc, targetDeploymentInfo['address']);
break;
}
const payload = {
function: `${coreMessages}::messenger::register_emitter`,
type_arguments: [],
arguments: [
targetNetwork.wormholeChainId,
Buffer.from(targetEmitter, 'hex')
],
}
const client = new AptosClient(srcNetwork.rpc);
const tx = await generateSignAndSubmitEntryFunction(
client,
new AptosAccount(hexToUint8Array(config.networks[src].privateKey)),
payload
);
const res = await client.waitForTransactionWithResult(tx.hash);
console.log(`Registered @ ${tx.hash}`);
}
export async function sendMsg(src:string, msg:string){
const client = new AptosClient(config.networks[src].rpc);
const payload = {
function: `${coreMessages}::messenger::send_message`,
type_arguments: [],
arguments: [
Buffer.from(msg)
],
}
const tx = (await generateSignAndSubmitEntryFunction(
client,
new AptosAccount(hexToUint8Array(config.networks[src].privateKey)),
payload
) as Types.UserTransaction);
const res = await client.waitForTransactionWithResult(tx.hash);
console.log(`Sent Messagage @ ${tx.hash}`);
let seq = parseSequenceFromLogAptos(config.networks[src].bridgeAddress, tx);
let emitter = await getEmitterAddress(config.networks[src].rpc, coreMessages);
console.log(
"Searching for: ",
`${config.wormhole.restAddress}/v1/signed_vaa/${config.networks[src].wormholeChainId}/${emitter}/${seq}`
);
let vaaBytes = undefined;
while (!vaaBytes) {
vaaBytes = (await (
await fetch(
`${config.wormhole.restAddress}/v1/signed_vaa/${config.networks[src].wormholeChainId}/${emitter}/${seq}`
)
).json()).vaaBytes;
await new Promise((r) => setTimeout(r, 1000)); // Poll guardiand every sec for message
}
console.log("VAA: ", vaaBytes);
let srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
if(!srcDeploymentInfo['vaas']){
srcDeploymentInfo['vaas'] = [vaaBytes]
} else {
srcDeploymentInfo['vaas'].push(vaaBytes)
}
fs.writeFileSync(
`./deployinfo/${src}.deploy.json`,
JSON.stringify(srcDeploymentInfo, null, 4)
);
return vaaBytes;
}
export async function submitVaa(src:string, target:string, idx: string){
const srcNetwork = config.networks[src];
let srcDeploymentInfo;
let targetDeploymentInfo;
try{
srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
} catch (e){
throw new Error(`${src} is not deployed yet`);
}
try{
targetDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${target}.deploy.json`).toString());
} catch (e){
throw new Error(`${target} is not deployed yet`);
}
const vaa = isNaN(parseInt(idx))
? targetDeploymentInfo.vaas.pop()
: targetDeploymentInfo.vaas[parseInt(idx)];
const payload = {
function: `${coreMessages}::messenger::receive_message`,
type_arguments: [],
arguments: [
Buffer.from(vaa, 'base64')
],
}
const client = new AptosClient(srcNetwork.rpc);
const tx = (await generateSignAndSubmitEntryFunction(
client,
new AptosAccount(hexToUint8Array(config.networks[src].privateKey)),
payload
) as Types.UserTransaction);
const res = await client.waitForTransactionWithResult(tx.hash);
console.log(`Submitted @ ${tx.hash}`);
return tx.hash;
}
export async function getCurrentMsg(src:string){
const srcNetwork = config.networks[src];
let srcDeploymentInfo;
try{
srcDeploymentInfo = JSON.parse(fs.readFileSync(`./deployinfo/${src}.deploy.json`).toString());
} catch (e){
throw new Error(`${src} is not deployed yet`);
}
let resourceAccAddress = AptosAccount.getResourceAccountAddress(
`${coreMessages}`,
Buffer.from("messenger_state")
);
const client = new AptosClient(srcNetwork.rpc);
let state = await client.getAccountResource(
resourceAccAddress,
`${coreMessages}::messenger::State`
);
return (state.data as any).current_message;
}
export async function getEmitterAddress(rpc: string, deployedContractAddress:string): Promise<string> {
let resourceAccAddress = AptosAccount.getResourceAccountAddress(
`${deployedContractAddress}`,
Buffer.from("messenger_state")
);
const client = new AptosClient(rpc);
let state = await client.getAccountResource(
resourceAccAddress,
`${deployedContractAddress}::messenger::State`
);
return Number((state.data as any).emitter_cap.emitter).toString(16).padStart(64, '0');
}

View File

@ -1,17 +1,19 @@
import * as fs from 'fs';
import { exec } from "child_process";
import { getEmitterAddressEth, getEmitterAddressSolana, parseSequenceFromLogEth, setDefaultWasm } from '@certusone/wormhole-sdk';
import { getEmitterAddressEth, getEmitterAddressSolana, parseSequenceFromLogEth } from '@certusone/wormhole-sdk';
import * as ethers from 'ethers';
import fetch from 'node-fetch';
import {getEmitterAddress as getEmitterAddressAptos} from './aptos';
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString());
export async function deploy(chain: string){
const rpc = config.networks[chain]['rpc'];
const privateKey = config.networks[chain]['privateKey'];
const bridgeAddress = config.networks[chain]['bridgeAddress'];
exec(
`cd chains/evm && forge build && forge create --legacy --rpc-url ${rpc} --private-key ${privateKey} src/Messenger.sol:Messenger && exit`,
`cd chains/evm && forge build && forge create --legacy --rpc-url ${rpc} --private-key ${privateKey} src/Messenger.sol:Messenger --constructor-args ${bridgeAddress} && exit`,
(err, out, errStr) => {
if (err) {
throw new Error(err.message);
@ -61,9 +63,11 @@ export async function registerApp(src:string, target:string){
targetEmitter = getEmitterAddressEth(targetDeploymentInfo['address']);
break;
case 'solana':
setDefaultWasm("node"); // *sigh*
targetEmitter = await getEmitterAddressSolana(targetDeploymentInfo['address']);
break;
case 'aptos':
targetEmitter = await getEmitterAddressAptos(targetNetwork.rpc, targetDeploymentInfo['address']);
break;
}
const emitterBuffer = Buffer.from(targetEmitter, 'hex');
@ -110,8 +114,8 @@ export async function sendMsg(src:string, msg:string){
).abi,
signer
);
const tx = await (await messenger.sendMsg(Buffer.from(msg))).wait();
const tx = await (await messenger.sendMsg(Buffer.from(msg), {gasLimit: 150000})).wait();
const seq = parseSequenceFromLogEth(tx, srcNetwork['bridgeAddress']);
const emitterAddr = getEmitterAddressEth(srcDeploymentInfo['address']);

View File

@ -5,11 +5,10 @@ import fetch from 'node-fetch';
import {
getEmitterAddressEth,
getEmitterAddressSolana,
importCoreWasm,
parseSequenceFromLogSolana,
postVaaSolanaWithRetry,
setDefaultWasm,
getSignedVAAHash,
getSignedVAAHash,
parseVaa,
} from '@certusone/wormhole-sdk';
import * as anchor from '@project-serum/anchor';
import { findProgramAddressSync } from '@project-serum/anchor/dist/cjs/utils/pubkey';
@ -190,7 +189,6 @@ export async function sendMsg(src:string, msg:string){
.rpc();
const seq = parseSequenceFromLogSolana(await messenger.provider.connection.getTransaction(tx))
setDefaultWasm("node"); // *sigh*
const emitterAddr = await getEmitterAddressSolana(messenger.programId.toString()); //same as whDerivedEmitter
await new Promise((r) => setTimeout(r, 5000)); // Wait for guardian to pick up message
@ -222,7 +220,6 @@ export async function sendMsg(src:string, msg:string){
}
export async function submitVaa(src:string, target:string, idx:string){
setDefaultWasm("node"); //WASM will be removed very soon, but until then, all the solana functions rely on it
const srcNetwork = config.networks[src];
let srcDeploymentInfo;
@ -270,23 +267,21 @@ export async function submitVaa(src:string, target:string, idx:string){
// Then we submit to our program to go through the program's checks
await new Promise((r) => setTimeout(r, 5000));
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 parsed_vaa = parseVaa(Buffer.from(vaa, 'base64'));
const vaaHash = getVaaHash(parsed_vaa); //await getSignedVAAHash(Buffer.from(vaa, "base64"));
//console.log("Hash: ", vaaHash, await getSignedVAAHash(Buffer.from(vaa, "base64")));
// Account that we stored the registered foreign emitter
let emitterAddressAcc = findProgramAddressSync([
Buffer.from("EmitterAddress"),
byteify.serializeUint16(parsed_vaa.emitter_chain)
byteify.serializeUint16(parsed_vaa.emitterChain)
], messenger.programId)[0];
// A blank account we're creating just to keep track of already processed messages
let processedVaaKey = findProgramAddressSync([
Buffer.from(getEmitterAddressEth(targetDeploymentInfo.address), "hex"),
byteify.serializeUint16(parsed_vaa.emitter_chain),
byteify.serializeUint64(parsed_vaa.sequence)
byteify.serializeUint16(parsed_vaa.emitterChain),
byteify.serializeUint64(Number(parsed_vaa.sequence))
], messenger.programId)[0];
// Account where the core bridge stored the vaa after the signatures checked out

View File

@ -2,6 +2,7 @@ import { Command } from 'commander';
import * as fs from 'fs';
import * as evm from './handlers/evm';
import * as solana from './handlers/solana';
import * as aptos from './handlers/aptos';
const config = JSON.parse(fs.readFileSync('./xdapp.config.json').toString())
@ -29,6 +30,9 @@ program
case "solana":
await solana.deploy(network);
break;
case 'aptos':
await aptos.deploy(network);
break;
}
console.log(`Deploy finished!`);
@ -56,6 +60,10 @@ program
break;
case "solana":
await solana.registerApp(src, target);
break;
case "aptos":
await aptos.registerApp(src, target);
break;
}
console.log(`Foreign Network ${target} registered on ${src}`);
@ -82,6 +90,9 @@ program
case "solana":
await solana.sendMsg(src, msg);
break;
case "aptos":
await aptos.sendMsg(src, msg);
break;
}
console.log(`Emitted VAA on ${src} network. Submit it using \`submit-vaa\` command on a target network.`)
} catch (e){
@ -113,6 +124,9 @@ program
case "solana":
await solana.submitVaa(src,target,idx);
break;
case "aptos":
await aptos.submitVaa(src, target, idx);
break;
}
console.log(`Submitted VAA #${idx} from ${target} to chain ${src}`);
@ -140,6 +154,9 @@ program
case "solana":
msg = await solana.getCurrentMsg(src);
break;
case "aptos":
msg = await aptos.getCurrentMsg(src);
break;
}
console.log(`Current Message on Network ${src} is ${msg}`);

View File

@ -6,10 +6,11 @@
"author": "Dev Bharel",
"license": "MIT",
"dependencies": {
"@certusone/wormhole-sdk": "^0.6.0",
"@certusone/wormhole-sdk": "^0.9.6",
"@project-serum/anchor": "^0.25.0",
"@solana/web3.js": "^1.64.0",
"@types/node-fetch": "^2.6.2",
"aptos": "^1.4.0",
"byteify": "^2.0.10",
"commander": "^9.4.0",
"ethers": "^5.6.9",

View File

@ -14,9 +14,9 @@ import { ethers } from "ethers";
import { Connection as SolanaConnection } from "@solana/web3.js";
import { BaseVAA } from "./utils";
import { TextDecoder } from "util";
import { parseVaa } from "@certusone/wormhole-sdk";
// todo: do we need this in the plugin or just the relayer??
whSdk.setDefaultWasm("node");
function create(
commonConfig: CommonPluginEnv,
@ -165,8 +165,7 @@ export class MessengerRelayerPlugin implements Plugin<VAA> {
async parseVAA(vaa: Buffer | Uint8Array): Promise<BaseVAA> {
try {
const { parse_vaa } = await whSdk.importCoreWasm();
return parse_vaa(new Uint8Array(vaa)) as BaseVAA;
return parseVaa(new Uint8Array(vaa)) as any as BaseVAA;
} catch (e) {
this.logger.error("Failed to parse vaa");
throw e;

View File

@ -0,0 +1,26 @@
# Install Deps
yarn
# Deploy evm0 and aptos
ts-node orchestrator.ts deploy evm0
ts-node orchestrator.ts deploy aptos
# Register evm0 on aptos and vice versa
ts-node orchestrator.ts register-network evm0 aptos
ts-node orchestrator.ts register-network aptos evm0
# Emit VAA from evm0
ts-node orchestrator.ts emit-msg evm0 "Hello from evm0"
# Emit VAA from aptos
ts-node orchestrator.ts emit-msg aptos "Hello from aptos"
# Submit evm0 VAA to aptos
ts-node orchestrator.ts submit-vaa aptos evm0 latest
ts-node orchestrator.ts submit-vaa evm0 aptos latest
# Wait a couple blocks for confirmation
sleep 3
# Get Current Messages from evm0 and aptos
ts-node orchestrator.ts get-msg evm0
ts-node orchestrator.ts get-msg aptos

View File

@ -20,6 +20,13 @@
"rpc": "http://localhost:8899",
"privateKey": "J2D4pwDred8P9ioyPEZVLPht885AeYpifsFGUyuzVmiKQosAvmZP4EegaKFrSprBC5vVP1xTvu61vYDWsxBNsYx",
"bridgeAddress": "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
},
"aptos": {
"type": "aptos",
"wormholeChainId": 22,
"rpc": "http://localhost:8080",
"privateKey": "537c1f91e56891445b491068f519b705f8c0f1a1e66111816dd5d4aa85b8113d",
"bridgeAddress": "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017"
}
},
"wormhole": {

File diff suppressed because it is too large Load Diff

3133
projects/messenger/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/usr/bin/env bash
set -euo pipefail
@ -6,5 +6,4 @@ set -euo pipefail
npx pm2 delete aptos 2> /dev/null || true
npx pm2 start 'aptos node run-local-testnet --with-faucet --force-restart --assume-yes' --name aptos
sleep 15
cd wormhole/aptos/scripts && bash deploy devnet && cd ../../../
cd wormhole/aptos/scripts && bash deploy devnet && worm aptos faucet && bash register_devnet && cd ../../../

View File

@ -46,5 +46,5 @@ docker run --rm --name guardiand $DOCKER_FLAGS --hostname guardian-0 --cap-add=I
--algorandAlgodRPC https://$HOST:4001 \
--algorandAppID "4" \
--aptosRPC http://$HOST:8080 \
--aptosAccount "277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b" \
--aptosHandle "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b::state::WormholeMessageHandle" \
--aptosAccount "de0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017" \
--aptosHandle "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017::state::WormholeMessageHandle"

View File

@ -6,7 +6,7 @@
"scripts": {
"setup": "bash setup.bash",
"wormhole": "npm run setup && bash wormhole.bash",
"spy": "npm run setup && bash spy_guardiand.bash",
"spy": "npm run setup && bash spy.bash",
"evm": "npm run setup && bash evm.bash",
"solana": "npm run setup && bash solana-binary-deploy.bash",
"aptos": "npm run setup && bash aptos.bash",

View File

@ -7,5 +7,7 @@ then
git clone https://github.com/wormhole-foundation/wormhole
cd wormhole/clients/js
make install
cd ../../../
cd ../../scripts
bash guardian-set-init.sh
cd ../../
fi