233 lines
8.7 KiB
Plaintext
233 lines
8.7 KiB
Plaintext
module token_bridge::state {
|
|
use std::table::{Self, Table};
|
|
use std::option::{Self, Option};
|
|
use aptos_framework::type_info::{Self, TypeInfo, type_of};
|
|
use aptos_framework::account::{Self, SignerCapability};
|
|
use aptos_framework::aptos_coin::AptosCoin;
|
|
use aptos_framework::coin::Coin;
|
|
|
|
use wormhole::u16::U16;
|
|
use wormhole::emitter::EmitterCapability;
|
|
use wormhole::state;
|
|
use wormhole::wormhole;
|
|
use wormhole::set::{Self, Set};
|
|
use wormhole::external_address::ExternalAddress;
|
|
|
|
use token_bridge::token_hash::{Self, TokenHash};
|
|
|
|
friend token_bridge::contract_upgrade;
|
|
friend token_bridge::register_chain;
|
|
friend token_bridge::token_bridge;
|
|
friend token_bridge::vaa;
|
|
friend token_bridge::attest_token;
|
|
friend token_bridge::wrapped;
|
|
friend token_bridge::complete_transfer;
|
|
friend token_bridge::complete_transfer_with_payload;
|
|
friend token_bridge::transfer_tokens;
|
|
|
|
#[test_only]
|
|
friend token_bridge::wrapped_test;
|
|
#[test_only]
|
|
friend token_bridge::vaa_test;
|
|
|
|
const E_ORIGIN_CHAIN_MISMATCH: u64 = 0;
|
|
const E_ORIGIN_ADDRESS_MISMATCH: u64 = 1;
|
|
const E_WRAPPING_NATIVE_COIN: u64 = 2;
|
|
const E_WRAPPED_ASSET_NOT_INITIALIZED: u64 = 3;
|
|
|
|
/// The origin chain and address of a token. In case of native tokens
|
|
/// (where the chain is aptos), the token_address is the hash of the token
|
|
/// info (see token_hash.move for more details)
|
|
struct OriginInfo has key, store, copy, drop {
|
|
token_chain: U16,
|
|
token_address: ExternalAddress,
|
|
}
|
|
|
|
public fun get_origin_info_token_address(info: &OriginInfo): ExternalAddress {
|
|
info.token_address
|
|
}
|
|
|
|
public fun get_origin_info_token_chain(info: &OriginInfo): U16 {
|
|
info.token_chain
|
|
}
|
|
|
|
public(friend) fun create_origin_info(
|
|
token_chain: U16,
|
|
token_address: ExternalAddress,
|
|
): OriginInfo {
|
|
OriginInfo { token_address: token_address, token_chain: token_chain }
|
|
}
|
|
|
|
struct WrappedInfo has store {
|
|
type_info: Option<TypeInfo>,
|
|
signer_cap: SignerCapability
|
|
}
|
|
|
|
struct State has key, store {
|
|
/// Set of consumed VAA hashes
|
|
consumed_vaas: Set<vector<u8>>,
|
|
|
|
/// Mapping of wrapped assets ((chain_id, origin_address) => wrapped_asset info)
|
|
wrapped_infos: Table<OriginInfo, WrappedInfo>,
|
|
|
|
/// Reverse mapping of hash(TypeInfo) for native tokens, so their
|
|
/// information can be looked up externally by knowing their hash (which
|
|
/// is the 32 byte "address" that goes into the VAA).
|
|
native_infos: Table<TokenHash, TypeInfo>,
|
|
|
|
signer_cap: SignerCapability,
|
|
|
|
emitter_cap: EmitterCapability,
|
|
|
|
// Mapping of bridge contracts on other chains
|
|
registered_emitters: Table<U16, ExternalAddress>,
|
|
}
|
|
|
|
// getters
|
|
|
|
public fun vaa_is_consumed(hash: vector<u8>): bool acquires State {
|
|
let state = borrow_global<State>(@token_bridge);
|
|
set::contains(&state.consumed_vaas, hash)
|
|
}
|
|
|
|
public fun wrapped_asset_info(native_info: OriginInfo): TypeInfo acquires State {
|
|
let wrapped_infos = &borrow_global<State>(@token_bridge).wrapped_infos;
|
|
let type_info = table::borrow(wrapped_infos, native_info).type_info;
|
|
assert!(option::is_some(&type_info), E_WRAPPED_ASSET_NOT_INITIALIZED);
|
|
option::extract(&mut type_info)
|
|
}
|
|
|
|
public fun native_asset_info(token_address: TokenHash): TypeInfo acquires State {
|
|
let native_infos = &borrow_global<State>(@token_bridge).native_infos;
|
|
*table::borrow(native_infos, token_address)
|
|
}
|
|
|
|
/// Returns the origin information for a CoinType
|
|
public fun origin_info<CoinType>(): OriginInfo acquires OriginInfo {
|
|
if (is_wrapped_asset<CoinType>()) {
|
|
*borrow_global<OriginInfo>(type_info::account_address(&type_of<CoinType>()))
|
|
} else {
|
|
let token_chain = state::get_chain_id();
|
|
let token_address = token_hash::get_external_address(&token_hash::derive<CoinType>());
|
|
OriginInfo { token_chain, token_address }
|
|
}
|
|
}
|
|
|
|
public fun get_registered_emitter(chain_id: U16): Option<ExternalAddress> acquires State {
|
|
let state = borrow_global<State>(@token_bridge);
|
|
if (table::contains(&state.registered_emitters, chain_id)) {
|
|
option::some(*table::borrow(&state.registered_emitters, chain_id))
|
|
} else {
|
|
option::none()
|
|
}
|
|
|
|
}
|
|
|
|
// given the hash of the TypeInfo of a Coin, this tells us if it is registered with Token Bridge
|
|
public fun is_registered_native_asset<CoinType>(): bool acquires State {
|
|
let token = token_hash::derive<CoinType>();
|
|
let native_infos = &borrow_global<State>(@token_bridge).native_infos;
|
|
!is_wrapped_asset<CoinType>() && table::contains(native_infos, token)
|
|
}
|
|
|
|
public fun is_wrapped_asset<CoinType>(): bool {
|
|
exists<OriginInfo>(type_info::account_address(&type_of<CoinType>()))
|
|
}
|
|
|
|
public(friend) fun setup_wrapped<CoinType>(
|
|
origin_info: OriginInfo
|
|
) acquires State {
|
|
assert!(origin_info.token_chain != state::get_chain_id(), E_WRAPPING_NATIVE_COIN);
|
|
let wrapped_infos = &mut borrow_global_mut<State>(@token_bridge).wrapped_infos;
|
|
let wrapped_info = table::borrow_mut(wrapped_infos, origin_info);
|
|
|
|
let coin_signer = account::create_signer_with_capability(&wrapped_info.signer_cap);
|
|
move_to(&coin_signer, origin_info);
|
|
|
|
wrapped_info.type_info = option::some(type_of<CoinType>());
|
|
|
|
}
|
|
|
|
public fun assert_coin_origin_info<CoinType>(origin: OriginInfo) acquires OriginInfo {
|
|
let coin_origin = origin_info<CoinType>();
|
|
assert!(coin_origin.token_chain == origin.token_chain, E_ORIGIN_CHAIN_MISMATCH);
|
|
assert!(coin_origin.token_address == origin.token_address, E_ORIGIN_ADDRESS_MISMATCH);
|
|
}
|
|
|
|
public(friend) fun publish_message(
|
|
nonce: u64,
|
|
payload: vector<u8>,
|
|
message_fee: Coin<AptosCoin>,
|
|
): u64 acquires State {
|
|
let emitter_cap = &mut borrow_global_mut<State>(@token_bridge).emitter_cap;
|
|
wormhole::publish_message(
|
|
emitter_cap,
|
|
nonce,
|
|
payload,
|
|
message_fee
|
|
)
|
|
}
|
|
|
|
public(friend) fun token_bridge_signer(): signer acquires State {
|
|
account::create_signer_with_capability(&borrow_global<State>(@token_bridge).signer_cap)
|
|
}
|
|
|
|
// setters
|
|
|
|
public(friend) fun set_vaa_consumed(hash: vector<u8>) acquires State {
|
|
let state = borrow_global_mut<State>(@token_bridge);
|
|
set::add(&mut state.consumed_vaas, hash);
|
|
}
|
|
|
|
public(friend) fun set_registered_emitter(chain_id: U16, bridge_contract: ExternalAddress) acquires State {
|
|
let state = borrow_global_mut<State>(@token_bridge);
|
|
table::upsert(&mut state.registered_emitters, chain_id, bridge_contract);
|
|
}
|
|
|
|
// 32-byte native asset address => type info
|
|
public(friend) fun set_native_asset_type_info<CoinType>() acquires State {
|
|
let token_address = token_hash::derive<CoinType>();
|
|
let type_info = type_of<CoinType>();
|
|
|
|
let state = borrow_global_mut<State>(@token_bridge);
|
|
let native_infos = &mut state.native_infos;
|
|
if (table::contains(native_infos, token_address)){
|
|
//TODO: throw error, because we should only be able to set native asset type info once?
|
|
table::remove(native_infos, token_address);
|
|
};
|
|
table::add(native_infos, token_address, type_info);
|
|
}
|
|
|
|
public(friend) fun set_wrapped_asset_signer_capability(token: OriginInfo, signer_cap: SignerCapability) acquires State {
|
|
let state = borrow_global_mut<State>(@token_bridge);
|
|
let wrapped_info = WrappedInfo {
|
|
type_info: option::none(),
|
|
signer_cap
|
|
};
|
|
table::add(&mut state.wrapped_infos, token, wrapped_info);
|
|
}
|
|
|
|
public(friend) fun get_wrapped_asset_signer(origin_info: OriginInfo): signer acquires State {
|
|
let wrapped_coin_signer_caps
|
|
= &borrow_global<State>(@token_bridge).wrapped_infos;
|
|
let wrapped_info = table::borrow(wrapped_coin_signer_caps, origin_info);
|
|
account::create_signer_with_capability(&wrapped_info.signer_cap)
|
|
}
|
|
|
|
public(friend) fun init_token_bridge_state(
|
|
signer_cap: SignerCapability,
|
|
emitter_cap: EmitterCapability
|
|
) {
|
|
let token_bridge = account::create_signer_with_capability(&signer_cap);
|
|
move_to(&token_bridge, State {
|
|
consumed_vaas: set::new<vector<u8>>(),
|
|
wrapped_infos: table::new(),
|
|
native_infos: table::new(),
|
|
signer_cap: signer_cap,
|
|
emitter_cap: emitter_cap,
|
|
registered_emitters: table::new(),
|
|
}
|
|
);
|
|
}
|
|
}
|