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  ChildIdentityCreated(IdentityId),
60  MultiSigCreated(AccountId),
61  VenueCreated(VenueId),
62  InstructionCreated(InstructionId),
63  CheckpointCreated(CheckpointId),
64  ScheduleCreated(ScheduleId),
65}
66
67/// Get ids from *Created events.
68pub async fn get_created_ids(res: &mut TransactionResults) -> Result<Vec<CreatedIds>> {
69  Ok(
70    res
71      .events()
72      .await?
73      .map(|events| {
74        let mut ids = Vec::new();
75        for rec in &events.0 {
76          match &rec.event {
77            RuntimeEvent::Asset(AssetEvent::AssetCreated(_, id, ..)) => {
78              ids.push(CreatedIds::AssetCreated(*id));
79            }
80            RuntimeEvent::Settlement(SettlementEvent::VenueCreated(_, id, ..)) => {
81              ids.push(CreatedIds::VenueCreated(*id));
82            }
83            RuntimeEvent::Settlement(SettlementEvent::InstructionCreated(_, _, id, ..)) => {
84              ids.push(CreatedIds::InstructionCreated(*id));
85            }
86            RuntimeEvent::Checkpoint(CheckpointEvent::CheckpointCreated(_, _, id, ..)) => {
87              ids.push(CreatedIds::CheckpointCreated(id.clone()));
88            }
89            RuntimeEvent::Checkpoint(CheckpointEvent::ScheduleCreated(_, _, id, ..)) => {
90              ids.push(CreatedIds::ScheduleCreated(id.clone()));
91            }
92            RuntimeEvent::Identity(IdentityEvent::DidCreated(id, ..)) => {
93              ids.push(CreatedIds::IdentityCreated(*id));
94            }
95            RuntimeEvent::Identity(IdentityEvent::ChildDidCreated(_, id, ..)) => {
96              ids.push(CreatedIds::ChildIdentityCreated(*id));
97            }
98            RuntimeEvent::MultiSig(MultiSigEvent::MultiSigCreated { multisig, .. }) => {
99              ids.push(CreatedIds::MultiSigCreated(*multisig));
100            }
101            _ => (),
102          }
103        }
104        ids
105      })
106      .unwrap_or_default(),
107  )
108}
109
110/// Search transaction events for newly created identity id.
111pub async fn get_identity_id(res: &mut TransactionResults) -> Result<Option<IdentityId>> {
112  Ok(res.events().await?.and_then(|events| {
113    for rec in &events.0 {
114      match &rec.event {
115        RuntimeEvent::Identity(IdentityEvent::DidCreated(id, ..)) => {
116          return Some(*id);
117        }
118        RuntimeEvent::Identity(IdentityEvent::ChildDidCreated(_, id, ..)) => {
119          return Some(*id);
120        }
121        _ => (),
122      }
123    }
124    None
125  }))
126}
127
128/// Search transaction events for VenueId.
129pub async fn get_venue_id(res: &mut TransactionResults) -> Result<Option<VenueId>> {
130  Ok(res.events().await?.and_then(|events| {
131    for rec in &events.0 {
132      match &rec.event {
133        RuntimeEvent::Settlement(SettlementEvent::VenueCreated(_, venue_id, ..)) => {
134          return Some(*venue_id);
135        }
136        _ => (),
137      }
138    }
139    None
140  }))
141}
142
143/// Search transaction events for InstructionId.
144pub async fn get_instruction_id(res: &mut TransactionResults) -> Result<Option<InstructionId>> {
145  Ok(res.events().await?.and_then(|events| {
146    for rec in &events.0 {
147      match &rec.event {
148        RuntimeEvent::Settlement(SettlementEvent::InstructionCreated(_, _, instruction_id, ..)) => {
149          return Some(*instruction_id);
150        }
151        _ => (),
152      }
153    }
154    None
155  }))
156}
157
158/// Search transaction events for AssetId.
159pub async fn get_asset_id(res: &mut TransactionResults) -> Result<Option<AssetId>> {
160  Ok(res.events().await?.and_then(|events| {
161    for rec in &events.0 {
162      match &rec.event {
163        RuntimeEvent::Asset(AssetEvent::AssetCreated(_, asset_id, ..)) => {
164          return Some(*asset_id);
165        }
166        _ => (),
167      }
168    }
169    None
170  }))
171}
172
173/// Label for signing identity secondary key addition authorization.
174pub const IDENTITY_ADD_SECONDARY_KEY_LABEL: &[u8] = b"Polymesh Identity Add Secondary Key";
175
176/// Label for signing relay transactions.
177pub const RELAY_TX_LABEL: &[u8] = b"Polymesh Relay Transaction";
178
179/// Label for signing STO fundraiser receipts.
180pub const STO_FUNDRAISER_RECEIPT_LABEL: &[u8] = b"Polymesh STO Fundraiser Receipt";
181
182/// Label for signing settlement receipts.
183pub const SETTLEMENT_RECEIPT_LABEL: &[u8] = b"Polymesh Settlement Receipt";
184
185/// Chain scoped message for signing and verifying signatures. This is used to prevent signature replay attacks across different chains.
186#[derive(Encode)]
187pub struct ChainScopedMessage<M: Encode> {
188  pub genesis_hash: BlockHash,
189  pub nonce_or_id: u64,
190  pub label: &'static [u8],
191  pub expires_at: Moment,
192  pub message: M,
193}
194
195impl<M: Encode> ChainScopedMessage<M> {
196  /// Create a new [`ChainScopedMessage`] unchecked.
197  pub async fn new(
198    api: &Api,
199    nonce_or_id: u64,
200    label: &'static [u8],
201    expires_at: Option<Moment>,
202    message: M,
203  ) -> Result<ChainScopedMessage<M>> {
204    let genesis_hash = api.client().get_genesis_hash();
205    let expires_at = if let Some(expires_at) = expires_at {
206      expires_at
207    } else {
208      // Default to 1 hour from now.
209      let now = api.query().timestamp().now().await?;
210      now + 3600_000
211    };
212    Ok(ChainScopedMessage {
213      genesis_hash,
214      nonce_or_id,
215      label,
216      expires_at,
217      message,
218    })
219  }
220}