Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
658 changes: 508 additions & 150 deletions near/omni-bridge/src/lib.rs

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions near/omni-bridge/src/migrate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::{
storage::{Decimals, TransferMessageStorage},
Contract, ContractExt, StorageKey,
};
use borsh::{BorshDeserialize, BorshSerialize};
use near_contract_standards::storage_management::StorageBalance;
use near_sdk::{
collections::{LookupMap, LookupSet},
env, near, AccountId, PanicOnDefault,
};
use omni_types::{ChainKind, Nonce, OmniAddress, TransferId};

#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
pub struct OldState {
pub prover_account: AccountId,
pub factories: LookupMap<ChainKind, OmniAddress>,
pub pending_transfers: LookupMap<TransferId, TransferMessageStorage>,
pub finalised_transfers: LookupSet<TransferId>,
pub token_id_to_address: LookupMap<(ChainKind, AccountId), OmniAddress>,
pub token_address_to_id: LookupMap<OmniAddress, AccountId>,
pub token_decimals: LookupMap<OmniAddress, Decimals>,
pub deployed_tokens: LookupSet<AccountId>,
pub token_deployer_accounts: LookupMap<ChainKind, AccountId>,
pub mpc_signer: AccountId,
pub current_origin_nonce: Nonce,
pub destination_nonces: LookupMap<ChainKind, Nonce>,
pub accounts_balances: LookupMap<AccountId, StorageBalance>,
pub wnear_account_id: AccountId,
}

#[near]
impl Contract {
#[private]
#[init(ignore_state)]
pub fn migrate() -> Self {
let old_state: OldState = env::state_read()
.unwrap_or_else(|| env::panic_str("Old state not found. Migration is not needed."));

Self {
prover_account: old_state.prover_account,
factories: old_state.factories,
pending_transfers: old_state.pending_transfers,
finalised_transfers: old_state.finalised_transfers,
fast_transfers: LookupMap::new(StorageKey::FastTransfers),
token_id_to_address: old_state.token_id_to_address,
token_address_to_id: old_state.token_address_to_id,
token_decimals: old_state.token_decimals,
deployed_tokens: old_state.deployed_tokens,
token_deployer_accounts: old_state.token_deployer_accounts,
mpc_signer: old_state.mpc_signer,
current_origin_nonce: old_state.current_origin_nonce,
destination_nonces: old_state.destination_nonces,
accounts_balances: old_state.accounts_balances,
wnear_account_id: old_state.wnear_account_id,
}
}
}
81 changes: 75 additions & 6 deletions near/omni-bridge/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use near_contract_standards::storage_management::{StorageBalance, StorageBalanceBounds};
use near_sdk::{assert_one_yocto, borsh, near};
use near_sdk::{env, near_bindgen, AccountId, NearToken};
use omni_types::TransferId;
use omni_types::{FastTransferStatus, TransferId, TransferMessageV0};

use crate::{
require, ChainKind, Contract, ContractExt, Fee, OmniAddress, Promise, SdkExpect,
Expand All @@ -11,6 +11,13 @@ use crate::{
pub const BRIDGE_TOKEN_INIT_BALANCE: NearToken = NearToken::from_near(3);
pub const NEP141_DEPOSIT: NearToken = NearToken::from_yoctonear(1_250_000_000_000_000_000_000);

#[near(serializers=[borsh, json])]
#[derive(Debug, Clone)]
pub struct TransferMessageStorageValueV0 {
pub message: TransferMessageV0,
pub owner: AccountId,
}

#[near(serializers=[borsh, json])]
#[derive(Debug, Clone)]
pub struct TransferMessageStorageValue {
Expand All @@ -22,27 +29,56 @@ pub struct TransferMessageStorageValue {
#[near(serializers=[borsh, json])]
#[derive(Debug, Clone)]
pub enum TransferMessageStorage {
V0(TransferMessageStorageValue),
V0(TransferMessageStorageValueV0),
V1(TransferMessageStorageValue),
}

impl TransferMessageStorage {
pub fn into_main(self) -> TransferMessageStorageValue {
match self {
TransferMessageStorage::V0(m) => m,
TransferMessageStorage::V0(m) => TransferMessageStorageValue {
message: TransferMessage {
origin_nonce: m.message.origin_nonce,
token: m.message.token,
amount: m.message.amount,
recipient: m.message.recipient,
fee: m.message.fee,
sender: m.message.sender,
msg: m.message.msg,
destination_nonce: m.message.destination_nonce,
origin_transfer_id: None,
},
owner: m.owner,
},
TransferMessageStorage::V1(m) => m,
}
}

pub fn encode_borsh(
message: TransferMessage,
owner: AccountId,
) -> Result<Vec<u8>, std::io::Error> {
borsh::to_vec(&TransferMessageStorage::V0(TransferMessageStorageValue {
borsh::to_vec(&TransferMessageStorage::V1(TransferMessageStorageValue {
message,
owner,
}))
}
}

#[near(serializers=[borsh, json])]
#[derive(Debug, Clone)]
pub enum FastTransferStatusStorage {
V0(FastTransferStatus),
}

impl FastTransferStatusStorage {
pub fn into_main(self) -> FastTransferStatus {
match self {
FastTransferStatusStorage::V0(status) => status,
}
}
}

#[near(serializers=[borsh, json])]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Decimals {
Expand Down Expand Up @@ -93,9 +129,13 @@ impl Contract {
.sdk_expect("The amount is greater than the available storage balance");

self.accounts_balances.insert(&account_id, &storage);

Promise::new(account_id).transfer(to_withdraw);

storage
}

#[payable]
pub fn storage_unregister(&mut self, force: Option<bool>) -> bool {
assert_one_yocto();
let account_id = env::predecessor_account_id();
Expand Down Expand Up @@ -156,7 +196,7 @@ impl Contract {
.sdk_expect("ERR_CAST");

let value_len: u64 =
borsh::to_vec(&TransferMessageStorage::V0(TransferMessageStorageValue {
borsh::to_vec(&TransferMessageStorage::V1(TransferMessageStorageValue {
message: TransferMessage {
origin_nonce: 0,
token: OmniAddress::Near(max_account_id.clone()),
Expand All @@ -166,6 +206,10 @@ impl Contract {
sender: OmniAddress::Near(max_account_id.clone()),
msg: String::new(),
destination_nonce: 0,
origin_transfer_id: Some(TransferId {
origin_chain: ChainKind::Near,
origin_nonce: 0,
}),
},
owner: max_account_id,
}))
Expand All @@ -179,7 +223,7 @@ impl Contract {
}

pub fn required_balance_for_fin_transfer(&self) -> NearToken {
let key_len: u64 = borsh::to_vec(&(ChainKind::Eth, 0_u128))
let key_len: u64 = borsh::to_vec(&(ChainKind::Eth, 0_u64))
.sdk_expect("ERR_BORSH")
.len()
.try_into()
Expand All @@ -192,6 +236,31 @@ impl Contract {
storage_cost.saturating_add(ft_transfers_cost)
}

pub fn required_balance_for_fast_transfer(&self) -> NearToken {
let key_len: u64 = borsh::to_vec(&[0u8; 32])
.sdk_expect("ERR_BORSH")
.len()
.try_into()
.sdk_expect("ERR_CAST");

let max_account_id: AccountId = "a".repeat(64).parse().sdk_expect("ERR_PARSE_ACCOUNT_ID");
let value_len: u64 = borsh::to_vec(&FastTransferStatusStorage::V0(FastTransferStatus {
relayer: max_account_id.clone(),
finalised: false,
storage_owner: max_account_id,
}))
.sdk_expect("ERR_BORSH")
.len()
.try_into()
.sdk_expect("ERR_CAST");

let storage_cost = env::storage_byte_cost()
.saturating_mul((Self::get_basic_storage() + key_len + value_len).into());
let ft_transfers_cost = NearToken::from_yoctonear(1);

storage_cost.saturating_add(ft_transfers_cost)
}

pub fn required_balance_for_bind_token(&self) -> NearToken {
let max_token_id: AccountId = "a".repeat(64).parse().sdk_expect("ERR_PARSE_ACCOUNT_ID");

Expand Down
72 changes: 59 additions & 13 deletions near/omni-bridge/src/tests/lib_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use omni_types::{
locker_args::StorageDepositAction,
prover_result::{InitTransferMessage, ProverResult},
sol_address::SolAddress,
ChainKind, EvmAddress, Fee, InitTransferMsg, Nonce, OmniAddress, TransferId, TransferMessage,
UpdateFee,
BridgeOnTransferMsg, ChainKind, EvmAddress, Fee, InitTransferMsg, Nonce, OmniAddress,
TransferId, TransferMessage, UpdateFee,
};

use crate::storage::Decimals;
Expand Down Expand Up @@ -92,6 +92,33 @@ fn get_init_transfer_msg(recipient: &str, fee: u128, native_token_fee: u128) ->
}

fn run_ft_on_transfer(
contract: &mut Contract,
sender_id: String,
token_id: String,
amount: U128,
attached_deposit: Option<NearToken>,
msg: &BridgeOnTransferMsg,
) -> PromiseOrValue<U128> {
let sender_id = AccountId::try_from(sender_id).expect("Invalid sender ID");
let token_id = AccountId::try_from(token_id).expect("Invalid token ID");

let attached_deposit = if let Some(deplosit) = attached_deposit {
deplosit
} else {
let min_storage_balance = contract.required_balance_for_account();
let init_transfer_balance = contract.required_balance_for_init_transfer();
min_storage_balance.saturating_add(init_transfer_balance)
};

run_storage_deposit(contract, sender_id.clone(), attached_deposit);
setup_test_env(token_id.clone(), NearToken::from_yoctonear(0), None);

let msg = serde_json::to_string(msg).expect("Failed to serialize transfer message");

contract.ft_on_transfer(sender_id, amount, msg)
}

fn run_ft_on_transfer_legacy(
contract: &mut Contract,
sender_id: String,
token_id: String,
Expand All @@ -113,7 +140,7 @@ fn run_ft_on_transfer(
run_storage_deposit(contract, sender_id.clone(), attached_deposit);
setup_test_env(token_id.clone(), NearToken::from_yoctonear(0), None);

let msg = serde_json::to_string(&msg).expect("Failed to serialize transfer message");
let msg = serde_json::to_string(msg).expect("Failed to serialize transfer message");

contract.ft_on_transfer(sender_id, amount, msg)
}
Expand All @@ -129,7 +156,7 @@ fn test_initialize_contract() {
}

#[test]
fn test_ft_on_transfer_nonce_increment() {
fn test_init_transfer_nonce_increment() {
let mut contract = get_default_contract();

run_ft_on_transfer(
Expand All @@ -138,14 +165,14 @@ fn test_ft_on_transfer_nonce_increment() {
DEFAULT_FT_CONTRACT_ACCOUNT.to_string(),
U128(100),
None,
&get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0),
&BridgeOnTransferMsg::InitTransfer(get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0)),
);

assert_eq!(contract.current_origin_nonce, DEFAULT_NONCE + 1);
}

#[test]
fn test_ft_on_transfer_stored_transfer_message() {
fn test_init_transfer_stored_transfer_message() {
let mut contract = get_default_contract();

let msg = get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0);
Expand All @@ -155,7 +182,7 @@ fn test_ft_on_transfer_stored_transfer_message() {
DEFAULT_FT_CONTRACT_ACCOUNT.to_string(),
U128(DEFAULT_TRANSFER_AMOUNT),
None,
&msg,
&BridgeOnTransferMsg::InitTransfer(msg.clone()),
);

let stored_transfer = contract.get_transfer_message(TransferId {
Expand Down Expand Up @@ -189,7 +216,7 @@ fn test_ft_on_transfer_stored_transfer_message() {
}

#[test]
fn test_ft_on_transfer_promise_result() {
fn test_init_transfer_promise_result() {
let mut contract = get_default_contract();

let promise = run_ft_on_transfer(
Expand All @@ -198,7 +225,7 @@ fn test_ft_on_transfer_promise_result() {
DEFAULT_FT_CONTRACT_ACCOUNT.to_string(),
U128(DEFAULT_TRANSFER_AMOUNT),
None,
&get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0),
&BridgeOnTransferMsg::InitTransfer(get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0)),
);

let remaining = match promise {
Expand All @@ -210,20 +237,24 @@ fn test_ft_on_transfer_promise_result() {

#[test]
#[should_panic(expected = "ERR_INVALID_FEE")]
fn test_ft_on_transfer_invalid_fee() {
fn test_init_transfer_invalid_fee() {
let mut contract = get_default_contract();
run_ft_on_transfer(
&mut contract,
DEFAULT_NEAR_USER_ACCOUNT.to_string(),
DEFAULT_FT_CONTRACT_ACCOUNT.to_string(),
U128(DEFAULT_TRANSFER_AMOUNT),
None,
&get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, DEFAULT_TRANSFER_AMOUNT + 1, 0),
&BridgeOnTransferMsg::InitTransfer(get_init_transfer_msg(
DEFAULT_ETH_USER_ADDRESS,
DEFAULT_TRANSFER_AMOUNT + 1,
0,
)),
);
}

#[test]
fn test_ft_on_transfer_balance_updated() {
fn test_init_transfer_balance_updated() {
let mut contract = get_default_contract();

let min_storage_balance = contract.required_balance_for_account();
Expand All @@ -236,7 +267,7 @@ fn test_ft_on_transfer_balance_updated() {
DEFAULT_FT_CONTRACT_ACCOUNT.to_string(),
U128(DEFAULT_TRANSFER_AMOUNT),
Some(total_balance),
&get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0),
&BridgeOnTransferMsg::InitTransfer(get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0)),
);

let storage_balance = contract
Expand Down Expand Up @@ -270,6 +301,7 @@ fn run_update_transfer_fee(
sender: OmniAddress::Near(sender_id.clone().parse().unwrap()),
msg: String::new(),
destination_nonce: 1,
origin_transfer_id: None,
};

contract.insert_raw_transfer(
Expand Down Expand Up @@ -855,3 +887,17 @@ fn test_get_bridged_token() {
"Failed to handle NEAR to NEAR resolution"
);
}

#[test]
fn test_legacy_ft_on_transfer() {
let mut contract = get_default_contract();

run_ft_on_transfer_legacy(
&mut contract,
DEFAULT_NEAR_USER_ACCOUNT.to_string(),
DEFAULT_FT_CONTRACT_ACCOUNT.to_string(),
U128(100),
None,
&get_init_transfer_msg(DEFAULT_ETH_USER_ADDRESS, 0, 0),
);
}
Binary file added near/omni-tests/src/data/omni_bridge-0_2_6.wasm
Binary file not shown.
Loading
Loading