Skip to content
Open
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
89 changes: 63 additions & 26 deletions integration/src/asset_helper.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use anyhow::Result;
use std::collections::BTreeSet;

#[cfg(feature = "current_release")]
use polymesh_api::types::polymesh_primitives::settlement::InstructionId;
use polymesh_api::types::polymesh_primitives::{
asset::{AssetName, AssetType},
identity_id::{PortfolioId, PortfolioKind},
Expand All @@ -9,35 +11,73 @@ use polymesh_api::types::polymesh_primitives::{

use crate::*;

/// Get Pending Transfer ID from the transaction results.
#[cfg(feature = "current_release")]
pub async fn get_pending_transfer_id(
res: &mut TransactionResults,
) -> Result<Option<InstructionId>> {
if let Some(events) = res.events().await? {
for rec in &events.0 {
match &rec.event {
RuntimeEvent::Asset(AssetEvent::CreatedAssetTransfer {
pending_transfer_id,
..
}) => {
return Ok(pending_transfer_id.clone());
}
_ => (),
}
}
}
Ok(None)
}

/// Asset Helper.
pub struct AssetHelper {
pub api: Api,
pub asset_id: AssetId,
pub issuer: User,
pub issuer_venue_id: VenueId,
pub issuer_venue_id: Option<VenueId>,
pub issuer_did: IdentityId,
}

impl AssetHelper {
/// Create a new asset, mint some tokens, and pause compliance rules.
/// Create a new asset and mint some tokens.
pub async fn new(
api: &Api,
issuer: &mut User,
name: &str,
mint: u128,
signers: Vec<AccountId>,
) -> Result<Self> {
Self::new_full(api, issuer, name, mint, signers, true).await
}

/// Create a new asset and mint some tokens.
pub async fn new_full(
api: &Api,
issuer: &mut User,
name: &str,
mint: u128,
signers: Vec<AccountId>,
need_venue: bool,
) -> Result<Self> {
// Create a new venue.
let mut venue_res = api
.call()
.settlement()
.create_venue(
VenueDetails(format!("Venue for {name}").into()),
signers,
VenueType::Other,
)?
.submit_and_watch(issuer)
.await?;
let venue_res = if need_venue {
Some(
api.call()
.settlement()
.create_venue(
VenueDetails(format!("Venue for {name}").into()),
signers,
VenueType::Other,
)?
.submit_and_watch(issuer)
.await?,
)
} else {
None
};

// Create a new asset.
let mut asset_res = api
Expand Down Expand Up @@ -66,22 +106,19 @@ impl AssetHelper {
.submit_and_watch(issuer)
.await?;

// Pause compliance rules to allow transfers.
let mut pause_res = api
.call()
.compliance_manager()
.pause_asset_compliance(asset_id)?
.submit_and_watch(issuer)
.await?;

// Wait for mint and pause to complete.
// Wait for mint to complete.
mint_res.ok().await?;
pause_res.ok().await?;

// Get the venue ID from the response.
let issuer_venue_id = get_venue_id(&mut venue_res)
.await?
.expect("Venue ID not found");
let issuer_venue_id = if let Some(mut venue_res) = venue_res {
Some(
get_venue_id(&mut venue_res)
.await?
.expect("Venue ID not found"),
)
} else {
None
};

Ok(Self {
api: api.clone(),
Expand Down Expand Up @@ -155,7 +192,7 @@ impl AssetHelper {
.call()
.settlement()
.add_and_affirm_instruction(
Some(self.issuer_venue_id),
self.issuer_venue_id,
SettlementType::SettleManual(0),
None,
None,
Expand Down
218 changes: 218 additions & 0 deletions integration/tests/asset_transfers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// >=v7.4
#[cfg(feature = "current_release")]
mod asset_transfer_tests {
use anyhow::Result;

use integration::*;

/// Test for an asset transfer requiring receiver affirmation.
#[tokio::test]
async fn asset_transfer_with_receiver_affirm() -> Result<()> {
let mut tester = PolymeshTester::new().await?;
let mut users = tester
.users(&["AssetIssuer1", "Investor1"])
.await?
.into_iter();
let mut asset_issuer = users.next().expect("Asset issuer");
let mut investor = users.next().expect("Investor");

// Create a new asset and mint some tokens.
let asset_helper = AssetHelper::new_full(
&tester.api,
&mut asset_issuer,
"TestAsset",
1_000_000,
vec![],
false,
)
.await?;
let asset_id = asset_helper.asset_id;

// Create an asset transfer from the asset issuer to the investor.
let mut transfer_res = tester
.api
.call()
.asset()
.transfer_asset(asset_id, investor.account(), 10, None)?
.submit_and_watch(&mut asset_issuer)
.await?;

// Get the pending transfer ID from the response.
let pending_transfer_id = get_pending_transfer_id(&mut transfer_res)
.await?
.expect("Pending Transfer ID not found");

// The receiver needs to affirm the transfer
let mut affirm_res = tester
.api
.call()
.asset()
.receiver_affirm_asset_transfer(pending_transfer_id)?
.submit_and_watch(&mut investor)
.await?;

// Wait for the receiver affirmation to complete.
affirm_res.ok().await?;

Ok(())
}

/// Test the receiver rejecting an asset transfer.
#[tokio::test]
async fn asset_transfer_rejected_by_receiver() -> Result<()> {
let mut tester = PolymeshTester::new().await?;
let mut users = tester
.users(&["AssetIssuer1", "Investor1"])
.await?
.into_iter();
let mut asset_issuer = users.next().expect("Asset issuer");
let mut investor = users.next().expect("Investor");

// Create a new asset and mint some tokens.
let asset_helper = AssetHelper::new_full(
&tester.api,
&mut asset_issuer,
"TestAsset",
1_000_000,
vec![],
false,
)
.await?;
let asset_id = asset_helper.asset_id;

// Create an asset transfer from the asset issuer to the investor.
let mut transfer_res = tester
.api
.call()
.asset()
.transfer_asset(asset_id, investor.account(), 10, None)?
.submit_and_watch(&mut asset_issuer)
.await?;

// Get the pending transfer ID from the response.
let pending_transfer_id = get_pending_transfer_id(&mut transfer_res)
.await?
.expect("Pending Transfer ID not found");

// The receiver rejects the transfer
let mut reject_res = tester
.api
.call()
.asset()
.reject_asset_transfer(pending_transfer_id)?
.submit_and_watch(&mut investor)
.await?;

// Wait for the receiver rejection to complete.
reject_res.ok().await?;

Ok(())
}

/// The sender rejects the asset transfer before the receiver affirms it.
#[tokio::test]
async fn asset_transfer_rejected_by_sender() -> Result<()> {
let mut tester = PolymeshTester::new().await?;
let mut users = tester
.users(&["AssetIssuer1", "Investor1"])
.await?
.into_iter();
let mut asset_issuer = users.next().expect("Asset issuer");
let investor = users.next().expect("Investor");

// Create a new asset and mint some tokens.
let asset_helper = AssetHelper::new_full(
&tester.api,
&mut asset_issuer,
"TestAsset",
1_000_000,
vec![],
false,
)
.await?;
let asset_id = asset_helper.asset_id;

// Create an asset transfer from the asset issuer to the investor.
let mut transfer_res = tester
.api
.call()
.asset()
.transfer_asset(asset_id, investor.account(), 10, None)?
.submit_and_watch(&mut asset_issuer)
.await?;

// Get the pending transfer ID from the response.
let pending_transfer_id = get_pending_transfer_id(&mut transfer_res)
.await?
.expect("Pending Transfer ID not found");

// The sender rejects the transfer
let mut reject_res = tester
.api
.call()
.asset()
.reject_asset_transfer(pending_transfer_id)?
.submit_and_watch(&mut asset_issuer)
.await?;

// Wait for the sender rejection to complete.
reject_res.ok().await?;

Ok(())
}

/// Test for an asset transfer with pre-approved receiver affirmation.
#[tokio::test]
async fn asset_transfer_with_receiver_pre_approved() -> Result<()> {
let mut tester = PolymeshTester::new().await?;
let mut users = tester
.users(&["AssetIssuer1", "Investor1"])
.await?
.into_iter();
let mut asset_issuer = users.next().expect("Asset issuer");
let mut investor = users.next().expect("Investor");

// Create a new asset and mint some tokens.
let asset_helper = AssetHelper::new_full(
&tester.api,
&mut asset_issuer,
"TestAsset",
1_000_000,
vec![],
false,
)
.await?;
let asset_id = asset_helper.asset_id;

// Receiver pre-approves asset transfers.
let mut pre_approve_res = tester
.api
.call()
.asset()
.pre_approve_asset(asset_id)?
.submit_and_watch(&mut investor)
.await?;

// Wait for pre-approval to complete.
pre_approve_res.ok().await?;

// Create an asset transfer from the asset issuer to the investor.
let mut transfer_res = tester
.api
.call()
.asset()
.transfer_asset(asset_id, investor.account(), 10, None)?
.submit_and_watch(&mut asset_issuer)
.await?;

// Try to get the pending transfer ID from the response.
let pending_transfer_id = get_pending_transfer_id(&mut transfer_res).await?;

assert!(
pending_transfer_id.is_none(),
"Pending Transfer ID should not be found for pre-approved transfer"
);

Ok(())
}
}
2 changes: 1 addition & 1 deletion integration/tests/settlements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ async fn offchain_settlement() -> Result<()> {
.call()
.settlement()
.add_and_affirm_instruction(
Some(venue_id),
venue_id,
SettlementType::SettleManual(0),
None,
None,
Expand Down
Loading