wormhole/aptos/token_bridge/sources/state.move

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(),
}
);
}
}