Module gateway::gateway
- Module
gateway::gateway
- Struct
Vault
- Struct
Gateway
- Struct
WithdrawCap
- Struct
WhitelistCap
- Struct
AdminCap
- Struct
DepositEvent
- Struct
DepositAndCallEvent
- Struct
WithdrawEvent
- Struct
NonceIncreaseEvent
- Constants
- Function
init
- Function
increase_nonce
- Function
withdraw
- Function
whitelist
- Function
unwhitelist
- Function
issue_withdraw_and_whitelist_cap
- Function
pause
- Function
unpause
- Function
reset_nonce
- Function
deposit
- Function
deposit_and_call
- Function
check_receiver_and_deposit_to_vault
- Function
withdraw_impl
- Function
whitelist_impl
- Function
unwhitelist_impl
- Function
issue_withdraw_and_whitelist_cap_impl
- Function
pause_impl
- Function
unpause_impl
- Function
nonce
- Function
active_withdraw_cap
- Function
active_whitelist_cap
- Function
vault_balance
- Function
is_paused
- Function
is_whitelisted
- Function
coin_name
- Struct
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;
Struct Vault
public struct Vault<phantom T> has store
Fields
-
balance: sui::balance::Balance<T>
-
whitelisted: bool
Struct Gateway
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
Struct WithdrawCap
public struct WithdrawCap has key, store
Fields
-
id: sui::object::UID
Struct WhitelistCap
public struct WhitelistCap has key, store
Fields
-
id: sui::object::UID
Struct AdminCap
public struct AdminCap has key, store
Fields
-
id: sui::object::UID
Struct DepositEvent
public struct DepositEvent has copy, drop
Fields
-
coin_type: std::ascii::String
-
amount: u64
-
sender: address
-
receiver: std::ascii::String
Struct DepositAndCallEvent
public struct DepositAndCallEvent has copy, drop
Fields
-
coin_type: std::ascii::String
-
amount: u64
-
sender: address
-
receiver: std::ascii::String
-
payload: vector<u8>
Struct WithdrawEvent
public struct WithdrawEvent has copy, drop
Fields
-
coin_type: std::ascii::String
-
amount: u64
-
sender: address
-
receiver: address
-
nonce: u64
Struct NonceIncreaseEvent
public struct NonceIncreaseEvent has copy, drop
Fields
-
sender: address
-
nonce: u64
Constants
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;
Function init
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);
}
Function increase_nonce
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,
});
}
Function withdraw
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,
});
}
Function whitelist
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)
}
Function unwhitelist
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)
}
Function issue_withdraw_and_whitelist_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));
}
Function pause
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)
}
Function unpause
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)
}
Function reset_nonce
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;
}
Function deposit
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,
});
}
Function deposit_and_call
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,
});
}
Function check_receiver_and_deposit_to_vault
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());
}
Function withdraw_impl
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)
}
Function whitelist_impl
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);
}
}
Function unwhitelist_impl
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;
}
Function issue_withdraw_and_whitelist_cap_impl
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)
}
Function pause_impl
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;
}
Function unpause_impl
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;
}
Function nonce
public fun nonce(gateway: &gateway::gateway::Gateway): u64
Implementation
public fun nonce(gateway: &Gateway): u64 {
gateway.nonce
}
Function active_withdraw_cap
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
}
Function active_whitelist_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
}
Function vault_balance
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)
}
Function is_paused
public fun is_paused(gateway: &gateway::gateway::Gateway): bool
Implementation
public fun is_paused(gateway: &Gateway): bool {
gateway.deposit_paused
}
Function is_whitelisted
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
}
Function coin_name
fun coin_name<T>(): std::ascii::String
Implementation
fun coin_name<T>(): String {
into_string(get<T>())
}