Build
Protocol Contracts
Sui
use gateway::evm;
use std::address;
use std::ascii;
use std::bcs;
use std::option;
use std::string;
use std::type_name;
use std::vector;
use sui::address;
use sui::bag;
use sui::balance;
use sui::coin;
use sui::config;
use sui::deny_list;
use sui::dynamic_field;
use sui::dynamic_object_field;
use sui::event;
use sui::hex;
use sui::object;
use sui::party;
use sui::sui;
use sui::table;
use sui::transfer;
use sui::tx_context;
use sui::types;
use sui::url;
use sui::vec_map;
use sui::vec_set;
public struct Vault<phantom T> has store
Fields
balance: sui::balance::Balance<T>
whitelisted: bool
public struct Gateway has key
Fields
id: sui::object::UID
vaults: sui::bag::Bag
nonce: u64
active_withdraw_cap: sui::object::ID
active_whitelist_cap: sui::object::ID
deposit_paused: bool
public struct WithdrawCap has key, store
Fields
id: sui::object::UID
public struct WhitelistCap has key, store
Fields
id: sui::object::UID
public struct AdminCap has key, store
Fields
id: sui::object::UID
public struct DepositEvent has copy, drop
Fields
coin_type: std::ascii::String
amount: u64
sender: address
receiver: std::ascii::String
public struct DepositAndCallEvent has copy, drop
Fields
coin_type: std::ascii::String
amount: u64
sender: address
receiver: std::ascii::String
payload: vector<u8>
public struct WithdrawEvent has copy, drop
Fields
coin_type: std::ascii::String
amount: u64
sender: address
receiver: address
nonce: u64
public struct NonceIncreaseEvent has copy, drop
Fields
sender: address
nonce: u64
const EAlreadyWhitelisted: u64 = 0;
const EDepositPaused: u64 = 7;
const EInactiveWhitelistCap: u64 = 6;
const EInactiveWithdrawCap: u64 = 5;
const EInvalidReceiverAddress: u64 = 1;
const ENonceMismatch: u64 = 3;
const ENotWhitelisted: u64 = 2;
const EPayloadTooLong: u64 = 4;
const PayloadMaxLength: u64 = 1024;
fun init(ctx: &mut sui::tx_context::TxContext)
Implementation
fun init(ctx: &mut TxContext) {
    // to withdraw tokens from the gateway, the caller must have the WithdrawCap
    let withdraw_cap = WithdrawCap {
        id: object::new(ctx),
    };
    // to whitelist a new vault, the caller must have the WhitelistCap
    let whitelist_cap = WhitelistCap {
        id: object::new(ctx),
    };
    // to whitelist a new vault, the caller must have the AdminCap
    let admin_cap = AdminCap {
        id: object::new(ctx),
    };
    // create and share the gateway object
    let mut gateway = Gateway {
        id: object::new(ctx),
        vaults: bag::new(ctx),
        nonce: 0,
        active_withdraw_cap: object::id(&withdraw_cap),
        active_whitelist_cap: object::id(&whitelist_cap),
        deposit_paused: false,
    };
    // whitelist SUI by default
    whitelist_impl<SUI>(&mut gateway, &whitelist_cap);
    transfer::transfer(withdraw_cap, tx_context::sender(ctx));
    transfer::transfer(whitelist_cap, tx_context::sender(ctx));
    transfer::transfer(admin_cap, tx_context::sender(ctx));
    transfer::share_object(gateway);
}
entry fun increase_nonce(gateway: &mut gateway::gateway::Gateway, nonce: u64, cap: &gateway::gateway::WithdrawCap, ctx: &sui::tx_context::TxContext)
Implementation
entry fun increase_nonce(gateway: &mut Gateway, nonce: u64, cap: &WithdrawCap, ctx: &TxContext) {
    assert!(gateway.active_withdraw_cap == object::id(cap), EInactiveWithdrawCap);
    assert!(nonce == gateway.nonce, ENonceMismatch);
    gateway.nonce = nonce + 1;
    // Emit event
    event::emit(NonceIncreaseEvent {
        sender: tx_context::sender(ctx),
        nonce: gateway.nonce,
    });
}
entry fun withdraw<T>(gateway: &mut gateway::gateway::Gateway, amount: u64, nonce: u64, receiver: address, gas_budget: u64, cap: &gateway::gateway::WithdrawCap, ctx: &mut sui::tx_context::TxContext)
Implementation
entry fun withdraw<T>(
    gateway: &mut Gateway,
    amount: u64,
    nonce: u64,
    receiver: address,
    gas_budget: u64,
    cap: &WithdrawCap,
    ctx: &mut TxContext,
) {
    let (coins, coins_gas_budget) = withdraw_impl<T>(gateway, amount, nonce, gas_budget, cap, ctx);
    transfer::public_transfer(coins, receiver);
    transfer::public_transfer(coins_gas_budget, tx_context::sender(ctx));
    // Emit event
    event::emit(WithdrawEvent {
        coin_type: coin_name<T>(),
        amount: amount,
        sender: tx_context::sender(ctx),
        receiver: receiver,
        nonce: nonce,
    });
}
entry fun whitelist<T>(gateway: &mut gateway::gateway::Gateway, cap: &gateway::gateway::WhitelistCap)
Implementation
entry fun whitelist<T>(gateway: &mut Gateway, cap: &WhitelistCap) {
    whitelist_impl<T>(gateway, cap)
}
entry fun unwhitelist<T>(gateway: &mut gateway::gateway::Gateway, cap: &gateway::gateway::AdminCap)
Implementation
entry fun unwhitelist<T>(gateway: &mut Gateway, cap: &AdminCap) {
    unwhitelist_impl<T>(gateway, cap)
}
entry fun issue_withdraw_and_whitelist_cap(gateway: &mut gateway::gateway::Gateway, _cap: &gateway::gateway::AdminCap, ctx: &mut sui::tx_context::TxContext)
Implementation
entry fun issue_withdraw_and_whitelist_cap(
    gateway: &mut Gateway,
    _cap: &AdminCap,
    ctx: &mut TxContext,
) {
    let (withdraw_cap, whitelist_cap) = issue_withdraw_and_whitelist_cap_impl(gateway, _cap, ctx);
    transfer::transfer(withdraw_cap, tx_context::sender(ctx));
    transfer::transfer(whitelist_cap, tx_context::sender(ctx));
}
entry fun pause(gateway: &mut gateway::gateway::Gateway, cap: &gateway::gateway::AdminCap)
Implementation
entry fun pause(gateway: &mut Gateway, cap: &AdminCap) {
    pause_impl(gateway, cap)
}
entry fun unpause(gateway: &mut gateway::gateway::Gateway, cap: &gateway::gateway::AdminCap)
Implementation
entry fun unpause(gateway: &mut Gateway, cap: &AdminCap) {
    unpause_impl(gateway, cap)
}
entry fun reset_nonce(gateway: &mut gateway::gateway::Gateway, nonce: u64, _cap: &gateway::gateway::AdminCap)
Implementation
entry fun reset_nonce(gateway: &mut Gateway, nonce: u64, _cap: &AdminCap) {
    gateway.nonce = nonce;
}
public entry fun deposit<T>(gateway: &mut gateway::gateway::Gateway, coins: sui::coin::Coin<T>, receiver: std::ascii::String, ctx: &mut sui::tx_context::TxContext)
Implementation
public entry fun deposit<T>(
    gateway: &mut Gateway,
    coins: Coin<T>,
    receiver: String,
    ctx: &mut TxContext,
) {
    let amount = coins.value();
    let coin_name = coin_name<T>();
    check_receiver_and_deposit_to_vault(gateway, coins, receiver);
    // Emit deposit event
    event::emit(DepositEvent {
        coin_type: coin_name,
        amount: amount,
        sender: tx_context::sender(ctx),
        receiver: receiver,
    });
}
public entry fun deposit_and_call<T>(gateway: &mut gateway::gateway::Gateway, coins: sui::coin::Coin<T>, receiver: std::ascii::String, payload: vector<u8>, ctx: &mut sui::tx_context::TxContext)
Implementation
public entry fun deposit_and_call<T>(
    gateway: &mut Gateway,
    coins: Coin<T>,
    receiver: String,
    payload: vector<u8>,
    ctx: &mut TxContext,
) {
    assert!(payload.length() <= PayloadMaxLength, EPayloadTooLong);
    let amount = coins.value();
    let coin_name = coin_name<T>();
    check_receiver_and_deposit_to_vault(gateway, coins, receiver);
    // Emit deposit event
    event::emit(DepositAndCallEvent {
        coin_type: coin_name,
        amount: amount,
        sender: tx_context::sender(ctx),
        receiver: receiver,
        payload: payload,
    });
}
fun check_receiver_and_deposit_to_vault<T>(gateway: &mut gateway::gateway::Gateway, coins: sui::coin::Coin<T>, receiver: std::ascii::String)
Implementation
fun check_receiver_and_deposit_to_vault<T>(
    gateway: &mut Gateway,
    coins: Coin<T>,
    receiver: String,
) {
    assert!(evm::is_valid_evm_address(receiver), EInvalidReceiverAddress);
    assert!(is_whitelisted<T>(gateway), ENotWhitelisted);
    assert!(!gateway.deposit_paused, EDepositPaused);
    // Deposit the coin into the vault
    let coin_name = coin_name<T>();
    let vault = bag::borrow_mut<String, Vault<T>>(&mut gateway.vaults, coin_name);
    balance::join(&mut vault.balance, coins.into_balance());
}
public fun withdraw_impl<T>(gateway: &mut gateway::gateway::Gateway, amount: u64, nonce: u64, gas_budget: u64, cap: &gateway::gateway::WithdrawCap, ctx: &mut sui::tx_context::TxContext): (sui::coin::Coin<T>, sui::coin::Coin<sui::sui::SUI>)
Implementation
public fun withdraw_impl<T>(
    gateway: &mut Gateway,
    amount: u64,
    nonce: u64,
    gas_budget: u64,
    cap: &WithdrawCap,
    ctx: &mut TxContext,
): (Coin<T>, Coin<sui::sui::SUI>) {
    assert!(gateway.active_withdraw_cap == object::id(cap), EInactiveWithdrawCap);
    assert!(is_whitelisted<T>(gateway), ENotWhitelisted);
    assert!(nonce == gateway.nonce, ENonceMismatch); // prevent replay
    gateway.nonce = nonce + 1;
    // Withdraw the coin from the vault
    let coin_name = coin_name<T>();
    let vault = bag::borrow_mut<String, Vault<T>>(&mut gateway.vaults, coin_name);
    let coins_out = coin::take(&mut vault.balance, amount, ctx);
    // Withdraw SUI to cover the gas budget
    let sui_vault = bag::borrow_mut<String, Vault<sui::sui::SUI>>(
        &mut gateway.vaults,
        coin_name<sui::sui::SUI>(),
    );
    let coins_gas_budget = coin::take(&mut sui_vault.balance, gas_budget, ctx);
    (coins_out, coins_gas_budget)
}
public fun whitelist_impl<T>(gateway: &mut gateway::gateway::Gateway, cap: &gateway::gateway::WhitelistCap)
Implementation
public fun whitelist_impl<T>(gateway: &mut Gateway, cap: &WhitelistCap) {
    assert!(gateway.active_whitelist_cap == object::id(cap), EInactiveWhitelistCap);
    assert!(is_whitelisted<T>(gateway) == false, EAlreadyWhitelisted);
    // if the vault already exists, set it to whitelisted, otherwise create a new vault
    if (bag::contains_with_type<String, Vault<T>>(&gateway.vaults, coin_name<T>())) {
        let vault = bag::borrow_mut<String, Vault<T>>(&mut gateway.vaults, coin_name<T>());
        vault.whitelisted = true;
    } else {
        let vault_name = coin_name<T>();
        let vault = Vault<T> {
            balance: balance::zero<T>(),
            whitelisted: true,
        };
        bag::add(&mut gateway.vaults, vault_name, vault);
    }
}
public fun unwhitelist_impl<T>(gateway: &mut gateway::gateway::Gateway, _cap: &gateway::gateway::AdminCap)
Implementation
public fun unwhitelist_impl<T>(gateway: &mut Gateway, _cap: &AdminCap) {
    assert!(is_whitelisted<T>(gateway), ENotWhitelisted);
    let vault = bag::borrow_mut<String, Vault<T>>(&mut gateway.vaults, coin_name<T>());
    vault.whitelisted = false;
}
public fun issue_withdraw_and_whitelist_cap_impl(gateway: &mut gateway::gateway::Gateway, _cap: &gateway::gateway::AdminCap, ctx: &mut sui::tx_context::TxContext): (gateway::gateway::WithdrawCap, gateway::gateway::WhitelistCap)
Implementation
public fun issue_withdraw_and_whitelist_cap_impl(
    gateway: &mut Gateway,
    _cap: &AdminCap,
    ctx: &mut TxContext,
): (WithdrawCap, WhitelistCap) {
    let withdraw_cap = WithdrawCap {
        id: object::new(ctx),
    };
    let whitelist_cap = WhitelistCap {
        id: object::new(ctx),
    };
    gateway.active_withdraw_cap = object::id(&withdraw_cap);
    gateway.active_whitelist_cap = object::id(&whitelist_cap);
    (withdraw_cap, whitelist_cap)
}
public fun pause_impl(gateway: &mut gateway::gateway::Gateway, _cap: &gateway::gateway::AdminCap)
Implementation
public fun pause_impl(gateway: &mut Gateway, _cap: &AdminCap) {
    gateway.deposit_paused = true;
}
public fun unpause_impl(gateway: &mut gateway::gateway::Gateway, _cap: &gateway::gateway::AdminCap)
Implementation
public fun unpause_impl(gateway: &mut Gateway, _cap: &AdminCap) {
    gateway.deposit_paused = false;
}
public fun nonce(gateway: &gateway::gateway::Gateway): u64
Implementation
public fun nonce(gateway: &Gateway): u64 {
    gateway.nonce
}
public fun active_withdraw_cap(gateway: &gateway::gateway::Gateway): sui::object::ID
Implementation
public fun active_withdraw_cap(gateway: &Gateway): ID {
    gateway.active_withdraw_cap
}
public fun active_whitelist_cap(gateway: &gateway::gateway::Gateway): sui::object::ID
Implementation
public fun active_whitelist_cap(gateway: &Gateway): ID {
    gateway.active_whitelist_cap
}
public fun vault_balance<T>(gateway: &gateway::gateway::Gateway): u64
Implementation
public fun vault_balance<T>(gateway: &Gateway): u64 {
    if (!is_whitelisted<T>(gateway)) {
        return 0
    };
    let coin_name = coin_name<T>();
    let vault = bag::borrow<String, Vault<T>>(&gateway.vaults, coin_name);
    balance::value(&vault.balance)
}
public fun is_paused(gateway: &gateway::gateway::Gateway): bool
Implementation
public fun is_paused(gateway: &Gateway): bool {
    gateway.deposit_paused
}
public fun is_whitelisted<T>(gateway: &gateway::gateway::Gateway): bool
Implementation
public fun is_whitelisted<T>(gateway: &Gateway): bool {
    let vault_name = coin_name<T>();
    if (!bag::contains_with_type<String, Vault<T>>(&gateway.vaults, vault_name)) {
        return false
    };
    let vault = bag::borrow<String, Vault<T>>(&gateway.vaults, vault_name);
    vault.whitelisted
}
fun coin_name<T>(): std::ascii::String
Implementation
fun coin_name<T>(): String {
    into_string(get<T>())
}