Skip to main content

polymesh_api_client_extras/
lib.rs

1use codec::{Decode, Encode};
2
3use polymesh_api::client::basic_types::{AccountId, AssetId, IdentityId};
4use polymesh_api::client::error::Result;
5use polymesh_api::client::BlockHash;
6use polymesh_api::types::{
7  polymesh_primitives::{
8    asset::CheckpointId,
9    checkpoint::ScheduleId,
10    settlement::{InstructionId, LegId, VenueId},
11    ticker::Ticker,
12  },
13  runtime::{events::*, RuntimeEvent},
14};
15use polymesh_api::{Api, ChainApi, TransactionResults};
16
17mod user;
18pub use user::*;
19
20pub const ONE_POLYX: u128 = 1_000_000;
21
22pub type Moment = u64;
23pub type AuthorizationNonce = u64;
24
25#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
26pub struct TargetIdAuthorization {
27  /// Target identity which is authorized to make an operation.
28  pub target_id: IdentityId,
29  /// It HAS TO be `target_id` authorization nonce: See `Identity::offchain_authorization_nonce`.
30  pub nonce: AuthorizationNonce,
31  /// Expire timestamp to limit how long the authorization is valid for.
32  pub expires_at: Moment,
33}
34
35/// An offchain transaction receipt.
36#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
37pub struct Receipt {
38  /// Unique receipt number set by the signer for their receipts.
39  #[cfg(feature = "polymesh_v7")]
40  pub uid: u64,
41  /// The [`InstructionId`] of the instruction for which the receipt is for.
42  pub instruction_id: InstructionId,
43  /// The [`LegId`] of the leg for which the receipt is for.
44  pub leg_id: LegId,
45  /// The [`IdentityId`] of the sender.
46  pub sender_identity: IdentityId,
47  /// The [`IdentityId`] of the receiver.
48  pub receiver_identity: IdentityId,
49  /// [`Ticker`] of the asset being transferred.
50  pub ticker: Ticker,
51  /// The amount transferred.
52  pub amount: u128,
53}
54
55#[derive(Clone, Debug, PartialEq, Eq)]
56pub enum CreatedIds {
57  AssetCreated(AssetId),
58  IdentityCreated(IdentityId),
59  /// Deprecated.
60  ChildIdentityCreated(IdentityId),
61  MultiSigCreated(AccountId),
62  VenueCreated(VenueId),
63  InstructionCreated(InstructionId),
64  CheckpointCreated(CheckpointId),
65  ScheduleCreated(ScheduleId),
66}
67
68/// Get ids from *Created events.
69pub async fn get_created_ids(res: &mut TransactionResults) -> Result<Vec<CreatedIds>> {
70  Ok(
71    res
72      .events()
73      .await?
74      .map(|events| {
75        let mut ids = Vec::new();
76        for rec in &events.0 {
77          match &rec.event {
78            RuntimeEvent::Asset(AssetEvent::AssetCreated(_, id, ..)) => {
79              ids.push(CreatedIds::AssetCreated(*id));
80            }
81            RuntimeEvent::Settlement(SettlementEvent::VenueCreated(_, id, ..)) => {
82              ids.push(CreatedIds::VenueCreated(*id));
83            }
84            RuntimeEvent::Settlement(SettlementEvent::InstructionCreated(_, _, id, ..)) => {
85              ids.push(CreatedIds::InstructionCreated(*id));
86            }
87            RuntimeEvent::Checkpoint(CheckpointEvent::CheckpointCreated(_, _, id, ..)) => {
88              ids.push(CreatedIds::CheckpointCreated(id.clone()));
89            }
90            RuntimeEvent::Checkpoint(CheckpointEvent::ScheduleCreated(_, _, id, ..)) => {
91              ids.push(CreatedIds::ScheduleCreated(id.clone()));
92            }
93            RuntimeEvent::Identity(IdentityEvent::DidCreated(id, ..)) => {
94              ids.push(CreatedIds::IdentityCreated(*id));
95            }
96            #[cfg(not(feature = "polymesh_v8"))]
97            RuntimeEvent::Identity(IdentityEvent::ChildDidCreated(_, id, ..)) => {
98              ids.push(CreatedIds::ChildIdentityCreated(*id));
99            }
100            RuntimeEvent::MultiSig(MultiSigEvent::MultiSigCreated { multisig, .. }) => {
101              ids.push(CreatedIds::MultiSigCreated(*multisig));
102            }
103            _ => (),
104          }
105        }
106        ids
107      })
108      .unwrap_or_default(),
109  )
110}
111
112/// Search transaction events for newly created identity id.
113pub async fn get_identity_id(res: &mut TransactionResults) -> Result<Option<IdentityId>> {
114  Ok(res.events().await?.and_then(|events| {
115    for rec in &events.0 {
116      match &rec.event {
117        RuntimeEvent::Identity(IdentityEvent::DidCreated(id, ..)) => {
118          return Some(*id);
119        }
120        #[cfg(not(feature = "polymesh_v8"))]
121        RuntimeEvent::Identity(IdentityEvent::ChildDidCreated(_, id, ..)) => {
122          return Some(*id);
123        }
124        _ => (),
125      }
126    }
127    None
128  }))
129}
130
131/// Search transaction events for VenueId.
132pub async fn get_venue_id(res: &mut TransactionResults) -> Result<Option<VenueId>> {
133  Ok(res.events().await?.and_then(|events| {
134    for rec in &events.0 {
135      match &rec.event {
136        RuntimeEvent::Settlement(SettlementEvent::VenueCreated(_, venue_id, ..)) => {
137          return Some(*venue_id);
138        }
139        _ => (),
140      }
141    }
142    None
143  }))
144}
145
146/// Search transaction events for InstructionId.
147pub async fn get_instruction_id(res: &mut TransactionResults) -> Result<Option<InstructionId>> {
148  Ok(res.events().await?.and_then(|events| {
149    for rec in &events.0 {
150      match &rec.event {
151        RuntimeEvent::Settlement(SettlementEvent::InstructionCreated(_, _, instruction_id, ..)) => {
152          return Some(*instruction_id);
153        }
154        _ => (),
155      }
156    }
157    None
158  }))
159}
160
161/// Search transaction events for AssetId.
162pub async fn get_asset_id(res: &mut TransactionResults) -> Result<Option<AssetId>> {
163  Ok(res.events().await?.and_then(|events| {
164    for rec in &events.0 {
165      match &rec.event {
166        RuntimeEvent::Asset(AssetEvent::AssetCreated(_, asset_id, ..)) => {
167          return Some(*asset_id);
168        }
169        _ => (),
170      }
171    }
172    None
173  }))
174}
175
176/// Label for signing identity secondary key addition authorization.
177pub const IDENTITY_ADD_SECONDARY_KEY_LABEL: &[u8] = b"Polymesh Identity Add Secondary Key";
178
179/// Label for signing relay transactions.
180pub const RELAY_TX_LABEL: &[u8] = b"Polymesh Relay Transaction";
181
182/// Label for signing STO fundraiser receipts.
183pub const STO_FUNDRAISER_RECEIPT_LABEL: &[u8] = b"Polymesh STO Fundraiser Receipt";
184
185/// Label for signing settlement receipts.
186pub const SETTLEMENT_RECEIPT_LABEL: &[u8] = b"Polymesh Settlement Receipt";
187
188/// Chain scoped message for signing and verifying signatures. This is used to prevent signature replay attacks across different chains.
189#[derive(Encode)]
190pub struct ChainScopedMessage<M: Encode> {
191  pub genesis_hash: BlockHash,
192  pub nonce_or_id: u64,
193  pub label: &'static [u8],
194  pub expires_at: Moment,
195  pub message: M,
196}
197
198impl<M: Encode> ChainScopedMessage<M> {
199  /// Create a new [`ChainScopedMessage`] unchecked.
200  pub async fn new(
201    api: &Api,
202    nonce_or_id: u64,
203    label: &'static [u8],
204    expires_at: Option<Moment>,
205    message: M,
206  ) -> Result<ChainScopedMessage<M>> {
207    let genesis_hash = api.client().get_genesis_hash();
208    let expires_at = if let Some(expires_at) = expires_at {
209      expires_at
210    } else {
211      // Default to 1 hour from now.
212      let now = api.query().timestamp().now().await?;
213      now + 3600_000
214    };
215    Ok(ChainScopedMessage {
216      genesis_hash,
217      nonce_or_id,
218      label,
219      expires_at,
220      message,
221    })
222  }
223}