Skip to content

Commit

Permalink
[ABW-3835] Pre-Authorization request models (#238)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* add tests

* [no ci] impl Serialize&Deserialize for SignedPartialTransaction

* WIP

* WIP

* add response (missing Serialize/Deserialize)

* use

* fmt

* decl TXVersion using macro

* add integration tests

* update discriminator

* camelCase response

* add tests

* fixes

* serialize SignedPartialTransaction as hex

* merge fixes

* add tests

* wip

* wip

* wip

* wip

* remove child subintents

* wip

* fix kotlin

* wip

* fix

* wip

* docs

* wip

* updates

* merge fixes

* bump to 1.1.31

* wip

* wip

* update variant name and add docs

* changes after latest updates

* remove code from merge

---------

Co-authored-by: danvleju-rdx <[email protected]>
  • Loading branch information
matiasbzurovski and danvleju-rdx authored Oct 22, 2024
1 parent f2cba03 commit 8a7ade4
Show file tree
Hide file tree
Showing 22 changed files with 556 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,5 +191,27 @@
"networkId": 2,
"origin": "https://dev-sandbox.rdx-works-main.extratools.works/"
}
},
{
"interactionId": "17d530f6-0cb6-4122-8540-64e46a2e0f84",
"items": {
"discriminator": "preAuthorizationRequest",
"request": {
"version": 1,
"transactionManifest": "CALL_METHOD\n Address(\"account_rdx128y6j78mt0aqv6372evz28hrxp8mn06ccddkr7xppc88hyvynvjdwr\")\n \"lock_fee\"\n Decimal(\"0.61\")\n;\nCALL_METHOD\n Address(\"account_rdx128y6j78mt0aqv6372evz28hrxp8mn06ccddkr7xppc88hyvynvjdwr\")\n \"withdraw\"\n Address(\"resource_rdx1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxradxrd\")\n Decimal(\"1337\")\n;\nTAKE_FROM_WORKTOP\n Address(\"resource_rdx1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxradxrd\")\n Decimal(\"1337\")\n Bucket(\"bucket1\")\n;\nCALL_METHOD\n Address(\"account_rdx12xkzynhzgtpnnd02tudw2els2g9xl73yk54ppw8xekt2sdrlaer264\")\n \"try_deposit_or_abort\"\n Bucket(\"bucket1\")\n Enum<0u8>()\n;\n",
"blobs": [],
"message": "message",
"expiration": {
"discriminator": "expireAtTime",
"unixTimestampSeconds": "2023-09-11T16:05:56.000Z"
}
}
},
"metadata": {
"version": 2,
"dAppDefinitionAddress": "account_tdx_2_12xd46c22d6m696lv565t9afn088htudtq275px3qs925ywwty8axze",
"networkId": 2,
"origin": "https://dev-sandbox.rdx-works-main.extratools.works/"
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,13 @@
]
}
}
},
{
"discriminator": "success",
"interactionId": "17d530f6-0cb6-4122-8540-64e46a2e0f84",
"items": {
"discriminator": "preAuthorizationResponse",
"signedPartialTransaction": "replace_actual_encoded_string_here"
}
}
]
6 changes: 5 additions & 1 deletion crates/sargon/src/core/error/common_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,8 +697,12 @@ pub enum CommonError {
#[error("Invalid SignedPartialTransaction, failed to decompile")]
InvalidSignedPartialTransactionFailedToDecompile = 10195,

#[error("Invalid Signed Partial Transaction, failed to encode, reason: '{underlying}'")]
InvalidSignedPartialTransactionFailedToEncode { underlying: String } =
10196,

#[error("Failed to generate manifest summary")]
FailedToGenerateManifestSummary = 10196,
FailedToGenerateManifestSummary = 10197,
}

#[uniffi::export]
Expand Down
2 changes: 2 additions & 0 deletions crates/sargon/src/core/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod rounding_mode;
mod safe_to_log;
mod secret_bytes;
mod signatures;
mod version_type;

pub use appearance_id::*;
pub use appearance_id_uniffi_fn::*;
Expand Down Expand Up @@ -53,3 +54,4 @@ pub use rounding_mode::*;
pub use safe_to_log::*;
pub use secret_bytes::*;
pub use signatures::*;
pub use version_type::*;
96 changes: 96 additions & 0 deletions crates/sargon/src/core/types/version_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::prelude::*;
use paste::paste;

/// A macro that generates a XYZVersion type, which is a typed version of `u64`.
macro_rules! decl_version_type {
($name:ident) => {
paste! {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(transparent)]
pub struct [<$name Version>](u64);

impl HasSampleValues for [<$name Version>] {
fn sample() -> Self {
Self(1)
}

fn sample_other() -> Self {
Self(2)
}
}

impl From<u64> for [<$name Version>] {
fn from(value: u64) -> Self {
Self(value)
}
}

impl Deref for [<$name Version>] {
type Target = u64;

fn deref(&self) -> &Self::Target {
&self.0
}
}

uniffi::custom_newtype!([<$name Version>], u64);
}
};
}

pub(crate) use decl_version_type;

#[cfg(test)]
mod tests {
decl_version_type!(Example);

use crate::prelude::*;

#[test]
fn equality() {
assert_eq!(ExampleVersion::sample(), ExampleVersion::sample());
assert_eq!(
ExampleVersion::sample_other(),
ExampleVersion::sample_other()
);
}

#[test]
fn inequality() {
assert_ne!(ExampleVersion::sample(), ExampleVersion::sample_other());
}

#[test]
fn modification() {
let mut sut: ExampleVersion = 5.into();
assert_eq!(*sut, 5);
assert_eq!(sut.0, 5);

sut.0 = 10;
assert_eq!(*sut, 10);
assert_eq!(sut.0, 10);
}

#[test]
fn json_transparent() {
#[derive(Deserialize, Serialize, PartialEq, Debug)]
struct Test {
name: String,
version: ExampleVersion,
}

let sut = Test {
name: "test".to_string(),
version: ExampleVersion(25),
};
assert_eq_after_json_roundtrip(
&sut,
r#"
{
"name": "test",
"version": 25
}
"#,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub enum DappToWalletInteractionItems {

#[serde(rename = "transaction")]
Transaction(DappToWalletInteractionTransactionItems),

#[serde(rename = "preAuthorizationRequest")]
PreAuthorization(DappToWalletInteractionPreAuthorizationItems),
}

impl HasSampleValues for DappToWalletInteractionItems {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod items;
mod pre_authorization;
mod request;
mod transaction;

pub use items::*;
pub use pre_authorization::*;
pub use request::*;
pub use transaction::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::prelude::*;

/// Suggests that the subintent's expiry timestamp is set to `current_time + expire_after_seconds`
/// at the last moment, right before the intent is fixed for signing.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, uniffi::Record)]
#[serde(rename_all = "camelCase")]
pub struct DappToWalletInteractionSubintentExpireAfterDelay {
/// The time (in seconds) after the subintent is signed that it will expire.
pub expire_after_seconds: u64,
}

impl From<u64> for DappToWalletInteractionSubintentExpireAfterDelay {
fn from(expire_after_seconds: u64) -> Self {
Self {
expire_after_seconds,
}
}
}

impl HasSampleValues for DappToWalletInteractionSubintentExpireAfterDelay {
fn sample() -> Self {
Self {
expire_after_seconds: 10,
}
}

fn sample_other() -> Self {
Self {
expire_after_seconds: 20,
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[allow(clippy::upper_case_acronyms)]
type SUT = DappToWalletInteractionSubintentExpireAfterDelay;

#[test]
fn equality() {
assert_eq!(SUT::sample(), SUT::sample());
assert_eq!(SUT::sample_other(), SUT::sample_other());
}

#[test]
fn inequality() {
assert_ne!(SUT::sample(), SUT::sample_other());
}

#[test]
fn from() {
assert_eq!(SUT::from(10), SUT::sample());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use crate::prelude::*;

/// The subintent expires at a specific fixed timestamp
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, uniffi::Record)]
#[serde(rename_all = "camelCase")]
pub struct DappToWalletInteractionSubintentExpireAtTime {
/// The unix timestamp in seconds when the subintent expires.
pub unix_timestamp_seconds: Timestamp,
}

impl From<Timestamp> for DappToWalletInteractionSubintentExpireAtTime {
fn from(unix_timestamp_seconds: Timestamp) -> Self {
Self {
unix_timestamp_seconds,
}
}
}

impl HasSampleValues for DappToWalletInteractionSubintentExpireAtTime {
fn sample() -> Self {
Self {
unix_timestamp_seconds: Timestamp::sample(),
}
}

fn sample_other() -> Self {
Self {
unix_timestamp_seconds: Timestamp::sample_other(),
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[allow(clippy::upper_case_acronyms)]
type SUT = DappToWalletInteractionSubintentExpireAtTime;

#[test]
fn equality() {
assert_eq!(SUT::sample(), SUT::sample());
assert_eq!(SUT::sample_other(), SUT::sample_other());
}

#[test]
fn inequality() {
assert_ne!(SUT::sample(), SUT::sample_other());
}

#[test]
fn from() {
assert_eq!(SUT::from(Timestamp::sample()), SUT::sample());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::prelude::*;

/// An enum that represents the different ways a subintent can expire.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, uniffi::Enum)]
#[serde(tag = "discriminator", rename_all = "camelCase")]
pub enum DappToWalletInteractionSubintentExpiration {
/// The subintent expires at a specific fixed timestamp.
///
/// For example, a dApp sends a subintent for `User A` to approve sending 100 XRD before 5:00 PM,
/// and a subintent for `User B` to approve sending 2 USDT with same expiration.
///
/// If both users sign their subintents before 5:00 PM, the transaction to exchange
/// 100 XRD over 2 USDT will succeed. Otherwise, it would fail.
#[serde(rename = "expireAtTime")]
AtTime(DappToWalletInteractionSubintentExpireAtTime),

/// The subintent expires X seconds after its signature.
///
/// For example, a dApp sends a subintent for `User A` to approve sending 100 XRD with 1 hour expiration,
/// and a subintent for `User B` to approve sending 2 USDT with same expiration.
///
/// If both users sign their subintents within one hour from each other, the transaction to exchange
/// 100 XRD over 2 USDT will succeed. Otherwise, it would fail.
#[serde(rename = "expireAfterDelay")]
AfterDelay(DappToWalletInteractionSubintentExpireAfterDelay),
}

impl HasSampleValues for DappToWalletInteractionSubintentExpiration {
fn sample() -> Self {
Self::AtTime(DappToWalletInteractionSubintentExpireAtTime::sample())
}

fn sample_other() -> Self {
Self::AfterDelay(
DappToWalletInteractionSubintentExpireAfterDelay::sample(),
)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[allow(clippy::upper_case_acronyms)]
type SUT = DappToWalletInteractionSubintentExpiration;

#[test]
fn equality() {
assert_eq!(SUT::sample(), SUT::sample());
assert_eq!(SUT::sample_other(), SUT::sample_other());
}

#[test]
fn inequality() {
assert_ne!(SUT::sample(), SUT::sample_other());
}

#[test]
fn json_roundtrip() {
assert_eq_after_json_roundtrip(
&SUT::sample(),
r#"
{
"discriminator": "expireAtTime",
"unixTimestampSeconds": "2023-09-11T16:05:56.000Z"
}
"#,
);

assert_eq_after_json_roundtrip(
&SUT::sample_other(),
r#"
{
"discriminator": "expireAfterDelay",
"expireAfterSeconds": 10
}
"#,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod after_delay;
mod at_time;
mod expiration;

pub use after_delay::*;
pub use at_time::*;
pub use expiration::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod expiration;
mod pre_authorization;
mod subintent;

pub use expiration::*;
pub use pre_authorization::*;
pub use subintent::*;
Loading

0 comments on commit 8a7ade4

Please sign in to comment.