diff --git a/.changeset/perfect-fishes-fail.md b/.changeset/perfect-fishes-fail.md new file mode 100644 index 00000000..4cf27b36 --- /dev/null +++ b/.changeset/perfect-fishes-fail.md @@ -0,0 +1,5 @@ +--- +'@fuel-bridge/fungible-token': minor +--- + +Replace FRC20 with SRC20 diff --git a/Forc.lock b/Forc.lock index eb61f818..3fa98fec 100644 --- a/Forc.lock +++ b/Forc.lock @@ -4,6 +4,7 @@ source = "member" dependencies = [ "contract_message_receiver", "reentrancy", + "src_20", "std", ] @@ -29,6 +30,11 @@ name = "reentrancy" source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.12.0#063f118a3104adb6a04207ca877993b5ad03a492" dependencies = ["std"] +[[package]] +name = "src_20" +source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.1.2#a3744aa3dcb8c950a433d1fc16645ef9a83dc583" +dependencies = ["std"] + [[package]] name = "std" source = "git+https://github.com/fuellabs/sway?tag=v0.46.0#e75f14b03636bc96751a6760304a1a6d3eb5937d" diff --git a/packages/fungible-token/bridge-fungible-token/Forc.toml b/packages/fungible-token/bridge-fungible-token/Forc.toml index b5908866..5aa485f8 100644 --- a/packages/fungible-token/bridge-fungible-token/Forc.toml +++ b/packages/fungible-token/bridge-fungible-token/Forc.toml @@ -7,3 +7,5 @@ name = "bridge_fungible_token" [dependencies] contract_message_receiver = { path = "../../message-predicates/contract-message-receiver" } reentrancy = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.12.0" } +src_20 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.1.2" } + diff --git a/packages/fungible-token/bridge-fungible-token/src/interface.sw b/packages/fungible-token/bridge-fungible-token/src/interface.sw index f5c54fcf..8e619d05 100644 --- a/packages/fungible-token/bridge-fungible-token/src/interface.sw +++ b/packages/fungible-token/bridge-fungible-token/src/interface.sw @@ -1,5 +1,4 @@ library; -pub mod frc20; pub mod bridge; pub mod src7; diff --git a/packages/fungible-token/bridge-fungible-token/src/interface/bridge.sw b/packages/fungible-token/bridge-fungible-token/src/interface/bridge.sw index f8bf157f..e390d509 100644 --- a/packages/fungible-token/bridge-fungible-token/src/interface/bridge.sw +++ b/packages/fungible-token/bridge-fungible-token/src/interface/bridge.sw @@ -37,7 +37,7 @@ abi Bridge { // Recovers the sub_id used to generate an asset_id (= sha256(contract_id, sub_id)) #[storage(read)] - fn asset_to_sub_id(asset_id: b256) -> b256; + fn asset_to_sub_id(asset_id: AssetId) -> SubId; /// Sends a message to the L1 gateway to inform of capabilities to receive funds #[storage(read)] diff --git a/packages/fungible-token/bridge-fungible-token/src/interface/frc20.sw b/packages/fungible-token/bridge-fungible-token/src/interface/frc20.sw deleted file mode 100644 index 4e112bd2..00000000 --- a/packages/fungible-token/bridge-fungible-token/src/interface/frc20.sw +++ /dev/null @@ -1,23 +0,0 @@ -library; - -use std::{u256::U256, vm::evm::evm_address::EvmAddress}; - -abi FRC20 { - /// Get the total supply of the token. - #[storage(read)] - fn total_supply() -> U256; - - /// Get the name of the token - /// Example (with trailing padding): "MY_TOKEN " - #[storage(read)] - fn name() -> str[64]; - - /// Get the symbol of the token - /// Example (with trailing padding): "TKN " - #[storage(read)] - fn symbol() -> str[32]; - - /// Get the decimals of the token - #[storage(read)] - fn decimals() -> u8; -} diff --git a/packages/fungible-token/bridge-fungible-token/src/main.sw b/packages/fungible-token/bridge-fungible-token/src/main.sw index 9fb79c84..97ebcbde 100644 --- a/packages/fungible-token/bridge-fungible-token/src/main.sw +++ b/packages/fungible-token/bridge-fungible-token/src/main.sw @@ -12,7 +12,7 @@ use contract_message_receiver::MessageReceiver; use errors::BridgeFungibleTokenError; use data_structures::{ADDRESS_DEPOSIT_DATA_LEN, CONTRACT_DEPOSIT_WITHOUT_DATA_LEN, MessageData}; use events::{ClaimRefundEvent, DepositEvent, RefundRegisteredEvent, WithdrawalEvent}; -use interface::{bridge::Bridge, frc20::FRC20, src7::{Metadata, SRC7}}; +use interface::{bridge::Bridge, src7::{Metadata, SRC7}}; use reentrancy::reentrancy_guard; use std::{ call_frames::{ @@ -21,6 +21,10 @@ use std::{ }, constants::ZERO_B256, context::msg_amount, + flags::{ + disable_panic_on_overflow, + enable_panic_on_overflow, + }, hash::Hash, hash::sha256, inputs::input_message_sender, @@ -39,6 +43,7 @@ use utils::{ encode_data, encode_register_calldata, }; +use src_20::SRC20; configurable { DECIMALS: u8 = 9u8, @@ -50,9 +55,10 @@ configurable { } storage { - asset_to_sub_id: StorageMap = StorageMap {}, + asset_to_sub_id: StorageMap = StorageMap {}, refund_amounts: StorageMap> = StorageMap {}, - tokens_minted: u64 = 0, + tokens_minted: StorageMap = StorageMap {}, + total_assets: u64 = 0, } // Implement the process_message function required to be a message receiver @@ -69,14 +75,6 @@ impl MessageReceiver for Contract { let message_data = MessageData::parse(msg_idx); require(message_data.amount != ZERO_B256, BridgeFungibleTokenError::NoCoinsSent); - let sub_id = message_data.token_id; - let asset_id = AssetId::from(sha256((contract_id(), sub_id))); - - if storage.asset_to_sub_id.get(asset_id).try_read().is_none() - { - storage.asset_to_sub_id.insert(asset_id, sub_id); - }; - // register a refund if tokens don't match if (message_data.token_address != BRIDGED_TOKEN) { register_refund(message_data.from, message_data.token_address, message_data.token_id, message_data.amount); @@ -91,12 +89,31 @@ impl MessageReceiver for Contract { register_refund(message_data.from, message_data.token_address, message_data.token_id, message_data.amount); }, Result::Ok(amount) => { + let sub_id = message_data.token_id; + let asset_id = AssetId::new(contract_id(), sub_id); + + disable_panic_on_overflow(); + + let current_total_supply = storage.tokens_minted.get(asset_id).try_read().unwrap_or(0); + let new_total_supply = current_total_supply + amount; + + if new_total_supply < current_total_supply { + register_refund(message_data.from, message_data.token_address, message_data.token_id, message_data.amount); + return; + } + + enable_panic_on_overflow(); + + storage.tokens_minted.insert(asset_id, new_total_supply); + + if storage.asset_to_sub_id.get(asset_id).try_read().is_none() + { + storage.asset_to_sub_id.insert(asset_id, sub_id); + storage.total_assets.write(storage.total_assets.try_read().unwrap_or(0) + 1); + }; + // mint tokens & update storage mint(sub_id, amount); - match storage.tokens_minted.try_read() { - Option::Some(value) => storage.tokens_minted.write(value + amount), - Option::None => storage.tokens_minted.write(amount), - }; // when depositing to an address, msg_data.len is ADDRESS_DEPOSIT_DATA_LEN bytes. // when depositing to a contract, msg_data.len is CONTRACT_DEPOSIT_WITHOUT_DATA_LEN bytes. @@ -166,7 +183,7 @@ impl Bridge for Contract { // attempt to adjust amount into base layer decimals and burn the sent tokens let adjusted_amount = adjust_withdrawal_decimals(amount, DECIMALS, BRIDGED_TOKEN_DECIMALS).unwrap(); - storage.tokens_minted.write(storage.tokens_minted.read() - amount); + storage.tokens_minted.insert(asset_id, storage.tokens_minted.get(asset_id).read() - amount); burn(sub_id, amount); // send a message to unlock this amount on the base layer gateway contract @@ -192,32 +209,44 @@ impl Bridge for Contract { } #[storage(read)] - fn asset_to_sub_id(asset_id: b256) -> b256 { - _asset_to_sub_id(AssetId::from(asset_id)) + fn asset_to_sub_id(asset_id: AssetId) -> SubId { + _asset_to_sub_id(asset_id) } } -impl FRC20 for Contract { +impl SRC20 for Contract { #[storage(read)] - fn total_supply() -> U256 { - let minted: u64 = storage.tokens_minted.try_read().unwrap_or(0); + fn total_assets() -> u64 { + storage.total_assets.try_read().unwrap_or(0) + } - U256::from((0, 0, 0, minted)) + #[storage(read)] + fn total_supply(asset: AssetId) -> Option { + storage.tokens_minted.get(asset).try_read() } #[storage(read)] - fn name() -> str[64] { - NAME + fn name(asset: AssetId) -> Option { + match storage.tokens_minted.get(asset).try_read() { + Some(_) => Some(String::from_ascii_str(from_str_array(NAME))), + None => None, + } } #[storage(read)] - fn symbol() -> str[32] { - SYMBOL + fn symbol(asset: AssetId) -> Option { + match storage.tokens_minted.get(asset).try_read() { + Some(_) => Some(String::from_ascii_str(from_str_array(SYMBOL))), + None => None, + } } #[storage(read)] - fn decimals() -> u8 { - DECIMALS + fn decimals(asset: AssetId) -> Option { + match storage.tokens_minted.get(asset).try_read() { + Some(_) => Some(DECIMALS), + None => None, + } } } @@ -252,7 +281,7 @@ fn register_refund( } #[storage(read)] -fn _asset_to_sub_id(asset_id: AssetId) -> b256 { +fn _asset_to_sub_id(asset_id: AssetId) -> SubId { let sub_id = storage.asset_to_sub_id.get(asset_id).try_read(); require(sub_id.is_some(), BridgeFungibleTokenError::AssetNotFound); sub_id.unwrap() diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs index 4704d32b..463eed2d 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/bridge/mod.rs @@ -16,13 +16,13 @@ mod success { use super::*; - use crate::utils::setup::get_asset_id; use crate::utils::{ constants::BRIDGED_TOKEN_GATEWAY, - interface::bridge::{ - bridged_token, bridged_token_decimals, bridged_token_gateway, claim_refund, + interface::{ + bridge::{bridged_token, bridged_token_decimals, bridged_token_gateway, claim_refund}, + src20::total_supply, }, - setup::{ClaimRefundEvent, RefundRegisteredEvent}, + setup::{get_asset_id, ClaimRefundEvent, RefundRegisteredEvent}, }; use fuels::{prelude::Address, programs::contract::SettableContract, tx::Receipt}; use std::str::FromStr; @@ -80,7 +80,7 @@ mod success { .unwrap(); let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -219,7 +219,7 @@ mod success { .unwrap(); let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -343,21 +343,21 @@ mod success { // Do nothing } _ => { - assert!(false, "Transaction did not succeed") + panic!("Transaction did not succeed") } } - let _receipts = tx_status.take_receipts(); - let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; + let expected_deposited_amount = config.fuel_equivalent_amount(config.amount.max); + // Verify the message value was received by the bridge contract assert_eq!(asset_balance, MESSAGE_AMOUNT); // Check that wallet now has bridged coins - assert_eq!(balance, config.fuel_equivalent_amount(config.amount.max)); + assert_eq!(balance, expected_deposited_amount); // Now try to withdraw let withdrawal_amount = config.fuel_equivalent_amount(config.amount.test); @@ -392,6 +392,12 @@ mod success { assert_eq!(token, Bits256::from_hex_str(BRIDGED_TOKEN).unwrap()); assert_eq!(token_id, Bits256::from_hex_str(BRIDGED_TOKEN_ID).unwrap()); assert_eq!(amount, config.amount.test); + + // Check that supply has decreased by withdrawal_amount + let supply = total_supply(&bridge, get_asset_id(bridge.contract_id())) + .await + .unwrap(); + assert_eq!(supply, expected_deposited_amount - withdrawal_amount); } #[tokio::test] @@ -438,7 +444,7 @@ mod success { .await; let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -484,6 +490,12 @@ mod success { // now verify that the initial amount == the final amount assert_eq!(msg_data_amount, config.amount.min); + + // Verify that total_supply is zero after withdrawing the funds + let supply = total_supply(&bridge, get_asset_id(bridge.contract_id())) + .await + .unwrap(); + assert_eq!(supply, 0u64); } #[tokio::test] @@ -521,6 +533,8 @@ mod success { } mod revert { + use std::str::FromStr; + use super::*; use crate::utils::setup::get_asset_id; @@ -571,7 +585,7 @@ mod revert { .await; let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -621,10 +635,10 @@ mod revert { configurables, ) .await; - + bridge .methods() - .asset_to_sub_id(Bits256::from_hex_str(incorrect_asset_id).unwrap()) + .asset_to_sub_id(AssetId::from_str(incorrect_asset_id).unwrap()) .call() .await .unwrap(); diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/frc20/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/frc20/mod.rs deleted file mode 100644 index 239574de..00000000 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/frc20/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -mod success { - - use crate::utils::{ - constants::PROXY_TOKEN_DECIMALS, - interface::frc20::{decimals, name, symbol, total_supply}, - setup::create_token, - }; - - #[ignore] - #[tokio::test] - async fn check_total_supply() { - // Lacking SDK support on version 0.43 - let contract = create_token().await; - let _response = total_supply(&contract).await; - - // use crate::utils::setup::U256; - // assert_eq!(response, U256::new()); - } - - #[tokio::test] - async fn check_name() { - let contract = create_token().await; - let response = name(&contract).await; - - assert_eq!( - response, - String::from("MY_TOKEN ") - ); - } - - #[tokio::test] - async fn check_symbol() { - let contract = create_token().await; - let response = symbol(&contract).await; - - assert_eq!(response, String::from("MYTKN ")); - } - - #[tokio::test] - async fn check_decimals() { - let contract = create_token().await; - let response = decimals(&contract).await; - - assert_eq!(response, PROXY_TOKEN_DECIMALS) - } -} diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs index 13a2ab55..bb6a1653 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/message_receiver/mod.rs @@ -13,6 +13,7 @@ use std::str::FromStr; mod success { use super::*; + use crate::utils::interface::src20::total_supply; use crate::utils::setup::get_asset_id; use crate::utils::{ constants::MESSAGE_AMOUNT, @@ -62,7 +63,7 @@ mod success { .await; let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge @@ -110,7 +111,7 @@ mod success { .await; let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -120,6 +121,229 @@ mod success { assert_eq!(balance, config.fuel_equivalent_amount(config.amount.max)); } + #[tokio::test] + async fn deposit_to_wallet_multiple_times() { + let mut wallet = create_wallet(); + let configurables: Option = None; + let config = BridgingConfig::new(BRIDGED_TOKEN_DECIMALS, PROXY_TOKEN_DECIMALS); + + let deposit_amount = config.amount.min; + let fuel_deposit_amount = config.fuel_equivalent_amount(deposit_amount); + + let (first_deposit_message, coin, deposit_contract) = create_msg_data( + BRIDGED_TOKEN, + BRIDGED_TOKEN_ID, + FROM, + *wallet.address().hash(), + deposit_amount, + configurables.clone(), + false, + None, + ) + .await; + + let (second_deposit_message, _, _) = create_msg_data( + BRIDGED_TOKEN, + BRIDGED_TOKEN_ID, + FROM, + *wallet.address().hash(), + deposit_amount, + configurables.clone(), + false, + None, + ) + .await; + + let (bridge, utxo_inputs, provider) = setup_environment( + &mut wallet, + vec![coin], + vec![first_deposit_message, second_deposit_message], + deposit_contract, + None, + configurables, + ) + .await; + + let asset_id = get_asset_id(bridge.contract_id()); + + // Get the balance for the deposit contract before + assert!(total_supply(&bridge, asset_id).await.is_none()); + + //////////////////// + // First deposit // + //////////////////// + + // Relay the test message to the bridge contract + let _receipts = relay_message_to_contract( + &wallet, + utxo_inputs.message[0].clone(), + utxo_inputs.contract.clone(), + &utxo_inputs.coin[..], + ) + .await; + + let asset_balance = + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; + let balance = wallet_balance(&wallet, &asset_id).await; + + // Verify the message value was received by the bridge + assert_eq!(asset_balance, MESSAGE_AMOUNT); + + // Check that wallet now has bridged coins + assert_eq!(balance, fuel_deposit_amount); + + let supply = total_supply(&bridge, asset_id).await.unwrap(); + assert_eq!(supply, fuel_deposit_amount); + + //////////////////// + // Second deposit // + //////////////////// + + // Relay the test message to the bridge contract + let _receipts = relay_message_to_contract( + &wallet, + utxo_inputs.message[1].clone(), + utxo_inputs.contract.clone(), + &utxo_inputs.coin[..], + ) + .await; + + let balance = wallet_balance(&wallet, &asset_id).await; + assert_eq!(balance, fuel_deposit_amount * 2); + + let supply = total_supply(&bridge, asset_id).await.unwrap(); + assert_eq!(supply, fuel_deposit_amount * 2); + } + + #[tokio::test] + async fn deposit_to_wallet_total_supply_overflow_triggers_refunds() { + let mut wallet = create_wallet(); + let configurables: Option = None; + let config = BridgingConfig::new(BRIDGED_TOKEN_DECIMALS, PROXY_TOKEN_DECIMALS); + + let max_deposit_amount = config.amount.max; + let max_fuel_deposit_amount = config.fuel_equivalent_amount(max_deposit_amount); + + let (first_deposit_message, coin, deposit_contract) = create_msg_data( + BRIDGED_TOKEN, + BRIDGED_TOKEN_ID, + FROM, + *wallet.address().hash(), + max_deposit_amount, + configurables.clone(), + false, + None, + ) + .await; + + let (second_deposit_message, _, _) = create_msg_data( + BRIDGED_TOKEN, + BRIDGED_TOKEN_ID, + FROM, + *wallet.address().hash(), + max_deposit_amount, + configurables.clone(), + false, + None, + ) + .await; + + let (bridge, utxo_inputs, provider) = setup_environment( + &mut wallet, + vec![coin], + vec![first_deposit_message, second_deposit_message], + deposit_contract, + None, + configurables, + ) + .await; + + let asset_id = get_asset_id(bridge.contract_id()); + + // Get the balance for the deposit contract before + assert!(total_supply(&bridge, asset_id).await.is_none()); + + //////////////////// + // First deposit // + //////////////////// + + // Relay the test message to the bridge contract + let _receipts = relay_message_to_contract( + &wallet, + utxo_inputs.message[0].clone(), + utxo_inputs.contract.clone(), + &utxo_inputs.coin[..], + ) + .await; + + let asset_balance = + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; + let balance = wallet_balance(&wallet, &asset_id).await; + + // Verify the message value was received by the bridge + assert_eq!(asset_balance, MESSAGE_AMOUNT); + + // Check that wallet now has bridged coins + assert_eq!(balance, max_fuel_deposit_amount); + + let supply = total_supply(&bridge, asset_id).await.unwrap(); + assert_eq!(supply, max_fuel_deposit_amount); + + //////////////////// + // Second deposit // + //////////////////// + + // Relay the test message to the bridge contract + let tx_id = relay_message_to_contract( + &wallet, + utxo_inputs.message[1].clone(), + utxo_inputs.contract.clone(), + &utxo_inputs.coin[..], + ) + .await; + + let receipts = wallet + .provider() + .unwrap() + .tx_status(&tx_id) + .await + .expect("Could not obtain transaction status") + .take_receipts(); + + let utxos = wallet + .provider() + .unwrap() + .get_coins(wallet.address(), asset_id) + .await + .unwrap(); + + assert_eq!(utxos.len(), 1); + assert_eq!(utxos[0].amount, max_fuel_deposit_amount); + + let supply = total_supply(&bridge, asset_id).await.unwrap(); + assert_eq!(supply, max_fuel_deposit_amount); + + let refund_registered_events = bridge + .log_decoder() + .decode_logs_with_type::(&receipts) + .unwrap(); + + assert_eq!(refund_registered_events.len(), 1); + + let RefundRegisteredEvent { + amount, + token_address, + from, + token_id, + } = refund_registered_events[0]; + + // Check logs + assert_eq!(amount, Bits256(encode_hex(max_deposit_amount))); + assert_eq!(token_address, Bits256::from_hex_str(BRIDGED_TOKEN).unwrap()); + assert_eq!(from, Bits256::from_hex_str(FROM).unwrap()); + assert_eq!(token_id, Bits256::from_hex_str(BRIDGED_TOKEN_ID).unwrap()); + } + #[tokio::test] async fn deposit_to_contract() { let mut wallet = create_wallet(); @@ -127,12 +351,15 @@ mod success { let configurables: Option = None; let config = BridgingConfig::new(BRIDGED_TOKEN_DECIMALS, PROXY_TOKEN_DECIMALS); + let deposit_amount = config.amount.test; + let fuel_deposit_amount = config.fuel_equivalent_amount(deposit_amount); + let (message, coin, deposit_contract) = create_msg_data( BRIDGED_TOKEN, BRIDGED_TOKEN_ID, FROM, *deposit_contract_id, - config.amount.test, + deposit_amount, configurables.clone(), true, None, @@ -154,7 +381,7 @@ mod success { // Get the balance for the deposit contract before let deposit_contract_balance_before = - contract_balance(provider.clone(), deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider, deposit_contract.contract_id(), asset_id).await; // Relay the test message to the bridge contract let _receipts = relay_message_to_contract( @@ -167,11 +394,11 @@ mod success { // Get the balance for the deposit contract after let deposit_contract_balance_after = - contract_balance(provider, deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider, deposit_contract.contract_id(), asset_id).await; assert_eq!( deposit_contract_balance_after, - deposit_contract_balance_before + config.fuel_equivalent_amount(config.amount.test) + deposit_contract_balance_before + fuel_deposit_amount ); } @@ -209,7 +436,7 @@ mod success { // Get the balance for the deposit contract before let deposit_contract_balance_before = - contract_balance(provider.clone(), deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider.clone(), deposit_contract.contract_id(), asset_id).await; // Relay the test message to the bridge contract let _receipts = relay_message_to_contract( @@ -222,7 +449,7 @@ mod success { // Get the balance for the deposit contract after let deposit_contract_balance_after = - contract_balance(provider, deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider, deposit_contract.contract_id(), asset_id).await; assert_eq!( deposit_contract_balance_after, @@ -264,7 +491,7 @@ mod success { // Get the balance for the deposit contract before let deposit_contract_balance_before = - contract_balance(provider.clone(), deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider.clone(), deposit_contract.contract_id(), asset_id).await; // Relay the test message to the bridge contract let _receipts = relay_message_to_contract( @@ -277,7 +504,7 @@ mod success { // Get the balance for the deposit contract after let deposit_contract_balance_after = - contract_balance(provider, deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider, deposit_contract.contract_id(), asset_id).await; assert_eq!( deposit_contract_balance_after, @@ -319,7 +546,7 @@ mod success { // Get the balance for the deposit contract before let deposit_contract_balance_before = - contract_balance(provider.clone(), deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider.clone(), deposit_contract.contract_id(), asset_id).await; // Relay the test message to the bridge contract let _receipts = relay_message_to_contract( @@ -332,7 +559,7 @@ mod success { // Get the balance for the deposit contract after let deposit_contract_balance_after = - contract_balance(provider, deposit_contract.contract_id(), asset_id).await; + contract_balance(&provider, deposit_contract.contract_id(), asset_id).await; assert_eq!( deposit_contract_balance_after, @@ -399,7 +626,7 @@ mod success { .unwrap(); let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -474,7 +701,7 @@ mod success { .unwrap(); let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -549,7 +776,7 @@ mod success { .unwrap(); let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -624,7 +851,7 @@ mod success { .unwrap(); let asset_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -704,7 +931,7 @@ mod success { .unwrap(); let token_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -782,7 +1009,7 @@ mod success { .unwrap(); let token_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value was received by the bridge contract @@ -894,7 +1121,7 @@ mod success { .unwrap(); let token_balance = - contract_balance(provider, bridge.contract_id(), AssetId::default()).await; + contract_balance(&provider, bridge.contract_id(), AssetId::default()).await; let balance = wallet_balance(&wallet, &get_asset_id(bridge.contract_id())).await; // Verify the message value were received by the bridge contract @@ -982,7 +1209,7 @@ mod revert { assert_eq!(reason, "Revert(18446744073709486080)"); } _ => { - assert!(false, "Transaction did not revert"); + panic!("Transaction did not revert"); } } } diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs index 625bde4d..7b47fb37 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/mod.rs @@ -1,3 +1,3 @@ pub mod bridge; -pub mod frc20; pub mod message_receiver; +pub mod src20; diff --git a/packages/fungible-token/bridge-fungible-token/tests/functions/src20/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/functions/src20/mod.rs new file mode 100644 index 00000000..240d0736 --- /dev/null +++ b/packages/fungible-token/bridge-fungible-token/tests/functions/src20/mod.rs @@ -0,0 +1,62 @@ +mod success { + use std::ops::Div; + + use crate::utils::{ + constants::{PRECISION, PROXY_TOKEN_DECIMALS}, + interface::src20::{decimals, name, symbol, total_assets, total_supply}, + setup::{get_asset_id, setup_test}, + }; + + #[tokio::test] + async fn check_total_supply() { + let (contract, config) = setup_test().await; + let asset_id = get_asset_id(contract.contract_id()); + + let expected_total_supply: u64 = config.amount.test.div(PRECISION).as_u64(); + + assert_eq!( + total_supply(&contract, asset_id).await.unwrap(), + expected_total_supply + ); + } + + #[tokio::test] + async fn check_total_assets() { + let (contract, _config) = setup_test().await; + + assert_eq!(total_assets(&contract).await, 1); + } + + #[tokio::test] + async fn check_name() { + let (contract, _config) = setup_test().await; + let asset_id = get_asset_id(contract.contract_id()); + + let response = name(&contract, asset_id).await.unwrap(); + + assert_eq!( + response, + String::from("MY_TOKEN ") + ); + } + + #[tokio::test] + async fn check_symbol() { + let (contract, _config) = setup_test().await; + let asset_id = get_asset_id(contract.contract_id()); + + let response = symbol(&contract, asset_id).await.unwrap(); + + assert_eq!(response, String::from("MYTKN ")); + } + + #[tokio::test] + async fn check_decimals() { + let (contract, _config) = setup_test().await; + let asset_id = get_asset_id(contract.contract_id()); + + let response = decimals(&contract, asset_id).await.unwrap(); + + assert_eq!(response, PROXY_TOKEN_DECIMALS) + } +} diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs index 5546e231..38d826a2 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/constants.rs @@ -18,5 +18,6 @@ pub(crate) const FROM: &str = "0x00000000000000000000000088888888888888888888888 pub(crate) const BRIDGED_TOKEN_DECIMALS: u8 = 18; pub(crate) const PROXY_TOKEN_DECIMALS: u8 = 9; +pub(crate) const PRECISION: u64 = 10u64.pow((BRIDGED_TOKEN_DECIMALS - PROXY_TOKEN_DECIMALS) as u32); pub(crate) const MESSAGE_AMOUNT: u64 = 100; diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/frc20/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/frc20/mod.rs deleted file mode 100644 index ea7955a7..00000000 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/frc20/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::utils::setup::BridgeFungibleTokenContract; -use fuels::{accounts::wallet::WalletUnlocked, types::U256}; - -pub(crate) async fn total_supply(contract: &BridgeFungibleTokenContract) -> U256 { - contract - .methods() - .total_supply() - .call() - .await - .unwrap() - .value -} - -pub(crate) async fn name(contract: &BridgeFungibleTokenContract) -> String { - contract - .methods() - .name() - .call() - .await - .unwrap() - .value - .to_string() -} - -pub(crate) async fn symbol(contract: &BridgeFungibleTokenContract) -> String { - contract - .methods() - .symbol() - .call() - .await - .unwrap() - .value - .to_string() -} - -pub(crate) async fn decimals(contract: &BridgeFungibleTokenContract) -> u8 { - contract.methods().decimals().call().await.unwrap().value -} diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/mod.rs index 12e40485..b880101c 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/mod.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/mod.rs @@ -1,2 +1,2 @@ pub mod bridge; -pub mod frc20; +pub mod src20; diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/interface/src20/mod.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/src20/mod.rs new file mode 100644 index 00000000..fd448f5f --- /dev/null +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/interface/src20/mod.rs @@ -0,0 +1,64 @@ +use crate::utils::setup::BridgeFungibleTokenContract; +use fuels::{accounts::wallet::WalletUnlocked, types::AssetId}; + +pub(crate) async fn total_supply( + contract: &BridgeFungibleTokenContract, + asset_id: AssetId, +) -> Option { + contract + .methods() + .total_supply(asset_id) + .call() + .await + .unwrap() + .value +} + +pub(crate) async fn total_assets(contract: &BridgeFungibleTokenContract) -> u64 { + contract + .methods() + .total_assets() + .call() + .await + .unwrap() + .value +} + +pub(crate) async fn name( + contract: &BridgeFungibleTokenContract, + asset_id: AssetId, +) -> Option { + contract + .methods() + .name(asset_id) + .call() + .await + .unwrap() + .value +} + +pub(crate) async fn symbol( + contract: &BridgeFungibleTokenContract, + asset_id: AssetId, +) -> Option { + contract + .methods() + .symbol(asset_id) + .call() + .await + .unwrap() + .value +} + +pub(crate) async fn decimals( + contract: &BridgeFungibleTokenContract, + asset_id: AssetId, +) -> Option { + contract + .methods() + .decimals(asset_id) + .call() + .await + .unwrap() + .value +} diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs index 5af076a3..0650c236 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/setup.rs @@ -28,6 +28,10 @@ use primitive_types::U256 as Unsigned256; use sha3::{Digest, Keccak256}; use std::{mem::size_of, num::ParseIntError, result::Result as StdResult, str::FromStr}; +use super::constants::{ + BRIDGED_TOKEN, BRIDGED_TOKEN_DECIMALS, BRIDGED_TOKEN_ID, FROM, PROXY_TOKEN_DECIMALS, +}; + abigen!( Contract( name = "BridgeFungibleTokenContract", @@ -452,7 +456,7 @@ pub(crate) fn parse_output_message_data( } pub(crate) async fn contract_balance( - provider: Provider, + provider: &Provider, contract_id: &Bech32ContractId, asset: AssetId, ) -> u64 { @@ -478,3 +482,42 @@ where pub(crate) fn get_asset_id(contract_id: &Bech32ContractId) -> AssetId { contract_id.asset_id(&Bits256::zeroed()) } + +/// This setup mints tokens so that they are registered as minted assets in the bridge +pub(crate) async fn setup_test() -> (BridgeFungibleTokenContract, BridgingConfig) { + let mut wallet = create_wallet(); + + let config = BridgingConfig::new(BRIDGED_TOKEN_DECIMALS, PROXY_TOKEN_DECIMALS); + + let (message, coin, deposit_contract) = create_msg_data( + BRIDGED_TOKEN, + BRIDGED_TOKEN_ID, + FROM, + *wallet.address().hash(), + config.amount.test, + None, + false, + None, + ) + .await; + + let (contract, utxo_inputs, _) = setup_environment( + &mut wallet, + vec![coin], + vec![message], + deposit_contract, + None, + None, + ) + .await; + + let _receipts = relay_message_to_contract( + &wallet, + utxo_inputs.message[0].clone(), + utxo_inputs.contract, + &utxo_inputs.coin[..], + ) + .await; + + (contract, config) +}