diff --git a/.github/workflows/ci-rn.yml b/.github/workflows/ci-rn.yml index a1bd4152..c98a943d 100644 --- a/.github/workflows/ci-rn.yml +++ b/.github/workflows/ci-rn.yml @@ -6,11 +6,13 @@ on: - main paths: - 'packages/sdk-platforms/react-native/react-native-zksync-sso/**' + - '.github/workflows/ci-rn.yml' pull_request: branches: - main paths: - 'packages/sdk-platforms/react-native/react-native-zksync-sso/**' + - '.github/workflows/ci-rn.yml' jobs: build: @@ -97,7 +99,7 @@ jobs: x86_64-apple-ios - name: Install cargo-ndk - run: cargo install cargo-ndk + run: cargo install cargo-ndk --version 3.5.4 - name: Select Xcode 16.3 run: sudo xcode-select -s /Applications/Xcode_16.3.app diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml new file mode 100644 index 00000000..7e8d8db0 --- /dev/null +++ b/.github/workflows/ci-rust.yml @@ -0,0 +1,73 @@ +name: Rust CI + +on: + push: + paths: + - 'packages/contracts/**' + - 'packages/sdk-platforms/**' + - '.github/workflows/ci-rust.yml' + +jobs: + rust-sdk: + name: Rust SDK - latest + runs-on: ubuntu-latest + strategy: + matrix: + config: + - debug + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.4 + + - name: Install Rust + run: | + rustup update stable && rustup default stable + rustup toolchain install nightly + + - name: Run rustfmt + run: | + rustup component add rustfmt --toolchain nightly + cargo +nightly fmt --all -- --check + working-directory: packages/sdk-platforms/rust/zksync-sso + + - name: Install Anvil ZKsync Manually + run: | + SCRIPT_PATH=".github/workflows/scripts/install-anvil-zksync.sh" + chmod +x "$SCRIPT_PATH" + bash "$SCRIPT_PATH" + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.11.0 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: lts/Iron + cache: pnpm + + - name: Install dependencies + run: pnpm install -r --frozen-lockfile + + - name: Install contract dependencies + run: pnpm install -r --frozen-lockfile + working-directory: packages/contracts + + - name: Build contracts + run: pnpm build + working-directory: packages/contracts + + - name: Run clippy + run: | + rustup component add clippy --toolchain stable + cargo clippy --all-targets -- -D warnings + working-directory: packages/sdk-platforms/rust/zksync-sso + + - name: Run rust tests + run: cargo test + working-directory: packages/sdk-platforms/rust/zksync-sso \ No newline at end of file diff --git a/.github/workflows/ci-swift.yml b/.github/workflows/ci-swift.yml index 519868bf..15cc5bce 100644 --- a/.github/workflows/ci-swift.yml +++ b/.github/workflows/ci-swift.yml @@ -5,6 +5,7 @@ on: paths: - 'packages/contracts/**' - 'packages/sdk-platforms/**' + - '.github/workflows/ci-swift.yml' jobs: swift-sdk: @@ -25,19 +26,12 @@ jobs: - name: Install Rust run: | rustup update stable && rustup default stable - rustup toolchain install nightly - - - name: Run rustfmt - run: | - rustup component add rustfmt --toolchain nightly - cargo +nightly fmt --all -- --check - working-directory: packages/sdk-platforms/rust/zksync-sso - name: Install Anvil ZKsync Manually run: | SCRIPT_PATH=".github/workflows/scripts/install-anvil-zksync.sh" chmod +x "$SCRIPT_PATH" - sh "$SCRIPT_PATH" + bash "$SCRIPT_PATH" - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -61,16 +55,6 @@ jobs: run: pnpm build working-directory: packages/contracts - - name: Run clippy - run: | - rustup component add clippy --toolchain stable - cargo clippy --all-targets -- -D warnings - working-directory: packages/sdk-platforms/rust/zksync-sso - - - name: Run rust tests - run: cargo test - working-directory: packages/sdk-platforms/rust/zksync-sso - - name: Start anvil-zksync node run: | anvil-zksync --cache=none run > anvil-zksync.log 2>&1 & @@ -99,6 +83,7 @@ jobs: fi echo "Simulator UDID: $UDID" echo "SIMULATOR_UDID=$UDID" >> $GITHUB_ENV + - name: Install swiftformat run: brew install swiftformat diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce0357ec..b896783c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,17 @@ on: push: branches: - main + paths-ignore: + - 'packages/sdk-platforms/**' + - '.github/workflows/ci-swift.yml' + - '.github/workflows/ci-rust.yml' + - '.github/workflows/ci-rn.yml' pull_request: paths-ignore: - 'packages/sdk-platforms/**' + - '.github/workflows/ci-swift.yml' + - '.github/workflows/ci-rust.yml' + - '.github/workflows/ci-rn.yml' workflow_dispatch: jobs: diff --git a/.github/workflows/scripts/install-anvil-zksync.sh b/.github/workflows/scripts/install-anvil-zksync.sh index be6b5986..8c087eb0 100644 --- a/.github/workflows/scripts/install-anvil-zksync.sh +++ b/.github/workflows/scripts/install-anvil-zksync.sh @@ -4,7 +4,23 @@ set -euo pipefail REPO_URL="https://github.com/matter-labs/anvil-zksync.git" RELEASE_VERSION="v0.6.3" -RELEASE_FILE_NAME="anvil-zksync-${RELEASE_VERSION}-aarch64-apple-darwin.tar.gz" + +# Detect platform +if [[ "$OSTYPE" == "darwin"* ]]; then + RELEASE_FILE_NAME="anvil-zksync-${RELEASE_VERSION}-aarch64-apple-darwin.tar.gz" +elif [[ "$OSTYPE" == "linux-gnu"* ]]; then + ARCH=$(uname -m) + if [[ "$ARCH" == "x86_64" ]]; then + RELEASE_FILE_NAME="anvil-zksync-${RELEASE_VERSION}-x86_64-unknown-linux-gnu.tar.gz" + else + echo "Error: Unsupported Linux architecture: $ARCH" >&2 + exit 1 + fi +else + echo "Error: Unsupported OS: $OSTYPE" >&2 + exit 1 +fi + RELEASE_URL="https://github.com/matter-labs/anvil-zksync/releases/download/${RELEASE_VERSION}/${RELEASE_FILE_NAME}" INSTALL_DIR="/usr/local/bin" TEMP_DIR="$(mktemp -d)" diff --git a/packages/sdk-platforms/react-native/react-native-zksync-sso/example/src/config.json b/packages/sdk-platforms/react-native/react-native-zksync-sso/example/src/config.json index a8df0335..6bafc97f 100644 --- a/packages/sdk-platforms/react-native/react-native-zksync-sso/example/src/config.json +++ b/packages/sdk-platforms/react-native/react-native-zksync-sso/example/src/config.json @@ -1,13 +1,13 @@ { "contracts": { - "accountFactory": "0xda635aa336e8f5c1c3c19e6a0db6d601fa1e3e05", + "accountFactory": "0x7452d866cdbf84762b77a99849f00c26daaf2da8", "passkey": "0xa472581ea2aca6e6bd8ea6cca95d3e1297aa5ae3", - "session": "0x6e2eef457e8a4dc33f6259ff0a933a2f94d64a8e", - "accountPaymaster": "0x18d0069ac0e0431f2af792ec425950427b775f1d", - "recovery": "0xe72da237538a1854535e9565dbee0b414f382698" + "session": "0x11f853c85e282a542d8ee8f9cdd8fe6ce61badf1", + "accountPaymaster": "0x3ec8307648f31c494caeab625e5f2c99bfbaa560", + "recovery": "0x3a34dab058fd71b2fb0d9d9e7d3dd205f9072fc3" }, "nodeUrl": "http://localhost:8011/", "deployWallet": { - "privateKeyHex": "f1010e4119b26f6ceccb5a42b8dc6b2a10650d4f5943e4eb8285951aba264b6c" + "privateKeyHex": "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a" } } \ No newline at end of file diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/cli/src/deploy_contracts.rs b/packages/sdk-platforms/rust/zksync-sso/crates/cli/src/deploy_contracts.rs index 94f012d9..14de42f6 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/cli/src/deploy_contracts.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/cli/src/deploy_contracts.rs @@ -20,7 +20,7 @@ pub async fn deploy_contracts_and_update_example_configs( println!(" ExampleAuthServerPaymaster: {}", contracts.account_paymaster); println!(" Recovery: {}", contracts.recovery); - let deploy_wallet = Some(DeployWallet::random()); + let deploy_wallet = Some(DeployWallet::rich_wallet()); let config = Config::new(contracts, node_url.clone(), deploy_wallet); for path in config_paths { diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account.rs index 9cd1723e..873291de 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account.rs @@ -2,9 +2,11 @@ mod balance; mod deployment; mod fetch; mod fund; +mod owners; mod send; mod session; mod transaction; +mod validators; #[derive(Debug, Clone, uniffi::Record)] pub struct Account { diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/deployment.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/deployment.rs index da957570..d78c3ad6 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/deployment.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/deployment.rs @@ -1,4 +1,14 @@ use crate::config; +use sdk::api::{ + account::{ + passkey::{ + passkey_parameters::PasskeyParameters as SdkPasskeyParameters, + rp_id::{AndroidRpId as SdkAndroidRpId, RpId as SdkRpId}, + }, + session::decode_session_config, + }, + utils::parse_address, +}; #[derive(Debug, uniffi::Record)] pub struct AndroidRpId { @@ -6,9 +16,9 @@ pub struct AndroidRpId { pub rp_id: String, } -impl From for sdk::api::account::deployment::AndroidRpId { +impl From for SdkAndroidRpId { fn from(android_rp_id: AndroidRpId) -> Self { - sdk::api::account::deployment::AndroidRpId { + SdkAndroidRpId { origin: android_rp_id.origin, rp_id: android_rp_id.rp_id, } @@ -45,16 +55,12 @@ impl RpId { } } -impl From for sdk::api::account::deployment::RpId { +impl From for SdkRpId { fn from(rp_id: RpId) -> Self { match rp_id { - RpId::Apple(rp_id) => { - sdk::api::account::deployment::RpId::Apple(rp_id) - } + RpId::Apple(rp_id) => SdkRpId::Apple(rp_id), RpId::Android(android_rp_id) => { - sdk::api::account::deployment::RpId::Android( - android_rp_id.into(), - ) + SdkRpId::Android(android_rp_id.into()) } } } @@ -68,11 +74,9 @@ pub struct PasskeyParameters { pub rp_id: RpId, } -impl From - for sdk::api::account::deployment::PasskeyParameters -{ +impl From for SdkPasskeyParameters { fn from(passkey_parameters: PasskeyParameters) -> Self { - sdk::api::account::deployment::PasskeyParameters { + SdkPasskeyParameters { credential_raw_attestation_object: passkey_parameters .credential_raw_attestation_object, credential_raw_client_data_json: passkey_parameters @@ -103,15 +107,44 @@ pub enum DeployAccountError { #[error("Account already deployed")] AccountAlreadyDeployed, + + #[error("Invalid session config: {0}")] + InvalidSessionConfig(String), + + #[error("Invalid K1 owners: {0}")] + InvalidK1Owners(String), } #[uniffi::export(async_runtime = "tokio")] pub async fn deploy_account( passkey_parameters: PasskeyParameters, + initial_k1_owners: Option>, + initial_session_config_json: Option, config: config::Config, ) -> Result { + let initial_k1_owners = initial_k1_owners + .map(|k1_owners| { + k1_owners + .into_iter() + .map(|k1_owner| parse_address(&k1_owner)) + .collect::, _>>() + }) + .transpose() + .map_err(|e| DeployAccountError::InvalidK1Owners(e.to_string()))?; + + let initial_session = initial_session_config_json + .map(|session_config| { + decode_session_config(&session_config).map_err(|e| { + DeployAccountError::InvalidSessionConfig(e.to_string()) + }) + }) + .transpose() + .map_err(|e| DeployAccountError::InvalidSessionConfig(e.to_string()))?; + sdk::api::account::deployment::deploy_account( passkey_parameters.into(), + initial_k1_owners, + initial_session, &(config.try_into() as Result) .map_err(|e: config::ConfigError| { @@ -127,11 +160,34 @@ pub async fn deploy_account( pub async fn deploy_account_with_unique_id( passkey_parameters: PasskeyParameters, unique_account_id: String, + initial_k1_owners: Option>, + initial_session_config_json: Option, config: config::Config, ) -> Result { + let initial_k1_owners = initial_k1_owners + .map(|k1_owners| { + k1_owners + .into_iter() + .map(|k1_owner| parse_address(&k1_owner)) + .collect::, _>>() + }) + .transpose() + .map_err(|e| DeployAccountError::InvalidK1Owners(e.to_string()))?; + + let initial_session = initial_session_config_json + .map(|session_config| { + decode_session_config(&session_config).map_err(|e| { + DeployAccountError::InvalidSessionConfig(e.to_string()) + }) + }) + .transpose() + .map_err(|e| DeployAccountError::InvalidSessionConfig(e.to_string()))?; + sdk::api::account::deployment::deploy_account_with_unique_id( passkey_parameters.into(), unique_account_id, + initial_k1_owners, + initial_session, &(config.try_into() as Result) .map_err(|e: config::ConfigError| { diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/owners.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/owners.rs new file mode 100644 index 00000000..704366ba --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/owners.rs @@ -0,0 +1,45 @@ +use crate::config; +use sdk::api::{ + account::owners::is_k1_owner as is_k1_owner_sdk, utils::parse_address, +}; + +#[derive(Debug, uniffi::Record)] +pub struct IsK1OwnerArgs { + pub account: String, + pub owner_address: String, +} + +#[derive(Debug, thiserror::Error, uniffi::Error)] +pub enum IsK1OwnerError { + #[error("{0}")] + IsK1Owner(String), + #[error("Invalid address: {0}")] + InvalidAccountAddress(String), + #[error("Invalid owner address: {0}")] + InvalidOwnerAddress(String), + #[error("Invalid config: {0}")] + InvalidConfig(String), +} + +#[uniffi::export(async_runtime = "tokio")] +pub async fn is_k1_owner( + args: IsK1OwnerArgs, + config: config::Config, +) -> Result { + let account = parse_address(&args.account) + .map_err(|e| IsK1OwnerError::InvalidAccountAddress(e.to_string()))?; + let owner_address = parse_address(&args.owner_address) + .map_err(|e| IsK1OwnerError::InvalidOwnerAddress(e.to_string()))?; + let result = is_k1_owner_sdk( + account, + owner_address, + &(config.try_into() + as Result) + .map_err(|e: config::ConfigError| { + IsK1OwnerError::InvalidConfig(e.to_string()) + })?, + ) + .await + .map_err(|e| IsK1OwnerError::IsK1Owner(e.to_string()))?; + Ok(result) +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session.rs index bca9a5e1..b17632f3 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session.rs @@ -1,2 +1,5 @@ pub mod client; pub mod create; +pub mod hash; +pub mod revoke; +pub mod state; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/create.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/create.rs index 2e77b8b6..90e72018 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/create.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/create.rs @@ -41,8 +41,10 @@ pub enum CreateSessionError { InvalidAddress(String), #[error("Invalid session config: {0}")] InvalidSessionConfig(String), - #[error("Invalid session key: {0}")] - InvalidSessionKey(String), + #[error("Invalid config: {0}")] + InvalidConfig(String), + #[error("Invalid private key: {0}")] + InvalidPrivateKey(String), #[error("Invalid paymaster: {0}")] InvalidPaymaster(String), } @@ -68,7 +70,7 @@ pub async fn create_session( let args = SdkCreateSessionArgs { account, session_config, paymaster }; let sign_fn = sign_fn_from_private_key_hex(owner_private_key) - .map_err(|e| CreateSessionError::CreateSession(e.to_string()))?; + .map_err(|e| CreateSessionError::InvalidPrivateKey(e.to_string()))?; let result = sdk::api::account::session::create::create_session( args, @@ -76,7 +78,7 @@ pub async fn create_session( &(config.try_into() as Result) .map_err(|e: config::ConfigError| { - CreateSessionError::CreateSession(e.to_string()) + CreateSessionError::InvalidConfig(e.to_string()) })?, ) .await diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/hash.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/hash.rs new file mode 100644 index 00000000..258ad9be --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/hash.rs @@ -0,0 +1,26 @@ +use sdk::api::account::session::{ + decode_session_config, hash::get_session_hash as sdk_get_session_hash, +}; + +#[derive(Debug, thiserror::Error, uniffi::Error)] +pub enum GetSessionHashError { + #[error("{0}")] + GetSessionHash(String), + #[error("Invalid session config: {0}")] + InvalidSessionConfig(String), +} + +#[uniffi::export] +pub fn get_session_hash( + session_config_json: String, +) -> Result { + let session_config = + decode_session_config(&session_config_json).map_err(|e| { + GetSessionHashError::InvalidSessionConfig(e.to_string()) + })?; + + let hash = sdk_get_session_hash(session_config) + .map_err(|e| GetSessionHashError::GetSessionHash(e.to_string()))?; + + Ok(format!("{:#x}", hash.fixed_bytes())) +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/revoke.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/revoke.rs new file mode 100644 index 00000000..3e09014b --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/revoke.rs @@ -0,0 +1,67 @@ +use crate::config; +use sdk::api::{ + account::session::revoke::RevokeSessionArgs as SdkRevokeSessionArgs, + utils::{parse_address, sign_fn_from_private_key_hex}, +}; + +#[derive(Debug, uniffi::Record)] +pub struct RevokeSessionArgs { + pub account: String, + pub session_id: String, + pub owner_private_key: String, +} + +#[derive(Debug, uniffi::Record)] +pub struct RevokeSessionReturnType { + pub transaction_receipt_json: String, +} + +impl From + for sdk::api::account::session::revoke::RevokeSessionReturnType +{ + fn from(revoke_session_return_type: RevokeSessionReturnType) -> Self { + sdk::api::account::session::revoke::RevokeSessionReturnType { + transaction_receipt_json: revoke_session_return_type + .transaction_receipt_json, + } + } +} + +#[derive(Debug, thiserror::Error, uniffi::Error)] +pub enum RevokeSessionError { + #[error("{0}")] + RevokeSession(String), + #[error("Invalid address: {0}")] + InvalidAddress(String), +} + +#[uniffi::export(async_runtime = "tokio")] +pub async fn revoke_session( + args: RevokeSessionArgs, + config: config::Config, +) -> Result { + let account_address = parse_address(&args.account) + .map_err(|e| RevokeSessionError::InvalidAddress(e.to_string()))?; + + let sign_fn = sign_fn_from_private_key_hex(&args.owner_private_key) + .map_err(|e| RevokeSessionError::RevokeSession(e.to_string()))?; + + let sdk_args = SdkRevokeSessionArgs { session_id: args.session_id }; + + let result = sdk::api::account::session::revoke::revoke_session( + sdk_args, + account_address, + sign_fn, + &(config.try_into() + as Result) + .map_err(|e: config::ConfigError| { + RevokeSessionError::RevokeSession(e.to_string()) + })?, + ) + .await + .map_err(|e| RevokeSessionError::RevokeSession(e.to_string()))?; + + Ok(RevokeSessionReturnType { + transaction_receipt_json: result.transaction_receipt_json, + }) +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/state.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/state.rs new file mode 100644 index 00000000..fc0ec314 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/session/state.rs @@ -0,0 +1,60 @@ +use crate::config; +use sdk::api::{ + account::session::{ + decode_session_config, + state::GetSessionStateArgs as SdkGetSessionStateArgs, + }, + utils::parse_address, +}; + +#[derive(Debug, uniffi::Record)] +pub struct GetSessionStateArgs { + pub account: String, + pub session_config: String, +} + +#[derive(Debug, uniffi::Record)] +pub struct GetSessionStateReturnType { + pub session_state_json: String, +} + +#[derive(Debug, thiserror::Error, uniffi::Error)] +pub enum GetSessionStateError { + #[error("{0}")] + GetSessionState(String), + #[error("Invalid address: {0}")] + InvalidAddress(String), + #[error("Invalid session config: {0}")] + InvalidSessionConfig(String), +} + +#[uniffi::export(async_runtime = "tokio")] +pub async fn get_session_state( + args: GetSessionStateArgs, + config: config::Config, +) -> Result { + let session_config = + decode_session_config(&args.session_config).map_err(|e| { + GetSessionStateError::InvalidSessionConfig(e.to_string()) + })?; + let account = parse_address(&args.account) + .map_err(|e| GetSessionStateError::InvalidAddress(e.to_string()))?; + + let sdk_args = SdkGetSessionStateArgs { account, session_config }; + + let result = sdk::api::account::session::state::get_session_state( + sdk_args, + &(config.try_into() + as Result) + .map_err(|e: config::ConfigError| { + GetSessionStateError::GetSessionState(e.to_string()) + })?, + ) + .await + .map_err(|e| GetSessionStateError::GetSessionState(e.to_string()))?; + + let session_state_json = serde_json::to_string(&result.session_state) + .map_err(|e| GetSessionStateError::GetSessionState(e.to_string()))?; + + Ok(GetSessionStateReturnType { session_state_json }) +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/validators.rs b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/validators.rs new file mode 100644 index 00000000..fe73ce26 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/ffi/src/account/validators.rs @@ -0,0 +1,48 @@ +use crate::config; +use sdk::api::{ + account::validators::is_module_validator as is_module_validator_sdk, + utils::parse_address, +}; + +#[derive(Debug, uniffi::Record)] +pub struct IsModuleValidatorArgs { + pub account: String, + pub module_address: String, +} + +#[derive(Debug, thiserror::Error, uniffi::Error)] +pub enum IsModuleValidatorError { + #[error("{0}")] + IsModuleValidator(String), + #[error("Invalid address: {0}")] + InvalidAccountAddress(String), + #[error("Invalid module address: {0}")] + InvalidModuleAddress(String), + #[error("Invalid config: {0}")] + InvalidConfig(String), +} + +#[uniffi::export(async_runtime = "tokio")] +pub async fn is_module_validator( + args: IsModuleValidatorArgs, + config: config::Config, +) -> Result { + let account = parse_address(&args.account).map_err(|e| { + IsModuleValidatorError::InvalidAccountAddress(e.to_string()) + })?; + let module_address = parse_address(&args.module_address).map_err(|e| { + IsModuleValidatorError::InvalidModuleAddress(e.to_string()) + })?; + let result = is_module_validator_sdk( + account, + module_address, + &(config.try_into() + as Result) + .map_err(|e: config::ConfigError| { + IsModuleValidatorError::InvalidConfig(e.to_string()) + })?, + ) + .await + .map_err(|e| IsModuleValidatorError::IsModuleValidator(e.to_string()))?; + Ok(result) +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account.rs index d5a8edd7..6632a38c 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account.rs @@ -5,9 +5,12 @@ pub mod balance; pub mod deployment; pub mod fetch; pub mod fund; +pub mod owners; +pub mod passkey; pub mod send; pub mod session; pub mod transaction; +pub mod validators; #[derive(Debug, Clone)] pub struct Account { diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/deployment.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/deployment.rs index a4cff53a..f4ffd5ac 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/deployment.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/deployment.rs @@ -1,52 +1,17 @@ use crate::{ + api::account::passkey::passkey_parameters::{ + PasskeyParameters, parse_passkey_parameters, + }, client::passkey::{ account_factory::{AccountParams, create_account}, actions::deploy::CredentialDetails, }, config::Config, + utils::session::session_lib::session_spec::SessionSpec, }; use alloy::primitives::{Address, Bytes}; use alloy_zksync::network::unsigned_tx::eip712::PaymasterParams; use log::debug; -use parse_passkey_parameters::parse_passkey_parameters; - -pub mod parse_passkey_parameters; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AndroidRpId { - pub origin: String, - pub rp_id: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RpId { - Apple(String), - Android(AndroidRpId), -} - -impl RpId { - pub fn origin(&self) -> String { - match self { - RpId::Apple(rp_id) => format!("https://{rp_id}"), - RpId::Android(android_rp_id) => android_rp_id.origin.to_string(), - } - } - - pub fn rp_id(&self) -> String { - match self { - RpId::Apple(rp_id) => rp_id.to_string(), - RpId::Android(android_rp_id) => android_rp_id.rp_id.to_string(), - } - } -} - -#[derive(Debug, Clone)] -pub struct PasskeyParameters { - pub credential_raw_attestation_object: Vec, - pub credential_raw_client_data_json: Vec, - pub credential_id: Vec, - pub rp_id: RpId, -} pub struct DeployedAccountDetails { pub address: Address, @@ -56,6 +21,8 @@ pub struct DeployedAccountDetails { pub async fn deploy_account( passkey_parameters: PasskeyParameters, + initial_k1_owners: Option>, + initial_session: Option, config: &Config, ) -> eyre::Result { debug!("XDB deploy_account - passkey_parameters: {passkey_parameters:?}"); @@ -78,6 +45,8 @@ pub async fn deploy_account( expected_origin: Some(parsed_params.expected_origin), contracts: config.contracts, paymaster, + initial_k1_owners, + initial_session, ..Default::default() }; @@ -107,6 +76,8 @@ pub async fn deploy_account( pub async fn deploy_account_with_unique_id( passkey_parameters: PasskeyParameters, unique_account_id: String, + initial_k1_owners: Option>, + initial_session: Option, config: &Config, ) -> eyre::Result { let parsed_params = parse_passkey_parameters(&passkey_parameters).await?; @@ -127,6 +98,8 @@ pub async fn deploy_account_with_unique_id( &AccountParams { passkey_expected_origin: parsed_params.expected_origin, }, + initial_k1_owners, + initial_session, paymaster, config, ) diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/fund.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/fund.rs index 34345e42..7be82924 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/fund.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/fund.rs @@ -1,10 +1,17 @@ -use crate::{config::Config, utils::alloy::extensions::ProviderExt}; +use crate::{ + config::Config, + utils::{ + alloy::extensions::ProviderExt, anvil_zksync::rich_wallet::RichWallet, + }, +}; use alloy::{ network::TransactionBuilder, primitives::{Address, U256}, providers::Provider, }; -use alloy_zksync::network::transaction_request::TransactionRequest; +use alloy_zksync::{ + network::transaction_request::TransactionRequest, provider::zksync_provider, +}; use log::debug; use money::Money; use rand::Rng; @@ -18,19 +25,7 @@ pub async fn fund_account( debug!("XDB fund_account - address: {address:?}"); let provider = { - use alloy::signers::local::PrivateKeySigner; - use alloy_zksync::{provider::zksync_provider, wallet::ZksyncWallet}; - pub const RICH_WALLET_PRIVATE_KEY_2: &str = "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"; - - fn zksync_wallet_3() -> eyre::Result { - let signer = - RICH_WALLET_PRIVATE_KEY_2.parse::()?; - let zksync_wallet = ZksyncWallet::from(signer); - Ok(zksync_wallet) - } - - let wallet = zksync_wallet_3()?; - + let wallet = RichWallet::two().to_zksync_wallet()?; zksync_provider() .with_recommended_fillers() .wallet(wallet) diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/owners.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/owners.rs new file mode 100644 index 00000000..b980d924 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/owners.rs @@ -0,0 +1,13 @@ +use crate::{ + client::modular_account::is_k1_owner as is_k1_owner_client, config::Config, +}; +use alloy::primitives::Address; + +#[allow(dead_code)] +pub async fn is_k1_owner( + account_address: Address, + owner_address: Address, + config: &Config, +) -> eyre::Result { + is_k1_owner_client(account_address, owner_address, config).await +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey.rs new file mode 100644 index 00000000..184d1a2c --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey.rs @@ -0,0 +1,2 @@ +pub mod passkey_parameters; +pub mod rp_id; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/deployment/parse_passkey_parameters.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey/passkey_parameters.rs similarity index 93% rename from packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/deployment/parse_passkey_parameters.rs rename to packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey/passkey_parameters.rs index 803e4870..54112cd5 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/deployment/parse_passkey_parameters.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey/passkey_parameters.rs @@ -1,9 +1,19 @@ -use super::PasskeyParameters; -use crate::utils::passkey::authenticators::{ - apple::extract_public_key, verify::verify_registration, +use crate::{ + api::account::passkey::rp_id::RpId, + utils::passkey::authenticators::{ + apple::extract_public_key, verify::verify_registration, + }, }; use log::debug; +#[derive(Debug, Clone)] +pub struct PasskeyParameters { + pub credential_raw_attestation_object: Vec, + pub credential_raw_client_data_json: Vec, + pub credential_id: Vec, + pub rp_id: RpId, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParsedPasskeyParametersCredential { pub id: String, @@ -94,7 +104,7 @@ pub(crate) async fn parse_passkey_parameters( #[cfg(test)] mod tests { use super::*; - use crate::api::account::deployment::{AndroidRpId, RpId}; + use crate::api::account::passkey::rp_id::{AndroidRpId, RpId}; use base64::Engine; use eyre::Ok; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey/rp_id.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey/rp_id.rs new file mode 100644 index 00000000..65b0a19a --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/passkey/rp_id.rs @@ -0,0 +1,27 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AndroidRpId { + pub origin: String, + pub rp_id: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RpId { + Apple(String), + Android(AndroidRpId), +} + +impl RpId { + pub fn origin(&self) -> String { + match self { + RpId::Apple(rp_id) => format!("https://{rp_id}"), + RpId::Android(android_rp_id) => android_rp_id.origin.to_string(), + } + } + + pub fn rp_id(&self) -> String { + match self { + RpId::Apple(rp_id) => rp_id.to_string(), + RpId::Android(android_rp_id) => android_rp_id.rp_id.to_string(), + } + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/session/revoke.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/session/revoke.rs index 588d635b..31223e4c 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/session/revoke.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/session/revoke.rs @@ -61,3 +61,293 @@ pub fn private_key_to_address(private_key_hex: &str) -> eyre::Result
{ let address = wallet.default_signer().address(); Ok(address) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + api::account::fund::fund_account, + client::{ + modular_account::is_module_validator, + session::actions::session::{ + hash::get_session_hash, + state::{GetSessionStateArgs, get_session_state}, + }, + }, + config::deploy_wallet::DeployWallet, + utils::{ + anvil_zksync::rich_wallet::RichWallet, + session::session_lib::session_spec::{ + SessionSpec, limit_type::LimitType, + transfer_spec::TransferSpec, usage_limit::UsageLimit, + }, + test_utils::{ + passkey::get_mock_credential_details, + spawn_node_and_deploy_contracts, + }, + }, + }; + use alloy::{ + primitives::{FixedBytes, U256, address, hex}, + providers::Provider, + }; + use alloy_zksync::provider::zksync_provider; + use url; + + #[tokio::test] + async fn test_create_and_revoke_session() -> eyre::Result<()> { + // Add delay to avoid test run timing out + tokio::time::sleep(tokio::time::Duration::from_secs(15)).await; + + // Arrange + let (anvil_zksync, mut config, _) = + spawn_node_and_deploy_contracts().await?; + + let private_key = RichWallet::four().private_key_hex(); + + // Owner private key for ECDSA smart account client + let owner_private_key = "0x5de4111afa1a4b94908f83103c3a57e0c3c9e9da2dd5a02a84e9fde30d7e96c3"; + + config.deploy_wallet = + Some(DeployWallet { private_key_hex: private_key.to_string() }); + + let owner_address = private_key_to_address(owner_private_key)?; + println!("owner_address: {owner_address:?}"); + let expected_owner_address = + address!("0x6a34Ea49c29BF7Cce95F51E7F0f419831Ad5dBC6"); + eyre::ensure!( + owner_address == expected_owner_address, + "owner_address does not match expected address, expected: {:?}, received: {:?}", + expected_owner_address, + owner_address + ); + + let transfer_session_target = + address!("0xdeBbD4CE2Bd6BD869D3ac93666A0D5F4fc06FC72"); + let session_owner_address = + address!("0x9BbC92a33F193174bf6Cc09c4b4055500d972479"); + let random_salt_str = "sdk-test-factory-replication-010"; + let random_salt = + alloy::primitives::keccak256(random_salt_str.as_bytes()); // Unique ID for deterministic salt + println!("random_salt: 0x{}", hex::encode(random_salt)); + let expires_at = 1749040108u64; + + // Create provider for contract calls + let public_provider = { + let node_url: url::Url = config.clone().node_url; + zksync_provider().with_recommended_fillers().on_http(node_url) + }; + + // Create the exact same session configuration as the original test + let exact_session_config = SessionSpec { + signer: session_owner_address, + expires_at: U256::from(expires_at), + fee_limit: UsageLimit { + limit_type: LimitType::Lifetime, + limit: U256::from(100000000000000000u64), // 0.1 ETH + period: U256::from(0), + }, + call_policies: vec![], // Empty array same as original + transfer_policies: vec![TransferSpec { + target: transfer_session_target, + max_value_per_use: U256::from(10000000000000000u64), // 0.01 ETH + value_limit: UsageLimit { + limit_type: LimitType::Unlimited, + limit: U256::from(0), + period: U256::from(0), + }, + }], + }; + + // Step 1: Deploy account WITH initial session + let deployed_account_address = { + use crate::client::passkey::actions::deploy::{ + DeployAccountArgs, deploy_account, + }; + use alloy::primitives::Bytes; + use alloy_zksync::network::unsigned_tx::eip712::PaymasterParams; + + let args = { + let deploy_account_credential = get_mock_credential_details(); + + let unique_account_id = Some(random_salt_str.to_string()); + + let contracts = config.clone().contracts; + + let origin: String = "https://example.com".to_string(); + + let paymaster = Some(PaymasterParams { + paymaster: contracts.account_paymaster, + paymaster_input: Bytes::new(), + }); + + DeployAccountArgs { + credential: deploy_account_credential, + expected_origin: Some(origin), + unique_account_id, + paymaster, + contracts, + initial_k1_owners: Some(vec![owner_address]), + initial_session: Some(exact_session_config.clone()), + } + }; + + let result = deploy_account(args, &config).await?; + + let deployed_account_address = result.address; + + println!( + "XDB - test_deploy_account - Deployed account address: {deployed_account_address}" + ); + + deployed_account_address + }; + + println!("Account deployed successfully!"); + println!(" Deployed address: {deployed_account_address}"); + + // Step 2: Verify session module is a validator + println!("\n--- Step 2: Verifying session module is a validator ---"); + + let is_module_validator = is_module_validator( + deployed_account_address, + config.contracts.session, + &config, + ) + .await?; + + println!("Session module is validator: {is_module_validator}"); + + eyre::ensure!( + is_module_validator, + "Session module should be a validator" + ); + + // Step 3: Get initial session state + println!("\n--- Step 3: Getting initial session state ---"); + let initial_session_state = get_session_state( + GetSessionStateArgs { + account: deployed_account_address, + session_config: exact_session_config.clone(), + }, + &config, + ) + .await?; + + // Verify the session is active + eyre::ensure!( + initial_session_state.session_state.is_active(), + "Initial session should be active (status=1)" + ); + + // Step 4: Calculate and verify session hash + println!("\n--- Step 4: Calculating session hash ---"); + let session_hash = get_session_hash(exact_session_config.clone())?; + println!("Session hash: 0x{}", hex::encode(session_hash.fixed_bytes())); + + let expected_session_hash: FixedBytes<32> = { + let expected_session_hash = hex::decode( + "c424e4a2319b9e449d85c13d6511e63eb383fb975dc68a96d5d7fcdcbbce675a", + )?; + FixedBytes::from_slice(&expected_session_hash) + }; + eyre::ensure!( + session_hash == expected_session_hash.into(), + "Session hash does not match expected value, expected: {expected_session_hash:?}, received: {session_hash:?}", + ); + + // Step 5: Fund the smart account and test session revocation + println!( + "\n--- Step 5: Fund smart account and test session revocation ---" + ); + + // Fund the smart account for transaction fees (1 ETH) + println!("Funding smart account for transaction fees..."); + let funding_amount = U256::from(1000000000000000000u64); // 1 ETH + fund_account(deployed_account_address, funding_amount, &config).await?; + + // Check smart account balance + let account_balance = + public_provider.get_balance(deployed_account_address).await?; + println!("Smart account balance: {account_balance} wei"); + println!( + "Smart account balance: {:.6} ETH", + f64::from(account_balance) / 1e18 + ); + let expected_account_balance = U256::from(1000000000000000000u64); + eyre::ensure!( + account_balance == expected_account_balance, + "Smart account balance should be 1 ETH:\n expected: {:?}\n received: {:?}", + expected_account_balance, + account_balance + ); + + println!(" Smart account address: {deployed_account_address}"); + println!(" Using owner private key for revocation"); + println!( + " Session hash to revoke: 0x{}", + hex::encode(session_hash.fixed_bytes()) + ); + + // Revoke the initial session + println!("Attempting to revoke session using owner's credentials..."); + + let session_hash_str = hex::encode(session_hash.fixed_bytes()); + + let revoke_args = RevokeSessionArgs { session_id: session_hash_str }; + + let signer = alloy::signers::local::PrivateKeySigner::from_str( + owner_private_key, + )?; + let sign_fn = + crate::client::session::actions::session::send::sign_fn_from_signer( + signer, + ); + let revoke_result = revoke_session( + revoke_args, + deployed_account_address, + sign_fn, + &config, + ) + .await?; + + println!("Session revocation successful:"); + println!( + " Transaction receipt json: {}", + revoke_result.transaction_receipt_json + ); + + // Step 6: Verify session is now revoked + println!("\n--- Step 6: Verifying session is revoked ---"); + let revoked_session_state = get_session_state( + GetSessionStateArgs { + account: deployed_account_address, + session_config: exact_session_config.clone(), + }, + &config, + ) + .await?; + + println!("Session state after revocation:"); + println!( + " Status: {:?} (2 = Closed/Revoked)", + revoked_session_state.session_state.status + ); + println!( + " Fees remaining: {:?}", + revoked_session_state.session_state.fees_remaining + ); + + // Verify session is now closed/revoked (status = 2) + eyre::ensure!( + revoked_session_state.session_state.is_closed(), + "Session should be closed/revoked (status=2)" + ); + + println!("✓ Session successfully revoked"); + + drop(anvil_zksync); + + Ok(()) + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/validators.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/validators.rs new file mode 100644 index 00000000..4e0e30cd --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/api/account/validators.rs @@ -0,0 +1,13 @@ +use crate::{ + client::modular_account::is_module_validator as is_module_validator_client, + config::Config, +}; +use alloy::primitives::Address; + +pub async fn is_module_validator( + account_address: Address, + module_address: Address, + config: &Config, +) -> eyre::Result { + is_module_validator_client(account_address, module_address, config).await +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/ecdsa/actions/deploy.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/ecdsa/actions/deploy.rs index 23aeeded..58a51a1c 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/ecdsa/actions/deploy.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/ecdsa/actions/deploy.rs @@ -323,6 +323,9 @@ mod tests { #[tokio::test] async fn test_deploy_account_with_initial_k1_owners_and_send_transaction() -> Result<()> { + // Add delay to avoid test run timing out + tokio::time::sleep(tokio::time::Duration::from_secs(15)).await; + // Arrange let (anvil_zksync, config, _) = spawn_node_and_deploy_contracts().await?; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/modular_account.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/modular_account.rs index eec43b3e..719b67c6 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/modular_account.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/modular_account.rs @@ -1,7 +1,7 @@ use crate::{ client::passkey::actions::deploy::CredentialDetails, config::Config, - contracts::AAFactory, + contracts::{AAFactory, SsoAccount}, utils::{ alloy::extensions::ProviderExt, contract_deployed::{Contract, check_contract_deployed}, @@ -348,6 +348,35 @@ pub fn hash_unique_account_id( Ok(hash) } +pub async fn is_module_validator( + account_address: Address, + module_address: Address, + config: &Config, +) -> eyre::Result { + let public_provider = { + let node_url: url::Url = config.clone().node_url; + zksync_provider().with_recommended_fillers().on_http(node_url) + }; + let account_contract = SsoAccount::new(account_address, &public_provider); + let is_module_validator = + account_contract.isModuleValidator(module_address).call().await?._0; + Ok(is_module_validator) +} + +pub async fn is_k1_owner( + account_address: Address, + owner_address: Address, + config: &Config, +) -> eyre::Result { + let public_provider = { + let node_url: url::Url = config.clone().node_url; + zksync_provider().with_recommended_fillers().on_http(node_url) + }; + let account_contract = SsoAccount::new(account_address, &public_provider); + let is_owner = account_contract.isK1Owner(owner_address).call().await?._0; + Ok(is_owner) +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/account_factory.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/account_factory.rs index 5182fab0..ac64bd9b 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/account_factory.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/account_factory.rs @@ -4,7 +4,10 @@ use crate::{ }, config::Config, contracts::AAFactory, - utils::contract_deployed::{Contract, check_contract_deployed}, + utils::{ + contract_deployed::{Contract, check_contract_deployed}, + session::session_lib::session_spec::SessionSpec, + }, }; use alloy::{ primitives::{Address, Bytes, FixedBytes, keccak256}, @@ -173,6 +176,8 @@ async fn deploy_smart_account( user_id: String, credential: &CredentialDetails, account_params: &AccountParams, + initial_k1_owners: Option>, + initial_session: Option, paymaster: Option, config: &Config, ) -> eyre::Result
{ @@ -187,8 +192,8 @@ async fn deploy_smart_account( unique_account_id: Some(user_id.clone()), paymaster, contracts, - initial_k1_owners: None, - initial_session: None, + initial_k1_owners, + initial_session, }; let result = deploy_account(args, config).await?; @@ -213,6 +218,8 @@ pub async fn create_account( user_id: String, credential: CredentialDetails, account_params: &AccountParams, + initial_k1_owners: Option>, + initial_session: Option, paymaster: Option, config: &Config, ) -> eyre::Result
{ @@ -239,6 +246,8 @@ pub async fn create_account( user_id.clone(), &credential, account_params, + initial_k1_owners, + initial_session, paymaster, config, ) @@ -304,6 +313,8 @@ mod tests { user_id.clone(), credential.clone(), &account_params, + None, + None, paymaster, &config, ) @@ -320,6 +331,8 @@ mod tests { credential.clone(), &account_params, None, + None, + None, &config, ) .await?; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/deploy.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/deploy.rs index abe011b9..970233fd 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/deploy.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/deploy.rs @@ -332,6 +332,9 @@ mod tests { #[tokio::test] async fn test_deploy_account() -> Result<()> { + // Add delay to avoid test run timing out + tokio::time::sleep(tokio::time::Duration::from_secs(15)).await; + // Arrange let (anvil_zksync, config, _) = spawn_node_and_deploy_contracts().await?; @@ -391,6 +394,9 @@ mod tests { #[tokio::test] async fn test_deploy_account_with_initial_k1_owners_and_send_transaction() -> Result<()> { + // Add delay to avoid test run timing out + tokio::time::sleep(tokio::time::Duration::from_secs(15)).await; + // Arrange let (anvil_zksync, config, _) = spawn_node_and_deploy_contracts().await?; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/send/sign.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/send/sign.rs index b1c33201..7733b146 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/send/sign.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/passkey/actions/send/sign.rs @@ -14,12 +14,6 @@ use tokio::sync::Mutex; pub mod hash_signature_response; -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub struct SendTransactionResult { - pub tx_hash: String, - pub receipt: String, -} - pub struct SignerWithMessage { sign_message: F, } diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/create.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/create.rs index 53f5fdc5..e9dd1bfe 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/create.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/create.rs @@ -115,10 +115,11 @@ pub(crate) async fn create_session_with_send_fn( mod tests { use super::*; use crate::{ + api::account::fund::fund_account, client::{ modular_account::{ DeployModularAccountArgs, SessionModuleArgs, - deploy_modular_account, + deploy_modular_account, is_module_validator, }, session::actions::session::{ hash::get_session_hash, @@ -127,9 +128,8 @@ mod tests { }, }, config::deploy_wallet::DeployWallet, - contracts::SsoAccount, utils::{ - alloy::extensions::ProviderExt, + anvil_zksync::rich_wallet::RichWallet, session::session_lib::session_spec::{ SessionSpec, limit_type::LimitType, transfer_spec::TransferSpec, usage_limit::UsageLimit, @@ -141,18 +141,14 @@ mod tests { }, }; use alloy::{ - network::{ReceiptResponse, TransactionBuilder}, + network::ReceiptResponse, primitives::{FixedBytes, U256, address, hex}, providers::Provider, signers::local::PrivateKeySigner, }; use alloy_zksync::{ - network::{ - transaction_request::TransactionRequest, - unsigned_tx::eip712::PaymasterParams, - }, - provider::zksync_provider, - wallet::ZksyncWallet, + network::unsigned_tx::eip712::PaymasterParams, + provider::zksync_provider, wallet::ZksyncWallet, }; use std::str::FromStr; use url; @@ -174,14 +170,9 @@ mod tests { "\n=== RUST SDK REPLICATION OF 'should deploy proxy account via factory' TEST ===" ); - // Hardcoded deterministic configuration (no dynamic node/contract deployment) - let expected_funding_signer_address = - address!("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"); // Account: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (Rich Wallet 4) - - // Account: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (Rich Wallet 4: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65) - let private_key = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a"; + let private_key = RichWallet::four().private_key_hex(); - // Owner private key for ECDSA smart account client (using rich wallet 3: 0x90F79bf6EB2c4f870365E785982E1f101E93b906) + // Owner private key for ECDSA smart account client let owner_private_key = "0x5de4111afa1a4b94908f83103c3a57e0c3c9e9da2dd5a02a84e9fde30d7e96c3"; config.deploy_wallet = @@ -352,13 +343,12 @@ mod tests { // Step 2: Verify session module is a validator println!("\n--- Step 2: Verifying session module is a validator ---"); - let account_contract = - SsoAccount::new(deployed_account_address, &public_provider); - let is_module_validator = account_contract - .isModuleValidator(config.contracts.session) - .call() - .await? - ._0; + let is_module_validator = is_module_validator( + deployed_account_address, + config.contracts.session, + &config, + ) + .await?; println!("Session module is validator: {is_module_validator}"); eyre::ensure!( @@ -401,7 +391,7 @@ mod tests { // Verify the session is active eyre::ensure!( - initial_session_state.session_state.status.is_active(), + initial_session_state.session_state.is_active(), "Initial session should be active (status=1)" ); @@ -480,46 +470,7 @@ mod tests { // Fund the smart account for transaction fees (1 ETH) println!("Funding smart account for transaction fees..."); let funding_amount = U256::from(1000000000000000000u64); // 1 ETH - - let funding_provider = { - let node_url: url::Url = config.clone().node_url; - let signer = PrivateKeySigner::from_str(private_key)?; - let signer_address = signer.address(); - println!("signer_address: {signer_address:?}"); - - eyre::ensure!( - signer_address == expected_funding_signer_address, - "signer address does not match owner address, expected: {:?}, received: {:?}", - expected_funding_signer_address, - signer_address - ); - - let wallet = ZksyncWallet::from(signer.clone()); - - zksync_provider() - .with_recommended_fillers() - .wallet(wallet) - .on_http(node_url) - }; - - // Send funding transaction to the smart account - let funding_tx = { - let tx_request = TransactionRequest::default() - .with_to(deployed_account_address) - .with_value(funding_amount); - - funding_provider.send_transaction(tx_request).await? - }; - println!("Funding transaction sent: {}", funding_tx.tx_hash()); - - // Wait for funding transaction to be confirmed - let funding_receipt = funding_provider - .wait_for_transaction_receipt(funding_tx.tx_hash().to_owned()) - .await?; - println!( - "Funding transaction confirmed: {:?}", - funding_receipt.status() - ); + fund_account(deployed_account_address, funding_amount, &config).await?; // Check smart account balance let account_balance = diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/revoke.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/revoke.rs index a3a6dfdf..ea9cff6f 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/revoke.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/revoke.rs @@ -77,10 +77,11 @@ pub async fn revoke_session_with_send_fn( mod tests { use super::*; use crate::{ + api::account::fund::fund_account, client::{ modular_account::{ DeployModularAccountArgs, SessionModuleArgs, - deploy_modular_account, + deploy_modular_account, is_module_validator, }, session::actions::session::{ hash::get_session_hash, @@ -88,9 +89,8 @@ mod tests { }, }, config::deploy_wallet::DeployWallet, - contracts::SsoAccount, utils::{ - alloy::extensions::ProviderExt, + anvil_zksync::rich_wallet::RichWallet, session::session_lib::{ session_spec::{ SessionSpec, limit_type::LimitType, @@ -105,20 +105,20 @@ mod tests { }, }; use alloy::{ - network::{ReceiptResponse, TransactionBuilder}, + network::ReceiptResponse, primitives::{FixedBytes, U256, address, hex}, providers::Provider, signers::local::PrivateKeySigner, }; - use alloy_zksync::{ - network::transaction_request::TransactionRequest, - provider::zksync_provider, wallet::ZksyncWallet, - }; + use alloy_zksync::{provider::zksync_provider, wallet::ZksyncWallet}; use std::str::FromStr; use url; #[tokio::test] async fn test_create_and_revoke_session() -> eyre::Result<()> { + // Add delay to avoid test run timing out + tokio::time::sleep(tokio::time::Duration::from_secs(15)).await; + // Arrange let (anvil_zksync, config, _) = spawn_node_and_deploy_contracts().await?; @@ -137,14 +137,9 @@ mod tests { "\n=== RUST SDK REPLICATION OF 'should deploy proxy account via factory' TEST ===" ); - // Hardcoded deterministic configuration (no dynamic node/contract deployment) - let expected_funding_signer_address = - address!("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"); // Account: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (Rich Wallet 4) - - // Account: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (Rich Wallet 4: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65) - let private_key = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a"; + let private_key = RichWallet::four().private_key_hex(); - // Owner private key for ECDSA smart account client (using rich wallet 3: 0x90F79bf6EB2c4f870365E785982E1f101E93b906) + // Owner private key for ECDSA smart account client let owner_private_key = "0x5de4111afa1a4b94908f83103c3a57e0c3c9e9da2dd5a02a84e9fde30d7e96c3"; config.deploy_wallet = @@ -184,9 +179,9 @@ mod tests { address!("0xdeBbD4CE2Bd6BD869D3ac93666A0D5F4fc06FC72"); let session_owner_address = address!("0x9BbC92a33F193174bf6Cc09c4b4055500d972479"); - let random_salt = alloy::primitives::keccak256( - "sdk-test-factory-replication-010".as_bytes(), - ); // Unique ID for deterministic salt + let random_salt_str = "sdk-test-factory-replication-010"; + let random_salt = + alloy::primitives::keccak256(random_salt_str.as_bytes()); // Unique ID for deterministic salt let expires_at = 1749040108u64; println!("=== REPLICATION DATA VERIFICATION ==="); @@ -290,9 +285,7 @@ mod tests { }), paymaster: None, passkey_module: None, - unique_account_id: Some( - "sdk-test-factory-replication-010".to_string(), - ), + unique_account_id: Some(random_salt_str.to_string()), }, &config, ) @@ -315,13 +308,12 @@ mod tests { // Step 2: Verify session module is a validator println!("\n--- Step 2: Verifying session module is a validator ---"); - let account_contract = - SsoAccount::new(deployed_account_address, &public_provider); - let is_module_validator = account_contract - .isModuleValidator(config.contracts.session) - .call() - .await? - ._0; + let is_module_validator = is_module_validator( + deployed_account_address, + config.contracts.session, + &config, + ) + .await?; println!("Session module is validator: {is_module_validator}"); eyre::ensure!( @@ -364,7 +356,7 @@ mod tests { // Verify the session is active eyre::ensure!( - initial_session_state.session_state.status == Status::Active, + initial_session_state.session_state.is_active(), "Initial session should be active (status=1)" ); @@ -443,46 +435,7 @@ mod tests { // Fund the smart account for transaction fees (1 ETH) println!("Funding smart account for transaction fees..."); let funding_amount = U256::from(1000000000000000000u64); // 1 ETH - - let funding_provider = { - let node_url: url::Url = config.clone().node_url; - let signer = PrivateKeySigner::from_str(private_key)?; - let signer_address = signer.address(); - println!("signer_address: {signer_address:?}"); - - eyre::ensure!( - signer_address == expected_funding_signer_address, - "signer address does not match owner address, expected: {:?}, received: {:?}", - expected_funding_signer_address, - signer_address - ); - - let wallet = ZksyncWallet::from(signer.clone()); - - zksync_provider() - .with_recommended_fillers() - .wallet(wallet) - .on_http(node_url) - }; - - // Send funding transaction to the smart account - let funding_tx = { - let tx_request = TransactionRequest::default() - .with_to(deployed_account_address) - .with_value(funding_amount); - - funding_provider.send_transaction(tx_request).await? - }; - println!("Funding transaction sent: {}", funding_tx.tx_hash()); - - // Wait for funding transaction to be confirmed - let funding_receipt = funding_provider - .wait_for_transaction_receipt(funding_tx.tx_hash().to_owned()) - .await?; - println!( - "Funding transaction confirmed: {:?}", - funding_receipt.status() - ); + fund_account(deployed_account_address, funding_amount, &config).await?; // Check smart account balance let account_balance = diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration.rs index d85a9ea9..ecae8e02 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration.rs @@ -3,10 +3,11 @@ mod send_integration; #[cfg(test)] mod tests { use crate::{ + api::account::fund::fund_account, client::{ modular_account::{ DeployModularAccountArgs, SessionModuleArgs, - deploy_modular_account, + deploy_modular_account, is_module_validator, }, session::{ actions::session::{ @@ -25,6 +26,7 @@ mod tests { contracts::SsoAccount, utils::{ alloy::extensions::ProviderExt, + anvil_zksync::rich_wallet::RichWallet, session::session_lib::session_spec::{ SessionSpec, limit_type::LimitType, transfer_spec::TransferSpec, usage_limit::UsageLimit, @@ -59,12 +61,7 @@ mod tests { "\n=== RUST SDK REPLICATION OF 'should deploy proxy account via factory' TEST ===" ); - // Hardcoded deterministic configuration (no dynamic node/contract deployment) - let expected_funding_signer_address = - address!("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"); // Account: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (Rich Wallet 4) - - // Account: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (Rich Wallet 4: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65) - let private_key = "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a"; + let private_key = RichWallet::four().private_key_hex(); // Owner private key for ECDSA smart account client (using rich wallet 3: 0x90F79bf6EB2c4f870365E785982E1f101E93b906) let owner_private_key = "0x5de4111afa1a4b94908f83103c3a57e0c3c9e9da2dd5a02a84e9fde30d7e96c3"; @@ -254,13 +251,12 @@ mod tests { // Step 2: Verify session module is a validator println!("\n--- Step 2: Verifying session module is a validator ---"); - let account_contract = - SsoAccount::new(deployed_account_address, &public_provider); - let is_module_validator = account_contract - .isModuleValidator(config.contracts.session) - .call() - .await? - ._0; + let is_module_validator = is_module_validator( + deployed_account_address, + config.contracts.session, + &config, + ) + .await?; println!("Session module is validator: {is_module_validator}"); eyre::ensure!( @@ -303,7 +299,7 @@ mod tests { // Verify the session is active eyre::ensure!( - initial_session_state.session_state.status.is_active(), + initial_session_state.session_state.is_active(), "Initial session should be active (status=1)" ); @@ -382,46 +378,7 @@ mod tests { // Fund the smart account for transaction fees (1 ETH) println!("Funding smart account for transaction fees..."); let funding_amount = U256::from(1000000000000000000u64); // 1 ETH - - let funding_provider = { - let node_url: url::Url = config.clone().node_url; - let signer = PrivateKeySigner::from_str(private_key)?; - let signer_address = signer.address(); - println!("signer_address: {signer_address:?}"); - - eyre::ensure!( - signer_address == expected_funding_signer_address, - "signer address does not match owner address, expected: {:?}, received: {:?}", - expected_funding_signer_address, - signer_address - ); - - let wallet = ZksyncWallet::from(signer.clone()); - - zksync_provider() - .with_recommended_fillers() - .wallet(wallet) - .on_http(node_url) - }; - - // Send funding transaction to the smart account - let funding_tx = { - let tx_request = TransactionRequest::default() - .with_to(deployed_account_address) - .with_value(funding_amount); - - funding_provider.send_transaction(tx_request).await? - }; - println!("Funding transaction sent: {}", funding_tx.tx_hash()); - - // Wait for funding transaction to be confirmed - let funding_receipt = funding_provider - .wait_for_transaction_receipt(funding_tx.tx_hash().to_owned()) - .await?; - println!( - "Funding transaction confirmed: {:?}", - funding_receipt.status() - ); + fund_account(deployed_account_address, funding_amount, &config).await?; // Check smart account balance let account_balance = @@ -1026,7 +983,7 @@ mod tests { // Verify the session is active eyre::ensure!( - initial_session_state.session_state.status.is_active(), + initial_session_state.session_state.is_active(), "Initial session should be active (status=1)" ); diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration/send_integration.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration/send_integration.rs index 038921ca..6c05df5f 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration/send_integration.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/actions/session/test/integration/send_integration.rs @@ -201,7 +201,7 @@ mod tests { ); eyre::ensure!( - initial_session_state.session_state.status.is_active(), + initial_session_state.session_state.is_active(), "Initial session should be active" ); eyre::ensure!( diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/client/session_client/signature/create_custom_session_signature.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/client/session_client/signature/create_custom_session_signature.rs index de628015..67950b8d 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/client/session_client/signature/create_custom_session_signature.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/client/session/client/session_client/signature/create_custom_session_signature.rs @@ -62,12 +62,12 @@ fn is_in_memory_node(chain: ChainId) -> bool { #[cfg(test)] mod tests { use super::*; - use crate::{ - config::contracts::SSOContracts, - utils::session::session_lib::session_spec::{ + use crate::utils::{ + session::session_lib::session_spec::{ limit_type::LimitType, transfer_spec::TransferSpec, usage_limit::UsageLimit, }, + test_utils::spawn_node_and_deploy_contracts, }; use alloy::{ hex, @@ -76,26 +76,17 @@ mod tests { #[tokio::test] async fn test_create_custom_session_signature() -> eyre::Result<()> { + // Add delay to avoid test run timing out + tokio::time::sleep(tokio::time::Duration::from_secs(15)).await; + + // Arrange + let (anvil_zksync, config, _) = + spawn_node_and_deploy_contracts().await?; + let chain = 260; let session_key = fixed_bytes!( "0x6954ddb21936036ccad688e2770846f15380a721bfab26c6e531e25b35cb5971" ); - let contracts = SSOContracts { - account_factory: address!( - "0x0000000000000000000000000000000000000000" - ), - passkey: address!("0x0000000000000000000000000000000000000000"), - session: address!("0x027ba0517cfa4471457c6e74f201753d98e7431d"), - account_paymaster: address!( - "0x0000000000000000000000000000000000000000" - ), - recovery: address!("0x0000000000000000000000000000000000000000"), - }; - let config = Config { - contracts, - node_url: "http://0.0.0.0:8011".parse().unwrap(), - deploy_wallet: None, - }; let hash = fixed_bytes!( "0x438331d7eba6601df86b9ddc6b0ca3f5ec7ac0b395a3d7e2795fa2a855b4daad" ); @@ -145,7 +136,7 @@ mod tests { println!("custom_signature: {custom_signature:?}"); let expected_custom_signature = bytes!( - "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000027ba0517cfa4471457c6e74f201753d98e7431d00000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000419bdec54176e0c91080f44c066837875a3c9afda7095568f64951b8ed9b6f420d7f9e89aa5d0be73a8625c41267bc4a7d62ac3fde218b5392b56cee2c23237e271b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000009bbc92a33f193174bf6cc09c4b4055500d972479000000000000000000000000000000000000000000000000000000006955b9000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000debbd4ce2bd6bd869d3ac93666a0d5f4fc06fc72000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000006000000000000000000000000011f853c85e282a542d8ee8f9cdd8fe6ce61badf100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000419bdec54176e0c91080f44c066837875a3c9afda7095568f64951b8ed9b6f420d7f9e89aa5d0be73a8625c41267bc4a7d62ac3fde218b5392b56cee2c23237e271b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000009bbc92a33f193174bf6cc09c4b4055500d972479000000000000000000000000000000000000000000000000000000006955b9000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000debbd4ce2bd6bd869d3ac93666a0d5f4fc06fc72000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ); // Compare signatures byte by byte to find differences @@ -187,6 +178,8 @@ mod tests { expected_custom_signature, ); + drop(anvil_zksync); + Ok(()) } } diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/config/deploy_wallet.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/config/deploy_wallet.rs index ab79dffb..f25f723e 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/config/deploy_wallet.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/config/deploy_wallet.rs @@ -1,3 +1,4 @@ +use crate::utils::anvil_zksync::rich_wallet::RichWallet; use alloy::{primitives::Address, signers::local::PrivateKeySigner}; use alloy_zksync::wallet::ZksyncWallet; use serde::{Deserialize, Serialize}; @@ -21,6 +22,12 @@ impl DeployWallet { Self { private_key_hex } } + pub fn rich_wallet() -> Self { + let rich_wallet = RichWallet::four(); + let private_key_hex = rich_wallet.private_key_hex().to_string(); + Self { private_key_hex } + } + pub fn address(&self) -> Address { let signer = PrivateKeySigner::from_str(&self.private_key_hex).unwrap(); let wallet = ZksyncWallet::from(signer); diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils.rs index 052087e3..5dcd8b0e 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils.rs @@ -1,4 +1,5 @@ pub(crate) mod alloy; +pub(crate) mod anvil_zksync; pub(crate) mod contract_deployed; pub(crate) mod deployment_utils; pub(crate) mod encoding; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/anvil_zksync.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/anvil_zksync.rs new file mode 100644 index 00000000..8b5bac5d --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/anvil_zksync.rs @@ -0,0 +1 @@ +pub mod rich_wallet; diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/anvil_zksync/rich_wallet.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/anvil_zksync/rich_wallet.rs new file mode 100644 index 00000000..d00f5b59 --- /dev/null +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/anvil_zksync/rich_wallet.rs @@ -0,0 +1,345 @@ +use alloy::{ + primitives::Address, + signers::local::{LocalSignerError, PrivateKeySigner}, +}; +use alloy_zksync::wallet::ZksyncWallet; +use serde::{Deserialize, Serialize}; +use std::{str::FromStr, sync::LazyLock}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct RichWallet { + private_key_hex: String, + address: Address, +} + +impl RichWallet { + fn from_private_key( + private_key_hex: String, + ) -> Result { + let signer = PrivateKeySigner::from_str(&private_key_hex)?; + let wallet = ZksyncWallet::from(signer); + let address = wallet.default_signer().address(); + + Ok(Self { private_key_hex, address }) + } + + #[allow(dead_code)] + pub fn private_key_hex(&self) -> &str { + &self.private_key_hex + } + + #[allow(dead_code)] + pub fn address(&self) -> Address { + self.address + } + + #[allow(dead_code)] + pub fn to_zksync_wallet(&self) -> Result { + let signer = self.to_local_signer()?; + Ok(ZksyncWallet::from(signer)) + } + + #[allow(dead_code)] + pub fn to_local_signer( + &self, + ) -> Result { + PrivateKeySigner::from_str(&self.private_key_hex) + } + + // Convenience methods to access specific rich wallets + /// Get the zeroth rich wallet (index 0) + /// Address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + #[allow(dead_code)] + pub fn zero() -> &'static RichWallet { + &RICH_WALLETS[0] + } + + /// Get the first rich wallet (index 1) + /// Address: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 + #[allow(dead_code)] + pub fn one() -> &'static RichWallet { + &RICH_WALLETS[1] + } + + /// Get the second rich wallet (index 2) + /// Address: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC + #[allow(dead_code)] + pub fn two() -> &'static RichWallet { + &RICH_WALLETS[2] + } + + /// Get the third rich wallet (index 3) + /// Address: 0x90F79bf6EB2c4f870365E785982E1f101E93b906 + #[allow(dead_code)] + pub fn three() -> &'static RichWallet { + &RICH_WALLETS[3] + } + + /// Get the fourth rich wallet (index 4) + /// Address: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 + #[allow(dead_code)] + pub fn four() -> &'static RichWallet { + &RICH_WALLETS[4] + } + + /// Get the fifth rich wallet (index 5) + /// Address: 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc + #[allow(dead_code)] + pub fn five() -> &'static RichWallet { + &RICH_WALLETS[5] + } + + /// Get the sixth rich wallet (index 6) + /// Address: 0x976EA74026E726554dB657fA54763abd0C3a0aa9 + #[allow(dead_code)] + pub fn six() -> &'static RichWallet { + &RICH_WALLETS[6] + } + + /// Get the seventh rich wallet (index 7) + /// Address: 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 + #[allow(dead_code)] + pub fn seven() -> &'static RichWallet { + &RICH_WALLETS[7] + } + + /// Get the eighth rich wallet (index 8) + /// Address: 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f + #[allow(dead_code)] + pub fn eight() -> &'static RichWallet { + &RICH_WALLETS[8] + } + + /// Get the ninth rich wallet (index 9) + /// Address: 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 + #[allow(dead_code)] + pub fn nine() -> &'static RichWallet { + &RICH_WALLETS[9] + } +} + +impl TryFrom for RichWallet { + type Error = LocalSignerError; + + fn try_from(private_key_hex: String) -> Result { + Self::from_private_key(private_key_hex) + } +} + +impl TryFrom<&str> for RichWallet { + type Error = LocalSignerError; + + fn try_from(private_key_hex: &str) -> Result { + Self::try_from(private_key_hex.to_string()) + } +} + +/// Static array of rich wallets for testing +/// +/// Rich Accounts (each with 10000.000000000000000000 ETH) +/// ======================== +/// (0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +/// (1) 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 +/// (2) 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC +/// (3) 0x90F79bf6EB2c4f870365E785982E1f101E93b906 +/// (4) 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 +/// (5) 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc +/// (6) 0x976EA74026E726554dB657fA54763abd0C3a0aa9 +/// (7) 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 +/// (8) 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f +/// (9) 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 +/// +/// Mnemonic: test test test test test test test test test test test junk +/// Derivation path: m/44'/60'/0'/0/0 +/// Chain ID: 260 +pub static RICH_WALLETS: LazyLock<[RichWallet; 10]> = LazyLock::new(|| { + [ + RichWallet::try_from( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + ).unwrap(), + RichWallet::try_from( + "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + ).unwrap(), + RichWallet::try_from( + "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a", + ).unwrap(), + RichWallet::try_from( + "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6", + ).unwrap(), + RichWallet::try_from( + "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", + ).unwrap(), + RichWallet::try_from( + "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", + ).unwrap(), + RichWallet::try_from( + "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", + ).unwrap(), + RichWallet::try_from( + "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356", + ).unwrap(), + RichWallet::try_from( + "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97", + ).unwrap(), + RichWallet::try_from( + "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", + ).unwrap(), + ] +}); + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::address; + + #[test] + fn test_rich_wallets_derive_correct_addresses() { + // Expected addresses from the comments above + let expected_addresses = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", // (0) + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", // (1) + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", // (2) + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", // (3) + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", // (4) + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", // (5) + "0x976EA74026E726554dB657fA54763abd0C3a0aa9", // (6) + "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", // (7) + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", // (8) + "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", // (9) + ]; + + // Test that each rich wallet derives the correct address + for (i, expected_address) in expected_addresses.iter().enumerate() { + let actual_address = format!("{:#x}", RICH_WALLETS[i].address()); + assert_eq!( + actual_address, + expected_address.to_lowercase(), + "Rich wallet {i} address mismatch. Expected: {expected_address}, Got: {actual_address}" + ); + } + + // Also verify we have exactly 10 wallets + assert_eq!(RICH_WALLETS.len(), 10); + } + + #[test] + fn test_rich_wallet_convenience_methods() { + // Test that all convenience methods return the correct wallets + let expected_addresses = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", // zero + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", // one + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", // two + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", // three + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", // four + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", // five + "0x976EA74026E726554dB657fA54763abd0C3a0aa9", // six + "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", // seven + "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", // eight + "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", // nine + ]; + + // Test each convenience method + assert_eq!( + format!("{:#x}", RichWallet::zero().address()), + expected_addresses[0].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::one().address()), + expected_addresses[1].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::two().address()), + expected_addresses[2].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::three().address()), + expected_addresses[3].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::four().address()), + expected_addresses[4].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::five().address()), + expected_addresses[5].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::six().address()), + expected_addresses[6].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::seven().address()), + expected_addresses[7].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::eight().address()), + expected_addresses[8].to_lowercase() + ); + assert_eq!( + format!("{:#x}", RichWallet::nine().address()), + expected_addresses[9].to_lowercase() + ); + + // Verify they're the same as accessing via array index + assert_eq!(RichWallet::zero().address(), RICH_WALLETS[0].address()); + assert_eq!(RichWallet::two().address(), RICH_WALLETS[2].address()); + assert_eq!(RichWallet::nine().address(), RICH_WALLETS[9].address()); + } + + #[test] + fn test_rich_wallet_to_zksync_wallet() { + let rich_wallet = RichWallet::zero(); + let zksync_wallet = rich_wallet.to_zksync_wallet().unwrap(); + + // Verify the wallet has the correct address + assert_eq!( + zksync_wallet.default_signer().address(), + rich_wallet.address() + ); + + // Test with a few more wallets + let second_wallet = RichWallet::one(); + let second_zksync = second_wallet.to_zksync_wallet().unwrap(); + assert_eq!( + second_zksync.default_signer().address(), + second_wallet.address() + ); + + let tenth_wallet = RichWallet::nine(); + let tenth_zksync = tenth_wallet.to_zksync_wallet().unwrap(); + assert_eq!( + tenth_zksync.default_signer().address(), + tenth_wallet.address() + ); + } + + #[test] + fn test_rich_wallet_to_local_signer() { + let rich_wallet = RichWallet::zero(); + let local_signer = rich_wallet.to_local_signer().unwrap(); + + // Verify the signer has the correct address + assert_eq!(local_signer.address(), rich_wallet.address()); + + // Test with a few more wallets + let third_wallet = RichWallet::two(); + let third_signer = third_wallet.to_local_signer().unwrap(); + assert_eq!(third_signer.address(), third_wallet.address()); + + let ninth_wallet = RichWallet::eight(); + let ninth_signer = ninth_wallet.to_local_signer().unwrap(); + assert_eq!(ninth_signer.address(), ninth_wallet.address()); + } + + #[test] + fn test_rich_wallet_from_private_key() { + let private_key = "0x5de4111afa1a4b94908f83103c3a57e0c3c9e9da2dd5a02a84e9fde30d7e96c3"; + let rich_wallet = + RichWallet::from_private_key(private_key.to_string()).unwrap(); + assert_eq!( + rich_wallet.address(), + address!("0x6a34Ea49c29BF7Cce95F51E7F0f419831Ad5dBC6") + ); + } +} diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/manual_build_transaction/register_passkey.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/manual_build_transaction/register_passkey.rs index 2bf466d4..6c362574 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/manual_build_transaction/register_passkey.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/manual_build_transaction/register_passkey.rs @@ -1,8 +1,9 @@ #[cfg(test)] pub mod tests { use crate::{ - api::account::deployment::{ - DeployedAccountDetails, PasskeyParameters, RpId, deploy_account, + api::account::{ + deployment::{DeployedAccountDetails, deploy_account}, + passkey::{passkey_parameters::PasskeyParameters, rp_id::RpId}, }, config::Config, utils::test_utils::spawn_node_and_deploy_contracts, @@ -278,7 +279,7 @@ pub mod tests { .await .map_err(|e| eyre::eyre!("Error registering Apple passkey: {:?}", e))?; - let result = deploy_account(deploy_args, config).await?; + let result = deploy_account(deploy_args, None, None, config).await?; Ok((result, credential)) } diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/passkey/authenticators/verify.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/passkey/authenticators/verify.rs index 6a271bf0..c3c68b27 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/passkey/authenticators/verify.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/passkey/authenticators/verify.rs @@ -2,7 +2,7 @@ use super::{ android::verify::verify_registration as android_verify_registration, apple::verify::verify_registration as apple_verify_registration, }; -use crate::api::account::deployment::RpId; +use crate::api::account::passkey::rp_id::RpId; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ValidatedPasskey { diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec.rs index fdbe55a5..c0bf5ef4 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec.rs @@ -20,6 +20,7 @@ pub mod transfer_spec; pub mod usage_limit; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct SessionSpec { pub signer: Address, pub expires_at: U256, @@ -217,4 +218,126 @@ mod tests { assert_eq!(original_limit, round_trip_limit); } } + + #[test] + fn test_session_spec_json_serialization() { + // Create a comprehensive SessionSpec to test camelCase serialization + let session_spec = SessionSpec { + signer: address!("0x9BbC92a33F193174bf6Cc09c4b4055500d972479"), + expires_at: U256::from(1749040108u64), + fee_limit: UsageLimit { + limit_type: LimitType::Allowance, + limit: U256::from(100000000000000000u64), + period: U256::from(3600u64), + }, + call_policies: vec![CallSpec { + target: address!("0x1111111111111111111111111111111111111111"), + selector: fixed_bytes!("a9059cbb"), + max_value_per_use: U256::from(1000000000000000u64), + value_limit: UsageLimit { + limit_type: LimitType::Lifetime, + limit: U256::from(10000000000000000u64), + period: U256::ZERO, + }, + constraints: vec![Constraint { + condition: Condition::Equal, + index: 4, + ref_value: FixedBytes::ZERO, + limit: UsageLimit { + limit_type: LimitType::Unlimited, + limit: U256::ZERO, + period: U256::ZERO, + }, + }], + }], + transfer_policies: vec![TransferSpec { + target: address!("0x2222222222222222222222222222222222222222"), + max_value_per_use: U256::from(20000000000000000u64), + value_limit: UsageLimit { + limit_type: LimitType::Unlimited, + limit: U256::ZERO, + period: U256::ZERO, + }, + }], + }; + + // Expected JSON string with camelCase formatting (identical to session_spec but as JSON string) + let session_spec_json = r#"{ + "signer": "0x9bbc92a33f193174bf6cc09c4b4055500d972479", + "expiresAt": "0x68403bec", + "feeLimit": { + "limitType": "Allowance", + "limit": "0x16345785d8a0000", + "period": "0xe10" + }, + "callPolicies": [ + { + "target": "0x1111111111111111111111111111111111111111", + "selector": "0xa9059cbb", + "maxValuePerUse": "0x38d7ea4c68000", + "valueLimit": { + "limitType": "Lifetime", + "limit": "0x2386f26fc10000", + "period": "0x0" + }, + "constraints": [ + { + "condition": "Equal", + "index": 4, + "refValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "limit": { + "limitType": "Unlimited", + "limit": "0x0", + "period": "0x0" + } + } + ] + } + ], + "transferPolicies": [ + { + "target": "0x2222222222222222222222222222222222222222", + "maxValuePerUse": "0x470de4df820000", + "valueLimit": { + "limitType": "Unlimited", + "limit": "0x0", + "period": "0x0" + } + } + ] +}"#; + + // Serialize to JSON + let actual_json = serde_json::to_string_pretty(&session_spec) + .expect("Failed to serialize to JSON"); + + // Print the JSON to see the camelCase formatting + println!("SessionSpec JSON representation:"); + println!("{actual_json}"); + + // Parse both JSON strings to serde_json::Value for comparison (ignoring whitespace differences) + let expected_value: serde_json::Value = + serde_json::from_str(session_spec_json) + .expect("Failed to parse expected JSON"); + let actual_value: serde_json::Value = + serde_json::from_str(&actual_json) + .expect("Failed to parse actual JSON"); + + // Assert that the JSON structures are identical + assert_eq!( + expected_value, actual_value, + "JSON serialization doesn't match expected format.\nExpected:\n{session_spec_json}\nActual:\n{actual_json}" + ); + + // Also test that we can deserialize it back + let deserialized: SessionSpec = serde_json::from_str(&actual_json) + .expect("Failed to deserialize from JSON"); + assert_eq!(session_spec, deserialized); + + // Test that we can also deserialize from the expected JSON + let deserialized_from_expected: SessionSpec = + serde_json::from_str(session_spec_json) + .expect("Failed to deserialize from expected JSON"); + assert_eq!(session_spec, deserialized_from_expected); + } } diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/call_spec.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/call_spec.rs index 64487a92..8731f2ec 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/call_spec.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/call_spec.rs @@ -8,6 +8,7 @@ use alloy::primitives::{Address, FixedBytes, U256}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct CallSpec { pub target: Address, pub selector: FixedBytes<4>, diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/constraint.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/constraint.rs index 7067208e..93c2ba71 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/constraint.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/constraint.rs @@ -8,6 +8,7 @@ use alloy::primitives::FixedBytes; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct Constraint { pub condition: Condition, pub index: u64, diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/transfer_spec.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/transfer_spec.rs index cfb0f4e7..0ff1a065 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/transfer_spec.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/transfer_spec.rs @@ -6,6 +6,7 @@ use alloy::primitives::{Address, U256}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct TransferSpec { pub target: Address, pub max_value_per_use: U256, diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/usage_limit.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/usage_limit.rs index f7b49e25..d441e7c9 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/usage_limit.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_spec/usage_limit.rs @@ -6,6 +6,7 @@ use alloy::primitives::U256; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] pub struct UsageLimit { pub limit_type: LimitType, pub limit: U256, diff --git a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_state.rs b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_state.rs index ddfca1d9..49a35de8 100644 --- a/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_state.rs +++ b/packages/sdk-platforms/rust/zksync-sso/crates/sdk/src/utils/session/session_lib/session_state.rs @@ -19,6 +19,16 @@ pub struct SessionState { pub call_params: Vec, } +impl SessionState { + pub fn is_active(&self) -> bool { + self.status.is_active() + } + + pub fn is_closed(&self) -> bool { + self.status.is_closed() + } +} + impl From for SessionState { fn from(value: SessionLibSessionState) -> Self { SessionState { diff --git a/packages/sdk-platforms/swift/.editorconfig b/packages/sdk-platforms/swift/.editorconfig new file mode 100644 index 00000000..c2e6de7a --- /dev/null +++ b/packages/sdk-platforms/swift/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig is awesome: https://EditorConfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.swift] +indent_style = space +indent_size = 4 + +[*.{pbxproj,xcworkspacedata,xcscheme}] +indent_style = tab + +[*.{json,yml,yaml}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/packages/sdk-platforms/swift/.swift-version b/packages/sdk-platforms/swift/.swift-version new file mode 100644 index 00000000..b883184b --- /dev/null +++ b/packages/sdk-platforms/swift/.swift-version @@ -0,0 +1 @@ +5.9 \ No newline at end of file diff --git a/packages/sdk-platforms/swift/.swiftformat b/packages/sdk-platforms/swift/.swiftformat new file mode 100644 index 00000000..201c6185 --- /dev/null +++ b/packages/sdk-platforms/swift/.swiftformat @@ -0,0 +1,13 @@ +# SwiftFormat configuration +--indent 4 +--indentcase false +--trimwhitespace always +--emptybraces no-space +--stripunusedargs closure-only +--self remove +--header ignore +--allman false +--wrapconditions preserve +--wraparguments preserve +--wrapparameters preserve +--wrapcollections preserve \ No newline at end of file diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Package.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Package.swift index ddd93f03..0fde8f5e 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Package.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Package.swift @@ -10,21 +10,24 @@ let package = Package( products: [ .library( name: "ExamplePackage", - targets: ["ExamplePackage"]), + targets: ["ExamplePackage"] + ), ], dependencies: [ - .package(path: "../../../ZKsyncSSO/") + .package(path: "../../../ZKsyncSSO/"), ], targets: [ .target( name: "ExamplePackage", dependencies: [ - "ZKsyncSSO", - "ExamplePackageUIComponents", - ]), + "ZKsyncSSO", + "ExamplePackageUIComponents", + ] + ), .target( name: "ExamplePackageUIComponents", - dependencies: []), + dependencies: [] + ), .testTarget( name: "ExamplePackageTests", dependencies: ["ExamplePackage"] diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/AccountSession.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/AccountSession.swift new file mode 100644 index 00000000..fe13dbac --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/AccountSession.swift @@ -0,0 +1,11 @@ +import Foundation + +struct AccountSession { + let accountDetails: AccountDetails + let signers: AccountSigners + + init(accountDetails: AccountDetails, signers: AccountSigners) { + self.accountDetails = accountDetails + self.signers = signers + } +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/AccountSigners.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/AccountSigners.swift new file mode 100644 index 00000000..567571b1 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/AccountSigners.swift @@ -0,0 +1,18 @@ +import Foundation + +struct AccountSigners { + var accountOwner: EOASigner + var sessionOwner: EOASigner + + init(accountOwner: EOASigner, sessionOwner: EOASigner) { + self.accountOwner = accountOwner + self.sessionOwner = sessionOwner + } + + static var `default`: AccountSigners { + return AccountSigners( + accountOwner: .accountOwner, + sessionOwner: .sessionOwner + ) + } +} \ No newline at end of file diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/EOASigner.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/EOASigner.swift new file mode 100644 index 00000000..96c6bed6 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/EOASigner.swift @@ -0,0 +1,23 @@ +import Foundation + +struct EOASigner { + let privateKeyHex: String + let address: String + + init(privateKeyHex: String, address: String) { + self.privateKeyHex = privateKeyHex + self.address = address + } +} + +extension EOASigner { + static let accountOwner = EOASigner( + privateKeyHex: "0x5de4111afa1a4b94908f83103c3a57e0c3c9e9da2dd5a02a84e9fde30d7e96c3", + address: "0x6a34ea49c29bf7cce95f51e7f0f419831ad5dbc6" + ) + + static let sessionOwner = EOASigner( + privateKeyHex: "0x6954ddb21936036ccad688e2770846f15380a721bfab26c6e531e25b35cb5971", + address: "0x9BbC92a33F193174bf6Cc09c4b4055500d972479" + ) +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/ExampleView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/ExampleView.swift index 99dc9e2e..8d15d3bd 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/ExampleView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/ExampleView.swift @@ -2,24 +2,24 @@ import SwiftUI import ZKsyncSSO public struct ExampleView: View { - let relyingPartyIdentifier: String - - @State private var accountDetails: AccountDetails? + + @State private var accountSession: AccountSession? public init(relyingPartyIdentifier: String, bundleIdentifier: String) { self.relyingPartyIdentifier = relyingPartyIdentifier - + ZKsyncSSO.initLogger(bundleIdentifier: bundleIdentifier, level: .trace) } public var body: some View { NavigationStack { - if let account = accountDetails { + if let session = accountSession { AccountDetailsView( - account: account, + account: session.accountDetails, + signers: session.signers, onLogout: { - accountDetails = nil + accountSession = nil } ) } else { @@ -29,11 +29,11 @@ public struct ExampleView: View { userID: "jdoe@example.com", domain: relyingPartyIdentifier ), - onAccountCreated: { account in - self.accountDetails = account + onAccountCreated: { session in + self.accountSession = session }, - onSignedIn: { account in - self.accountDetails = account + onSignedIn: { session in + self.accountSession = session } ) } @@ -43,7 +43,7 @@ public struct ExampleView: View { #Preview { ExampleView( - relyingPartyIdentifier: "soo-sdk-example-pages.pages.dev", + relyingPartyIdentifier: "auth-test.zksync.dev", bundleIdentifier: "io.jackpooley.MLSSOExample" ) } diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/LoggedOutView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/LoggedOutView.swift index 97ce74b3..ab29e620 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/LoggedOutView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/LoggedOutView.swift @@ -11,13 +11,13 @@ struct LoggedOutView: View { @State private var showingCreateAccount = false @State private var showingLoginView = false - var onAccountCreated: ((AccountDetails) -> Void)? - var onSignedIn: ((AccountDetails) -> Void)? + var onAccountCreated: ((AccountSession) -> Void)? + var onSignedIn: ((AccountSession) -> Void)? init( accountInfo: AccountInfo, - onAccountCreated: ((AccountDetails) -> Void)? = nil, - onSignedIn: ((AccountDetails) -> Void)? = nil + onAccountCreated: ((AccountSession) -> Void)? = nil, + onSignedIn: ((AccountSession) -> Void)? = nil ) { self.accountInfo = accountInfo self.onAccountCreated = onAccountCreated @@ -53,12 +53,15 @@ struct LoggedOutView: View { .sheet(isPresented: $showingCreateAccount) { AccountCreationView( accountInfo: accountInfo, - onDeployed: { deployedAccount in + onDeployed: { deployedAccount, signers in if let onAccountCreated = onAccountCreated { onAccountCreated( - AccountDetails( - account: deployedAccount, - balance: nil + AccountSession( + accountDetails: AccountDetails( + account: deployedAccount, + balance: nil + ), + signers: signers ) ) } @@ -90,7 +93,7 @@ struct LoggedOutView: View { accountInfo: AccountInfo( name: "Jane Doe", userID: "jdoe@example.com", - domain: "soo-sdk-example-pages.pages.dev" + domain: "auth-test.zksync.dev" ) ) } diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/UIError.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/UIError.swift new file mode 100644 index 00000000..e568ff62 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/UIError.swift @@ -0,0 +1,13 @@ +import Foundation + +public struct UIError { + public let message: String + + public init(message: String) { + self.message = message + } + + public init(from error: Error) { + self.message = error.localizedDescription + } +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationContentView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationContentView.swift index 28b8d5e7..86185d58 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationContentView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationContentView.swift @@ -4,20 +4,21 @@ import SwiftUI import ZKsyncSSO struct AccountCreationContentView: View { - - typealias OnDeployed = (DeployedAccount) -> Void typealias OnSuccess = () -> Void @Environment(\.dismiss) private var dismiss @Environment(\.authorizationController) private var authorizationController + @EnvironmentObject private var sessionsStore: SessionsStore + @State private var isLoading = false @State private var error: Error? @State private var deployedAddress: String? private let accountInfo: AccountInfo private let challenge: Data + private let signers: AccountSigners let onSuccess: OnSuccess let onDeployed: OnDeployed @@ -25,11 +26,13 @@ struct AccountCreationContentView: View { init( challenge: Data, accountInfo: AccountInfo, + signers: AccountSigners = .default, onSuccess: @escaping () -> Void, - onDeployed: @escaping (DeployedAccount) -> Void + onDeployed: @escaping OnDeployed ) { self.challenge = challenge self.accountInfo = accountInfo + self.signers = signers self.onSuccess = onSuccess self.onDeployed = onDeployed } @@ -79,11 +82,19 @@ struct AccountCreationContentView: View { Task { @MainActor in do { + let initialK1Owners: [String]? = [ signers.accountOwner.address ] + let initialSessionConfig = SessionSpec.initialSession( + sessionOwner: signers.sessionOwner + ) + let initialSessionConfigJson = try initialSessionConfig.toJsonString() + let account = try await createAccount( userName: accountInfo.name, userID: accountInfo.userID, challenge: challenge, relyingPartyIdentifier: accountInfo.domain, + initialK1Owners: initialK1Owners, + initialSessionConfigJson: initialSessionConfigJson, controller: authorizationController ) let address = account.address @@ -96,14 +107,19 @@ struct AccountCreationContentView: View { uniqueAccountId: uniqueAccountId ) - print("XXX deployed account: \(deployedAccount)") - + print("Deployed account: \(deployedAccount)") + + let initialSession = Session( + createdAt: Date(), + sessionSpec: initialSessionConfig + ) + sessionsStore.addSession(initialSession, for: address) + // Signal success to parent view onSuccess() - - // Provide deployed account to callback - onDeployed(deployedAccount) + // Provide deployed account and signer to callback + onDeployed(deployedAccount, signers) } catch let error as ASAuthorizationError where error.code == .canceled { print("User cancelled passkey creation") } catch { @@ -121,9 +137,9 @@ struct AccountCreationContentView: View { accountInfo: .init( name: "Jane Doe", userID: "jdoe", - domain: "soo-sdk-example-pages.pages.dev" + domain: "auth-test.zksync.dev" ), onSuccess: {}, - onDeployed: { _ in } + onDeployed: { _, _ in } ) } diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationView.swift index 54a76951..3d2e9111 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountCreationView.swift @@ -1,12 +1,14 @@ import ExamplePackageUIComponents import SwiftUI +typealias OnDeployed = (DeployedAccount, AccountSigners) -> Void + public struct AccountCreationView: View { @Environment(\.dismiss) private var dismiss @State private var accountInfo: AccountInfo @FocusState private var focusedField: Field? @State private var showingSuccess: Bool - let onDeployed: ((DeployedAccount) -> Void)? + let onDeployed: OnDeployed? let previewWithRedBackground: Bool enum Field { @@ -16,7 +18,7 @@ public struct AccountCreationView: View { init( accountInfo: AccountInfo, - onDeployed: ((DeployedAccount) -> Void)? = nil, + onDeployed: OnDeployed? = nil, showToast: Bool = false, previewWithRedBackground: Bool = false ) { @@ -26,140 +28,161 @@ public struct AccountCreationView: View { self.previewWithRedBackground = previewWithRedBackground } - public var body: some View { - NavigationStack { - ZStack { - // Main content - VStack(spacing: 0) { - Spacer() + private var usernameField: some View { + VStack(alignment: .leading, spacing: 8) { + Text("Username") + .foregroundStyle(.secondary) + .font(.subheadline) + + ZStack(alignment: .trailing) { + TextField("Enter username", text: $accountInfo.name) + .focused($focusedField, equals: .username) + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + .autocorrectionDisabled() + + if focusedField == .username && !accountInfo.name.isEmpty { + Button { + withAnimation { + accountInfo.name = "" + } + } label: { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.gray) + } + .padding(.trailing, 16) + .transition(.opacity) + } + } + .animation( + .easeInOut(duration: 0.2), + value: focusedField == .username && !accountInfo.name.isEmpty + ) + } + } - VStack(spacing: 24) { - Image(systemName: "person.badge.key.fill") - .font(.system(size: 60)) - .foregroundStyle(.blue) - .background(previewWithRedBackground ? Color.red.opacity(0.8) : nil) - - VStack(alignment: .leading, spacing: 8) { - Text("Username") - .foregroundStyle(.secondary) - .font(.subheadline) - - ZStack(alignment: .trailing) { - TextField("Enter username", text: $accountInfo.name) - .focused($focusedField, equals: .username) - .padding() - .background(Color(.systemGray6)) - .cornerRadius(10) - .autocorrectionDisabled() - - if focusedField == .username && !accountInfo.name.isEmpty { - Button { - withAnimation { - accountInfo.name = "" - } - } label: { - Image(systemName: "xmark.circle.fill") - .foregroundStyle(.gray) - } - .padding(.trailing, 16) - .transition(.opacity) - } - } - .animation( - .easeInOut(duration: 0.2), - value: focusedField == .username && !accountInfo.name.isEmpty) + private var userIDField: some View { + VStack(alignment: .leading, spacing: 8) { + Text("User ID") + .foregroundStyle(.secondary) + .font(.subheadline) + + ZStack(alignment: .trailing) { + TextField("Enter user ID", text: $accountInfo.userID) + .focused($focusedField, equals: .userID) + .padding() + .background(Color(.systemGray6)) + .cornerRadius(10) + .font(.system(.body, design: .monospaced)) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + + if focusedField == .userID && !accountInfo.userID.isEmpty { + Button { + withAnimation { + accountInfo.userID = "" } + } label: { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.gray) + } + .padding(.trailing, 16) + .transition(.opacity) + } + } + .animation( + .easeInOut(duration: 0.2), + value: focusedField == .userID && !accountInfo.userID.isEmpty + ) + } + } + + private var mainFormContent: some View { + VStack(spacing: 24) { + Image(systemName: "person.badge.key.fill") + .font(.system(size: 60)) + .foregroundStyle(.blue) + .background(previewWithRedBackground ? Color.red.opacity(0.8) : nil) + + usernameField + userIDField - VStack(alignment: .leading, spacing: 8) { - Text("User ID") - .foregroundStyle(.secondary) - .font(.subheadline) - - ZStack(alignment: .trailing) { - TextField("Enter user ID", text: $accountInfo.userID) - .focused($focusedField, equals: .userID) - .padding() - .background(Color(.systemGray6)) - .cornerRadius(10) - .font(.system(.body, design: .monospaced)) - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - - if focusedField == .userID && !accountInfo.userID.isEmpty { - Button { - withAnimation { - accountInfo.userID = "" - } - } label: { - Image(systemName: "xmark.circle.fill") - .foregroundStyle(.gray) - } - .padding(.trailing, 16) - .transition(.opacity) - } - } - .animation( - .easeInOut(duration: 0.2), - value: focusedField == .userID && !accountInfo.userID.isEmpty) + contentView + } + } + + private var contentView: some View { + VStack { + if !previewWithRedBackground { + AccountCreationContentView( + challenge: Data( + (0..<32).map { _ in UInt8.random(in: 0...255) }), + accountInfo: accountInfo, + signers: .default, + onSuccess: { + withAnimation { + showingSuccess = true } - VStack { - if !previewWithRedBackground { - AccountCreationContentView( - challenge: Data( - (0..<32).map { _ in UInt8.random(in: 0...255) }), - accountInfo: accountInfo, - onSuccess: { - withAnimation { - showingSuccess = true - } - - // Dismiss after delay - Task { - try? await Task.sleep(for: .seconds(1.5)) - dismiss() - } - }, - onDeployed: { deployedAccount in - onDeployed?(deployedAccount) - } - ) - } else { - Button { - } label: { - HStack { - Image(systemName: "key.fill") - .font(.system(size: 16)) - Text("Create Passkey") - .font(.headline) - } - .frame(maxWidth: .infinity) - .frame(height: 50) - .background(Color.blue) - .foregroundColor(.white) - .cornerRadius(10) - } - } + // Dismiss after delay + Task { + try? await Task.sleep(for: .seconds(1.5)) + dismiss() } - .padding(.top, 16) + }, + onDeployed: { deployedAccount, signer in + onDeployed?(deployedAccount, signer) + } + ) + .environmentObject(SessionsStore.shared) + } else { + Button { + } label: { + HStack { + Image(systemName: "key.fill") + .foregroundStyle(.white) + + Text("Create Account") + .foregroundStyle(.white) + .font(.headline) } - .padding(.horizontal, 24) + .frame(maxWidth: .infinity) + .frame(height: 50) + } + .background(Color.red) + .cornerRadius(10) + } + } + } + private var successOverlay: some View { + Color.black.opacity(0.001) // Invisible background to capture whole screen + .edgesIgnoringSafeArea(.all) + .overlay { + ToastView( + icon: "checkmark.circle.fill", + iconColor: .green, + message: "Account Deployed!" + ) + } + } + + public var body: some View { + NavigationStack { + ZStack { + // Main content + VStack(spacing: 0) { + Spacer() + mainFormContent Spacer() } - .background(previewWithRedBackground ? Color.red.opacity(0.2) : nil) + .padding(.horizontal, 24) + .opacity(showingSuccess ? 0 : 1) // Toast overlay if showingSuccess { - Color.black.opacity(0.001) // Invisible background to capture whole screen - .edgesIgnoringSafeArea(.all) - .overlay { - ToastView( - icon: "checkmark.circle.fill", - iconColor: .green, - message: "Account Deployed!" - ) - } + successOverlay } } .navigationTitle("Create Account") diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetails.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetails.swift index f9144185..ddc0127a 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetails.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetails.swift @@ -2,17 +2,17 @@ import Foundation struct AccountDetails { let account: DeployedAccount - + var address: String { account.address } - + var uniqueAccountId: String { account.uniqueAccountId } - + var balance: String? - + init(account: DeployedAccount, balance: String? = nil) { self.account = account self.balance = balance @@ -20,7 +20,6 @@ struct AccountDetails { } extension AccountDetails { - var explorerURL: URL { URL(string: "https://explorer.zksync.io/address/\(self)")! } diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetailsView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetailsView.swift index 0f802bda..57dcb671 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetailsView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountDetailsView.swift @@ -2,6 +2,10 @@ import ExamplePackageUIComponents import SwiftUI import ZKsyncSSO +#if canImport(UIKit) + import UIKit +#endif + struct AccountDetailsView: View { @Environment(\.dismiss) private var dismiss @@ -12,15 +16,19 @@ struct AccountDetailsView: View { @State private var isFunding = false @State private var showingCopiedFeedback = false @State private var showingSendTransaction = false + @State private var showingSessions = false @State private var showingLogoutConfirmation = false + let signers: AccountSigners var onLogout: (() -> Void)? init( account: AccountDetails, + signers: AccountSigners = .default, onLogout: (() -> Void)? = nil ) { self.account = account + self.signers = signers self.onLogout = onLogout } @@ -40,7 +48,9 @@ struct AccountDetailsView: View { .fill(.secondary.opacity(0.1)) } .onTapGesture { - UIPasteboard.general.string = account.address + #if canImport(UIKit) + UIPasteboard.general.string = account.address + #endif withAnimation { showingCopiedFeedback = true } @@ -70,7 +80,9 @@ struct AccountDetailsView: View { icon: "safari.fill", style: .plain, action: { - UIApplication.shared.open(account.explorerURL) + #if canImport(UIKit) + UIApplication.shared.open(account.explorerURL) + #endif } ) } @@ -88,7 +100,9 @@ struct AccountDetailsView: View { .fill(.secondary.opacity(0.1)) } .onTapGesture { - UIPasteboard.general.string = account.uniqueAccountId + #if canImport(UIKit) + UIPasteboard.general.string = account.uniqueAccountId + #endif withAnimation { showingCopiedFeedback = true } @@ -149,6 +163,18 @@ struct AccountDetailsView: View { style: .prominent, action: { showingSendTransaction = true } ) + + VStack(spacing: 12) { + Text("Sessions") + .font(.headline) + + ActionButton( + title: "Sessions", + icon: "list.bullet.rectangle.portrait", + style: .plain, + action: { showingSessions = true } + ) + } } .padding() } @@ -159,16 +185,20 @@ struct AccountDetailsView: View { await loadBalance() } .navigationTitle("Account Details") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button { - showingLogoutConfirmation = true - } label: { - Text("Logout") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + #if os(iOS) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + showingLogoutConfirmation = true + } label: { + Text("Logout") + } } } - } + #endif .confirmationDialog( "Are you sure you want to log out?", isPresented: $showingLogoutConfirmation, @@ -191,6 +221,10 @@ struct AccountDetailsView: View { } ) } + .sheet(isPresented: $showingSessions) { + SessionsView(account: account.account, signers: signers) + .environmentObject(SessionsStore.shared) + } .id("AccountDetailsView") .onAppear { print("AccountDetailsView appeared") } } @@ -202,9 +236,9 @@ struct AccountDetailsView: View { do { let authenticator = PasskeyAuthenticatorHelper( controllerProvider: { self.authorizationController }, - relyingPartyIdentifier: "soo-sdk-example-pages.pages.dev" + relyingPartyIdentifier: "auth-test.zksync.dev" ) - + let accountClient = AccountClient( account: .init( address: account.address, @@ -214,11 +248,11 @@ struct AccountDetailsView: View { authenticator: authenticator ) ) - + let balance = try await accountClient.getAccountBalance() - self.account.balance = balance + account.balance = balance } catch { - self.account.balance = "Error loading balance" + account.balance = "Error loading balance" } } @@ -231,7 +265,7 @@ struct AccountDetailsView: View { do { let authenticator = PasskeyAuthenticatorHelper( controllerProvider: { self.authorizationController }, - relyingPartyIdentifier: "soo-sdk-example-pages.pages.dev" + relyingPartyIdentifier: "auth-test.zksync.dev" ) let accountClient = AccountClient( account: .init( @@ -242,7 +276,7 @@ struct AccountDetailsView: View { authenticator: authenticator ) ) - + try await accountClient.fundAccount() await loadBalance() } catch { @@ -259,7 +293,7 @@ struct AccountDetailsView: View { info: .init( name: "Jane Doe", userID: "jdoe@example.com", - domain: "soo-sdk-example-pages.pages.dev" + domain: "auth-test.zksync.dev" ), address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", uniqueAccountId: "jdoe@example.com" @@ -277,7 +311,7 @@ struct AccountDetailsView: View { info: .init( name: "Jane Doe", userID: "jdoe@example.com", - domain: "soo-sdk-example-pages.pages.dev" + domain: "auth-test.zksync.dev" ), address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", uniqueAccountId: "jdoe@example.com" @@ -297,7 +331,7 @@ struct AccountDetailsView: View { info: .init( name: "Jane Doe", userID: "jdoe@example.com", - domain: "soo-sdk-example-pages.pages.dev" + domain: "auth-test.zksync.dev" ), address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", uniqueAccountId: "jdoe@example.com" @@ -312,7 +346,7 @@ struct AccountDetailsView: View { info: .init( name: "Jane Doe", userID: "jdoe@example.com", - domain: "soo-sdk-example-pages.pages.dev" + domain: "auth-test.zksync.dev" ), address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", uniqueAccountId: "jdoe@example.com" diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountInfo.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountInfo.swift index 8e4b45cc..4dff85a7 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountInfo.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/AccountInfo.swift @@ -15,9 +15,9 @@ struct DeployedAccount: Identifiable, Codable, Hashable, AccountInfoProviding { let info: AccountInfo let address: String let uniqueAccountId: String - + var name: String { info.name } var userID: String { info.userID } - + var id: String { address } } diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/LoginView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/LoginView.swift index 05e4f184..7135ab58 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/LoginView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/LoginView.swift @@ -8,13 +8,13 @@ public struct LoginView: View { @State private var accountInfo: AccountInfo @FocusState private var isFocused: Bool @State private var isSigningIn = false - @State private var error: String? + @State private var error: UIError? - let onSignedIn: ((AccountDetails) -> Void)? + let onSignedIn: ((AccountSession) -> Void)? init( accountInfo: AccountInfo, - onSignedIn: ((AccountDetails) -> Void)? = nil + onSignedIn: ((AccountSession) -> Void)? = nil ) { self.accountInfo = accountInfo self.onSignedIn = onSignedIn @@ -40,11 +40,11 @@ public struct LoginView: View { TextField("Enter your user ID", text: $accountInfo.userID) .focused($isFocused) .padding() - .background(Color(.systemGray6)) + .background(Color(uiColor: .systemGray6)) .cornerRadius(10) .font(.system(.body, design: .monospaced)) .autocorrectionDisabled() - .textInputAutocapitalization(.never) + .autocapitalization(.none) if isFocused && !accountInfo.userID.isEmpty { Button { @@ -61,11 +61,12 @@ public struct LoginView: View { } .animation( .easeInOut(duration: 0.2), - value: isFocused && !accountInfo.userID.isEmpty) + value: isFocused && !accountInfo.userID.isEmpty + ) } if let error = error { - Text(error) + Text(error.message) .foregroundStyle(.red) .font(.footnote) .padding(.top, 4) @@ -105,7 +106,7 @@ public struct LoginView: View { private func signIn() { guard !accountInfo.userID.isEmpty else { - error = "Please enter your user ID" + error = UIError(message: "Please enter your user ID") return } @@ -118,7 +119,7 @@ public struct LoginView: View { do { let uniqueAccountId = accountInfo.userID let relyingPartyIdentifier = accountInfo.domain - + let account = try await getAccountByUserId( uniqueAccountId: uniqueAccountId, relyingPartyIdentifier: relyingPartyIdentifier, @@ -132,10 +133,14 @@ public struct LoginView: View { ) ) - onSignedIn?(accountDetails) + onSignedIn?( + AccountSession( + accountDetails: accountDetails, + signers: .default + )) dismiss() } catch { - self.error = error.localizedDescription + self.error = UIError(from: error) print("Sign in error: \(error)") } } diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/Session.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/Session.swift new file mode 100644 index 00000000..5ca2cc06 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/Session.swift @@ -0,0 +1,77 @@ +import SwiftUI +import ZKsyncSSO + +struct Session: Identifiable, Hashable { + var id: String { + return sessionHash + } + + let createdAt: Date + let sessionHash: String + let sessionSpec: SessionSpec + + public init( + createdAt: Date, + sessionSpec: SessionSpec + ) { + self.createdAt = createdAt + self.sessionHash = try! sessionSpec.sessionHash() + self.sessionSpec = sessionSpec + } +} + +extension SessionSpec { + static var `default`: SessionSpec { + return SessionSpec( + signer: "0x9BbC92a33F193174bf6Cc09c4b4055500d972479", + expiresAt: String(Int(Date().addingTimeInterval(86400).timeIntervalSince1970)), // 24 hours + feeLimit: UsageLimit( + limitType: .lifetime, + limit: "100000000000000000", + period: "0" + ), + callPolicies: [], + transferPolicies: [ + TransferSpec( + target: "0xdeBbD4CE2Bd6BD869D3ac93666A0D5F4fc06FC72", + maxValuePerUse: "10000000000000000", + valueLimit: UsageLimit( + limitType: .unlimited, + limit: "0", + period: "0" + ) + ) + ] + ) + } + + static func initialSession(sessionOwner: EOASigner) -> SessionSpec { + return SessionSpec( + signer: sessionOwner.address, + expiresAt: String(Int(Date().addingTimeInterval(86400).timeIntervalSince1970)), // 24 hours + feeLimit: UsageLimit( + limitType: .lifetime, + limit: "100000000000000000", + period: "0" + ), + callPolicies: [], + transferPolicies: [ + TransferSpec( + target: "0xdeBbD4CE2Bd6BD869D3ac93666A0D5F4fc06FC72", + maxValuePerUse: "10000000000000000", + valueLimit: UsageLimit( + limitType: .unlimited, + limit: "0", + period: "0" + ) + ) + ] + ) + } + + func with(expiry: Date) -> SessionSpec { + var copy = self + copy.expiresAt = String(Int(expiry.timeIntervalSince1970)) + return copy + } +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionCreationView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionCreationView.swift new file mode 100644 index 00000000..d4819327 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionCreationView.swift @@ -0,0 +1,139 @@ +import SwiftUI +import ZKsyncSSO + +struct SessionCreationView: View { + let accountAddress: String + let signers: AccountSigners + let onSessionCreated: (Session) -> Void + + @Environment(\.dismiss) private var dismiss + @State private var sessionSpec = SessionSpec.default + @State private var isCreating = false + @State private var error: UIError? + + init( + accountAddress: String, + signers: AccountSigners, + onSessionCreated: @escaping (Session) -> Void, + error: UIError? = nil + ) { + self.accountAddress = accountAddress + self.signers = signers + self.onSessionCreated = onSessionCreated + self._error = State(initialValue: error) + } + + private var sessionConfigJson: String { + try! sessionSpec.toJsonString(pretty: true) + } + + var body: some View { + NavigationStack { + Form { + Section { + SessionSpecDetailsView(sessionSpec: sessionSpec) + } + .listRowInsets(EdgeInsets()) + + if let error = error { + Section { + Text(error.message) + .foregroundStyle(.red) + .font(.footnote) + } + .listRowInsets(EdgeInsets()) + .listRowBackground(Color.clear) + } + + Section { + Button(action: { Task { await createSession() } }) { + HStack(spacing: 8) { + Spacer() + if isCreating { + ProgressView() + .padding(.trailing, 4) + } + + Image(systemName: "plus.circle.fill") + + Text(isCreating ? "Creating Session..." : "Create Session") + .font(.headline) + Spacer() + } + .frame(maxWidth: .infinity) + .frame(height: 44) + } + .disabled(isCreating) + .buttonStyle(.borderedProminent) + } + .listRowInsets(EdgeInsets()) + .listRowBackground(Color.clear) + } + .navigationTitle("New Session") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + #endif + #if os(iOS) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button("Cancel") { + dismiss() + } + .disabled(isCreating) + } + } + #endif + } + } + + private func createSession() async { + guard !isCreating else { return } + isCreating = true + error = nil + defer { isCreating = false } + + do { + let args = CreateSessionArgs( + account: accountAddress, + sessionConfig: sessionSpec, + ownerPrivateKey: signers.accountOwner.privateKeyHex, + paymaster: nil + ) + _ = try await ZKsyncSSO.createSession( + args: args, + config: Config.default + ) + + let newSession = Session( + createdAt: Date(), + sessionSpec: sessionSpec + ) + onSessionCreated(newSession) + + dismiss() + } catch { + self.error = UIError(from: error) + print("Failed to create session: \(error)") + } + } +} + +#Preview { + SessionCreationView( + accountAddress: "0x1234567890abcdef", + signers: .default, + onSessionCreated: { _ in } + ) +} + +#Preview("Error State") { + SessionCreationView( + accountAddress: "0x1234567890abcdef", + signers: .default, + onSessionCreated: { _ in }, + error: UIError( + message: + "Failed to create session: ZKsyncSSOFFI.CreateSessionError.CreateSession(\"server returned an error response: error code 3: execution reverted: Error function_selector = 0x12345678, data = 0x12345678, data: \"0x12345678\"\")" + ) + ) +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionDetailView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionDetailView.swift new file mode 100644 index 00000000..5e8dfff2 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionDetailView.swift @@ -0,0 +1,167 @@ +import ExamplePackageUIComponents +import SwiftUI +import ZKsyncSSO + +struct SessionDetailView: View { + let session: Session + let account: DeployedAccount + let signers: AccountSigners + + @State private var showingRevokeConfirm = false + @State private var isRevoking = false + @State private var error: UIError? + + init( + session: Session, account: DeployedAccount, signers: AccountSigners, + error: UIError? = nil + ) { + self.session = session + self.account = account + self.signers = signers + self._error = State(initialValue: error) + } + + var body: some View { + Form { + Section { + VStack(alignment: .leading, spacing: 16) { + Text("Session Details") + .font(.headline) + + SessionSpecSummaryView(sessionSpec: session.sessionSpec) + + SessionSpecDetailsJSONView(sessionSpec: session.sessionSpec) + } + .padding() + .background(.background) + } + .listRowInsets(EdgeInsets()) + .listRowBackground(Color.clear) + + if let error = error { + Section { + Text(error.message) + .foregroundStyle(.red) + .font(.footnote) + } + .listRowInsets(EdgeInsets()) + .listRowBackground(Color.clear) + } + + Section { + Button(action: { showingRevokeConfirm = true }) { + HStack(spacing: 8) { + Spacer() + if isRevoking { + ProgressView() + .padding(.trailing, 4) + } + + Image(systemName: "trash.fill") + + Text(isRevoking ? "Revoking Session..." : "Revoke Session") + .font(.headline) + Spacer() + } + .frame(maxWidth: .infinity) + .frame(height: 44) + } + .disabled(isRevoking) + .buttonStyle(.borderedProminent) + .tint(.red) + } + .listRowInsets(EdgeInsets()) + .listRowBackground(Color.clear) + } + .navigationTitle(shortHash(session.sessionHash)) + .confirmationDialog( + "Revoke this session?", + isPresented: $showingRevokeConfirm, + titleVisibility: .visible + ) { + Button("Revoke", role: .destructive) { + Task { await revoke() } + } + Button("Cancel", role: .cancel) {} + } message: { + Text("This action cannot be undone.") + } + } + + private func revoke() async { + guard !isRevoking else { return } + isRevoking = true + error = nil + defer { isRevoking = false } + do { + let args = RevokeSessionArgs( + account: account.address, + sessionId: session.id, + ownerPrivateKey: signers.accountOwner.privateKeyHex + ) + _ = try await revokeSession(args: args, config: .default) + } catch { + self.error = UIError(from: error) + } + } + + private func shortHash(_ hash: String) -> String { + guard hash.count > 10 else { return hash } + let start = hash.prefix(6) + let end = hash.suffix(4) + return String(start + "…" + end) + } + + private func format(date: Date) -> String { + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .short + return formatter.string(from: date) + } +} + +#Preview { + NavigationStack { + SessionDetailView( + session: .init( + createdAt: Date(), + sessionSpec: SessionSpec.default + ), + account: .init( + info: .init( + name: "Jane Doe", + userID: "jdoe@example.com", + domain: "auth-test.zksync.dev" + ), + address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + uniqueAccountId: "jdoe@example.com" + ), + signers: .default + ) + } +} + +#Preview("Error State") { + NavigationStack { + SessionDetailView( + session: .init( + createdAt: Date(), + sessionSpec: SessionSpec.default + ), + account: .init( + info: .init( + name: "Jane Doe", + userID: "jdoe@example.com", + domain: "auth-test.zksync.dev" + ), + address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + uniqueAccountId: "jdoe@example.com" + ), + signers: .default, + error: UIError( + message: + "Failed to revoke session: ZKsyncSSOFFI.RevokeSessionError.RevokeSession(\"server returned an error response: error code 3: execution reverted: Error function_selector = 0x837529ed, data = 0x837529ed, data: \"0x837529ed\"\")" + ) + ) + } +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecDetailsJSONView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecDetailsJSONView.swift new file mode 100644 index 00000000..9e7d2937 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecDetailsJSONView.swift @@ -0,0 +1,70 @@ +import ExamplePackageUIComponents +import SwiftUI +import ZKsyncSSO + +#if os(iOS) + import UIKit +#endif + +struct SessionSpecDetailsJSONView: View { + let sessionSpec: SessionSpec + @State private var showCopiedToast = false + + private var sessionConfigJson: String { + try! sessionSpec.toJsonString(pretty: true) + } + + var body: some View { + ZStack { + VStack(alignment: .leading, spacing: 8) { + Text("JSON Configuration") + .font(.subheadline) + .fontWeight(.medium) + + Text(sessionConfigJson) + .font(.system(.caption, design: .monospaced)) + .textSelection(.enabled) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(12) + // Use a semantic system background for code block look + #if os(iOS) + .background(Color(UIColor.systemGray6)) + #else + .background(Color.gray.opacity(0.12)) + #endif + .cornerRadius(8) + .contentShape(Rectangle()) + .onTapGesture { + #if os(iOS) + UIPasteboard.general.string = sessionConfigJson + #endif + withAnimation(.spring(response: 0.25, dampingFraction: 0.9)) { + showCopiedToast = true + } + DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { + withAnimation(.easeOut(duration: 0.2)) { + showCopiedToast = false + } + } + } + } + + if showCopiedToast { + ToastView( + icon: "doc.on.doc.fill", + iconColor: .blue, + message: "Session config copied" + ) + .padding() + .transition(.scale.combined(with: .opacity)) + } + } + } +} + +#Preview { + SessionSpecDetailsJSONView( + sessionSpec: SessionSpec.default + ) + .padding() +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecDetailsView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecDetailsView.swift new file mode 100644 index 00000000..ecd6de98 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecDetailsView.swift @@ -0,0 +1,31 @@ +import SwiftUI +import ZKsyncSSO + +struct SessionSpecDetailsView: View { + let sessionSpec: SessionSpec + + private var sessionConfigJson: String { + try! sessionSpec.toJsonString(pretty: true) + } + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + Text("Session Details") + .font(.headline) + + SessionSpecSummaryView(sessionSpec: sessionSpec) + + SessionSpecDetailsJSONView(sessionSpec: sessionSpec) + } + .padding() + .frame(maxWidth: .infinity, alignment: .leading) + } +} + +#Preview { + ScrollView { + SessionSpecDetailsView( + sessionSpec: SessionSpec.default + ) + } +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecSummaryDetailRowView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecSummaryDetailRowView.swift new file mode 100644 index 00000000..fac61584 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecSummaryDetailRowView.swift @@ -0,0 +1,42 @@ +import SwiftUI + +struct SessionSpecSummaryDetailRowView: View { + let label: String + let value: String + + var body: some View { + HStack(spacing: 8) { + Text(label + ":") + .fontWeight(.medium) + .foregroundStyle(.secondary) + + // Expanding container for the value that consumes remaining space + HStack { + Spacer() + Text(value) + .lineLimit(1) + .truncationMode(.middle) + } + .frame(maxWidth: .infinity, alignment: .trailing) + } + .frame(maxWidth: .infinity, alignment: .leading) + } +} + +#Preview { + VStack(alignment: .leading, spacing: 12) { + SessionSpecSummaryDetailRowView( + label: "Signer", + value: "0x9BbC92a33DfeE0b40D16F7b3B1a55500d972479" + ) + SessionSpecSummaryDetailRowView( + label: "Expires At", + value: "15 Aug 2025 at 13:18" + ) + SessionSpecSummaryDetailRowView( + label: "Fee Limit", + value: "lifetime - 1000...000000000000" + ) + } + .padding() +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecSummaryView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecSummaryView.swift new file mode 100644 index 00000000..2ab67827 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionSpecSummaryView.swift @@ -0,0 +1,46 @@ +import SwiftUI +import ZKsyncSSO + +struct SessionSpecSummaryView: View { + let sessionSpec: SessionSpec + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + SessionSpecSummaryDetailRowView(label: "Signer", value: sessionSpec.signer) + SessionSpecSummaryDetailRowView( + label: "Expires At", value: formatTimestamp(sessionSpec.expiresAt) + ) + SessionSpecSummaryDetailRowView( + label: "Fee Limit", + value: "\(sessionSpec.feeLimit.limitType) - \(sessionSpec.feeLimit.limit)" + ) + SessionSpecSummaryDetailRowView( + label: "Call Policies", value: "\(sessionSpec.callPolicies.count) policies" + ) + SessionSpecSummaryDetailRowView( + label: "Transfer Policies", value: "\(sessionSpec.transferPolicies.count) policies" + ) + } + } + + private func formatTimestamp(_ timestamp: String) -> String { + guard let timeInterval = TimeInterval(timestamp) else { + return timestamp + } + let date = Date(timeIntervalSince1970: timeInterval) + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .short + return formatter.string(from: date) + } +} + +#Preview { + Form { + Section(header: Text("Session Spec")) { + SessionSpecSummaryView( + sessionSpec: SessionSpec.default + ) + } + } +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsEmptyView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsEmptyView.swift new file mode 100644 index 00000000..2e5743c9 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsEmptyView.swift @@ -0,0 +1,37 @@ +import SwiftUI +import ZKsyncSSO + +struct SessionsEmptyView: View { + let onCreateTapped: () -> Void + + var body: some View { + VStack(spacing: 16) { + Image(systemName: "rectangle.stack.badge.plus") + .font(.system(size: 48)) + .foregroundStyle(.secondary) + + Text("No Sessions Yet") + .font(.title3) + + Text( + "Create a session to authorize limited actions without re-authenticating each time." + ) + .font(.subheadline) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal) + + Button(action: onCreateTapped) { + Label("Create Session", systemImage: "plus") + } + .buttonStyle(.borderedProminent) + .padding(.top, 8) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding() + } +} + +#Preview { + SessionsEmptyView(onCreateTapped: {}) +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsListView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsListView.swift new file mode 100644 index 00000000..ad243d17 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsListView.swift @@ -0,0 +1,74 @@ +import SwiftUI +import ZKsyncSSO + +struct SessionsListView: View { + let sessions: [Session] + let account: DeployedAccount + let signers: AccountSigners + let onSelect: (Session) -> Void + + var body: some View { + List { + Section(header: Text("Active Sessions")) { + ForEach(sessions) { session in + NavigationLink(value: session) { + HStack { + VStack(alignment: .leading, spacing: 4) { + Text(shortHash(session.sessionHash)) + .font(.headline) + .lineLimit(1) + .truncationMode(.middle) + Text("Created \(format(date: session.createdAt))") + .font(.caption) + .foregroundStyle(.secondary) + } + } + } + } + } + } + .navigationDestination(for: Session.self) { session in + SessionDetailView( + session: session, + account: account, + signers: signers + ) + } + } + + private func shortHash(_ hash: String) -> String { + guard hash.count > 10 else { return hash } + let start = hash.prefix(6) + let end = hash.suffix(4) + return String(start + "…" + end) + } + + private func format(date: Date) -> String { + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .short + return formatter.string(from: date) + } +} + +#Preview { + SessionsListView( + sessions: [ + .init( + createdAt: Date(), + sessionSpec: SessionSpec.default + ) + ], + account: .init( + info: .init( + name: "Jane Doe", + userID: "jdoe@example.com", + domain: "auth-test.zksync.dev" + ), + address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + uniqueAccountId: "jdoe@example.com" + ), + signers: .default, + onSelect: { _ in } + ) +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsStore.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsStore.swift new file mode 100644 index 00000000..51c75396 --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsStore.swift @@ -0,0 +1,30 @@ +import SwiftUI + +@MainActor +class SessionsStore: ObservableObject { + + static let shared = SessionsStore() + + @Published private var sessionsByAccount: [String: [Session]] = [:] + + private init() {} + + static func preview() -> SessionsStore { + return SessionsStore() + } + + func getSessions(for accountAddress: String) -> [Session] { + return sessionsByAccount[accountAddress] ?? [] + } + + func addSession(_ session: Session, for accountAddress: String) { + if sessionsByAccount[accountAddress] == nil { + sessionsByAccount[accountAddress] = [] + } + sessionsByAccount[accountAddress]?.append(session) + } + + func removeSession(withId sessionId: String, for accountAddress: String) { + sessionsByAccount[accountAddress]?.removeAll { $0.id == sessionId } + } +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsView.swift new file mode 100644 index 00000000..77933c2b --- /dev/null +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Account/Sessions/SessionsView.swift @@ -0,0 +1,159 @@ +import SwiftUI +import ZKsyncSSO + +struct SessionsView: View { + let account: DeployedAccount + let signers: AccountSigners + + @EnvironmentObject private var sessionsStore: SessionsStore + @State private var showingCreateSession = false + + private var sessions: [Session] { + sessionsStore.getSessions(for: account.address) + } + + var body: some View { + NavigationStack { + Group { + if sessions.isEmpty { + SessionsEmptyView( + onCreateTapped: { showingCreateSession = true } + ) + } else { + SessionsListView( + sessions: sessions, + account: account, + signers: signers, + onSelect: { _ in } + ) + } + } + .navigationTitle("Sessions") + #if os(iOS) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + showingCreateSession = true + } label: { + Image(systemName: "plus") + } + } + } + #endif + .task { + // TODO: refresh sessions + } + .sheet(isPresented: $showingCreateSession) { + SessionCreationView( + accountAddress: account.address, + signers: signers, + onSessionCreated: { newSession in + sessionsStore.addSession(newSession, for: account.address) + showingCreateSession = false + } + ) + } + } + } +} + +#Preview("Empty State") { + SessionsView( + account: .init( + info: .init( + name: "Jane Doe", userID: "jdoe@example.com", + domain: "auth-test.zksync.dev" + ), + address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + uniqueAccountId: "jdoe@example.com" + ), + signers: .default + ) + .environmentObject(SessionsStore.shared) +} + +#Preview("1 Session") { + let store = SessionsStore.preview() + let account = DeployedAccount( + info: .init( + name: "Jane Doe", userID: "jdoe@example.com", + domain: "auth-test.zksync.dev" + ), + address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + uniqueAccountId: "jdoe@example.com" + ) + + store.addSession( + Session( + createdAt: Date(), + sessionSpec: SessionSpec.default + ), + for: account.address + ) + + return SessionsView( + account: account, + signers: .default + ) + .environmentObject(store) +} + +#Preview("5 Sessions") { + let store = SessionsStore.preview() + let account = DeployedAccount( + info: .init( + name: "Jane Doe", userID: "jdoe@example.com", + domain: "auth-test.zksync.dev" + ), + address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", + uniqueAccountId: "jdoe@example.com" + ) + + let sessions = [ + // Session 1: Created 2 days ago, expires in 7 days from creation (5 days from now) + Session( + createdAt: Date().addingTimeInterval(-86400 * 2), + sessionSpec: SessionSpec.default.with( + expiry: Date().addingTimeInterval(-86400 * 2 + 86400 * 7) + ) + ), + // Session 2: Created 1 day ago, expires in 5 days from creation (4 days from now) + Session( + createdAt: Date().addingTimeInterval(-86400), + sessionSpec: SessionSpec.default.with( + expiry: Date().addingTimeInterval(-86400 + 86400 * 5) + ) + ), + // Session 3: Created 12 hours ago, expires in 3 days from creation (2.5 days from now) + Session( + createdAt: Date().addingTimeInterval(-43200), + sessionSpec: SessionSpec.default.with( + expiry: Date().addingTimeInterval(-43200 + 86400 * 3) + ) + ), + // Session 4: Created 6 hours ago, expires in 2 days from creation (1.75 days from now) + Session( + createdAt: Date().addingTimeInterval(-21600), + sessionSpec: SessionSpec.default.with( + expiry: Date().addingTimeInterval(-21600 + 86400 * 2) + ) + ), + // Session 5: Created 1 hour ago, expires in 1 day from creation (23 hours from now) + Session( + createdAt: Date().addingTimeInterval(-3600), + sessionSpec: SessionSpec.default.with( + expiry: Date().addingTimeInterval(-3600 + 86400) + ) + ), + ] + + for session in sessions { + store.addSession(session, for: account.address) + } + + return SessionsView( + account: account, + signers: .default + ) + .environmentObject(store) +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/SendTransactionView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/SendTransactionView.swift index 850050ce..44ffcb48 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/SendTransactionView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/SendTransactionView.swift @@ -3,21 +3,20 @@ import SwiftUI import ZKsyncSSO struct SendTransactionView: View { - let fromAccount: DeployedAccount - + @Environment(\.dismiss) private var dismiss - + @Environment(\.authorizationController) private var authorizationController @State private var toAddress: String = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" @State private var amount: String = "1.0" @State private var isConfirming = false - @State private var error: String? + @State private var error: UIError? @State private var showingSuccess = false @State private var preparedTransaction: PreparedTransaction? @State private var isPreparing = false - + private let onTransactionSent: () -> Void init( @@ -48,14 +47,14 @@ struct SendTransactionView: View { if let error = error { Section { Button { - UIPasteboard.general.string = error + UIPasteboard.general.string = error.message } label: { HStack { - Text(error) + Text(error.message) .foregroundStyle(.red) - + Spacer() - + Image(systemName: "doc.on.doc") .foregroundStyle(.secondary) .font(.system(size: 14)) @@ -124,16 +123,16 @@ struct SendTransactionView: View { return } print("prepareTransaction amountInEth: \(amountInEth)") - + let amountInWei = String(Int(amountInEth * 1_000_000_000_000_000_000.0)) - + print("prepareTransaction amountInWei: \(amountInWei)") - + let authenticator = PasskeyAuthenticatorHelper( controllerProvider: { self.authorizationController }, - relyingPartyIdentifier: "soo-sdk-example-pages.pages.dev" + relyingPartyIdentifier: "auth-test.zksync.dev" ) - + let accountClient = AccountClient( account: .init( address: fromAccount.address, @@ -153,22 +152,22 @@ struct SendTransactionView: View { to: toAddress, value: amountInWei ) - + print("prepareTransaction transaction: \(transaction)") - + let prepared = try await accountClient.prepare( transaction: transaction ) - + print("prepareTransaction prepared: \(prepared)") - + print( "Prepared transaction JSON: \(prepared.transactionRequestJson)" ) preparedTransaction = prepared error = nil } catch { - self.error = error.localizedDescription + self.error = UIError(from: error) preparedTransaction = nil print("Error preparing transaction: \(error)") } @@ -178,17 +177,17 @@ struct SendTransactionView: View { private func confirmTransaction() { print("confirmTransaction") guard let amountInEth = Double(amount) else { return } - + let amountInWei = String(Int(amountInEth * 1_000_000_000_000_000_000.0)) isConfirming = true error = nil - + let authenticator = PasskeyAuthenticatorHelper( controllerProvider: { self.authorizationController }, - relyingPartyIdentifier: "soo-sdk-example-pages.pages.dev" + relyingPartyIdentifier: "auth-test.zksync.dev" ) - + let accountClient = AccountClient( account: .init( address: fromAccount.address, @@ -208,7 +207,7 @@ struct SendTransactionView: View { value: amountInWei ) ) - + withAnimation { showingSuccess = true } @@ -219,7 +218,7 @@ struct SendTransactionView: View { dismiss() } catch { - self.error = error.localizedDescription + self.error = UIError(from: error) print(error) isConfirming = false print("Error preparing transaction: \(error)") diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/TransactionFormFields.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/TransactionFormFields.swift index e5edf627..09f9834e 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/TransactionFormFields.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackage/Views/Transaction/TransactionFormFields.swift @@ -1,5 +1,5 @@ -import SwiftUI import ExamplePackageUIComponents +import SwiftUI struct TransactionFormFields: View { let fromAddress: String diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ActionButton.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ActionButton.swift index d572f7a4..853b744e 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ActionButton.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ActionButton.swift @@ -50,25 +50,25 @@ public struct ActionButton: View { Section { style.applyStyle( to: - Button(action: action) { - HStack(spacing: 8) { - Spacer() - if isLoading { - ProgressView() - .padding(.trailing, 4) - } - - if let icon = icon { - Image(systemName: icon) - } + Button(action: action) { + HStack(spacing: 8) { + Spacer() + if isLoading { + ProgressView() + .padding(.trailing, 4) + } - Text(isLoading ? (progressTitle ?? title) : title) - .font(.headline) - Spacer() + if let icon = icon { + Image(systemName: icon) } - .frame(maxWidth: .infinity) - .frame(height: 44) + + Text(isLoading ? (progressTitle ?? title) : title) + .font(.headline) + Spacer() } + .frame(maxWidth: .infinity) + .frame(height: 44) + } ) .listRowInsets(EdgeInsets()) .listRowBackground(Color.clear) diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/AddressView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/AddressView.swift index de46316e..53d63871 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/AddressView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/AddressView.swift @@ -3,11 +3,11 @@ import SwiftUI public struct AddressView: View { let address: String @State private var showingCopiedFeedback = false - + public init(address: String) { self.address = address } - + public var body: some View { Text(address) .lineLimit(1) @@ -48,4 +48,4 @@ public struct AddressView: View { #Preview { AddressView(address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") .padding() -} +} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ToastView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ToastView.swift index c86f0177..07d852fc 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ToastView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Sources/ExamplePackageUIComponents/ToastView.swift @@ -4,7 +4,7 @@ public struct ToastView: View { let icon: String let iconColor: Color let message: String - + public init(icon: String, iconColor: Color, message: String) { self.icon = icon self.iconColor = iconColor diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Tests/ExamplePackageTests/ExamplePackageTests.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Tests/ExamplePackageTests/ExamplePackageTests.swift index d6228470..4169d38b 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Tests/ExamplePackageTests/ExamplePackageTests.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExamplePackage/Tests/ExamplePackageTests/ExamplePackageTests.swift @@ -1,6 +1,4 @@ -import Testing @testable import ExamplePackage +import Testing -@Test func example() async throws { - -} +@Test func example() async throws {} diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExample/ContentView.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExample/ContentView.swift index b183276d..8e170b7e 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExample/ContentView.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExample/ContentView.swift @@ -1,5 +1,5 @@ -import SwiftUI import ExamplePackage +import SwiftUI struct ContentView: View { var body: some View { diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleTests/SSOExampleTests.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleTests/SSOExampleTests.swift index dc35409c..5688a158 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleTests/SSOExampleTests.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleTests/SSOExampleTests.swift @@ -1,10 +1,8 @@ -import Testing @testable import SSOExample +import Testing struct SSOExampleTests { - @Test func example() async throws { // Write your test here and use APIs like `#expect(...)` to check expected conditions. } - } diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITests.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITests.swift index 33b6e81c..7380c85c 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITests.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITests.swift @@ -1,7 +1,6 @@ import XCTest final class SSOExampleUITests: XCTestCase { - override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. diff --git a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITestsLaunchTests.swift b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITestsLaunchTests.swift index 2910808a..47344b38 100644 --- a/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITestsLaunchTests.swift +++ b/packages/sdk-platforms/swift/Examples/ExampleApp/ExampleiOS/SSOExampleUITests/SSOExampleUITestsLaunchTests.swift @@ -1,7 +1,6 @@ import XCTest final class ExampleUITestsLaunchTests: XCTestCase { - override class var runsForEachTargetApplicationUIConfiguration: Bool { true } diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Package.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Package.swift index e16259d4..6047a503 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Package.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Package.swift @@ -10,23 +10,28 @@ let package = Package( products: [ .library( name: "ZKsyncSSO", - targets: ["ZKsyncSSO"]) + targets: ["ZKsyncSSO"] + ), ], targets: [ .target( name: "ZKsyncSSO", dependencies: ["ZKsyncSSOFFI"], resources: [ - .copy("Config/config.json") - ]), + .copy("Config/config.json"), + ] + ), .target( name: "ZKsyncSSOFFI", - dependencies: ["ZKsyncSSOCore"]), + dependencies: ["ZKsyncSSOCore"] + ), .binaryTarget( name: "ZKsyncSSOCore", - path: "../../rust/zksync-sso/crates/ffi/out/ZKsyncSSOCore.xcframework"), + path: "../../rust/zksync-sso/crates/ffi/out/ZKsyncSSOCore.xcframework" + ), .testTarget( name: "ZKsyncSSOTests", - dependencies: ["ZKsyncSSO"]), + dependencies: ["ZKsyncSSO"] + ), ] ) diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/AccountClient.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/AccountClient.swift index 4c8989c8..1b97691f 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/AccountClient.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/AccountClient.swift @@ -2,11 +2,10 @@ import Foundation @preconcurrency import ZKsyncSSOFFI public struct AccountClient: Sendable { - public let authenticatorAsync: any PasskeyAuthenticatorAsync & Sendable - + public let account: Account - + public init( account: Account, authenticatorAsync: any PasskeyAuthenticatorAsync & Sendable @@ -14,7 +13,7 @@ public struct AccountClient: Sendable { self.account = account self.authenticatorAsync = authenticatorAsync } - + public func getAccountBalance() async throws -> String { let accountBalance = try await ZKsyncSSOFFI.getBalance( address: account.address, @@ -22,14 +21,14 @@ public struct AccountClient: Sendable { ) return accountBalance.balance } - + public func fundAccount() async throws { try await ZKsyncSSOFFI.fundAccount( address: account.address, config: Config.default.inner ) } - + @discardableResult public func send( transaction: TransactionRequest @@ -45,7 +44,7 @@ public struct AccountClient: Sendable { ) return result.txHash } - + public func prepare( transaction: TransactionRequest ) async throws -> PreparedTransaction { diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/Config.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/Config.swift index 0e7667a0..556ff852 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/Config.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/Config.swift @@ -9,7 +9,7 @@ public struct Config { nodeUrl: String, deployWallet: DeployWallet? ) { - self.inner = .init( + inner = .init( contracts: contracts.inner, nodeUrl: nodeUrl, deployWallet: deployWallet @@ -22,10 +22,10 @@ public struct Config { contracts: SsoContracts(inner: innerDefault.contracts), nodeUrl: innerDefault.nodeUrl, deployWallet: { - guard let privateKeyHex = innerDefault.deployWallet?.privateKeyHex else { - return nil - } - return DeployWallet(privateKeyHex: privateKeyHex) + guard let privateKeyHex = innerDefault.deployWallet?.privateKeyHex else { + return nil + } + return DeployWallet(privateKeyHex: privateKeyHex) }() ) } diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/PasskeyContracts.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/PasskeyContracts.swift index 572a2fd6..8e89c34a 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/PasskeyContracts.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Config/PasskeyContracts.swift @@ -3,7 +3,7 @@ import ZKsyncSSOFFI public struct SsoContracts { let inner: ZKsyncSSOFFI.SsoContracts - + public init( accountFactory: String, passkey: String, @@ -11,7 +11,7 @@ public struct SsoContracts { accountPaymaster: String, recovery: String ) { - self.inner = .init( + inner = .init( accountFactory: accountFactory, passkey: passkey, session: session, @@ -19,7 +19,7 @@ public struct SsoContracts { recovery: recovery ) } - + init(inner: ZKsyncSSOFFI.SsoContracts) { self.inner = inner } diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/DeployAccount.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/DeployAccount.swift index b2c8b360..b4f89f04 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/DeployAccount.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/DeployAccount.swift @@ -7,19 +7,25 @@ public struct DeployAccountParameters { var credentialId: Data var rpId: String var uniqueAccountId: String - + var initialK1Owners: [String]? + var initialSessionConfigJson: String? + public init( credentialRawAttestationObject: Data, credentialRawClientDataJson: Data, credentialId: Data, rpId: String, - uniqueAccountId: String + uniqueAccountId: String, + initialK1Owners: [String]?, + initialSessionConfigJson: String? ) { self.credentialRawAttestationObject = credentialRawAttestationObject self.credentialRawClientDataJson = credentialRawClientDataJson self.credentialId = credentialId self.rpId = rpId self.uniqueAccountId = uniqueAccountId + self.initialK1Owners = initialK1Owners + self.initialSessionConfigJson = initialSessionConfigJson } } @@ -32,17 +38,17 @@ public func deployAccountWith( credentialId: params.credentialId, rpId: .apple(params.rpId), ) - - let uniqueAccountId = params.uniqueAccountId - + let account = try await ZKsyncSSOFFI.deployAccountWithUniqueId( - passkeyParameters: passkeyParameters, - uniqueAccountId: params.uniqueAccountId, - config: Config.default.inner + passkeyParameters: passkeyParameters, + uniqueAccountId: params.uniqueAccountId, + initialK1Owners: nil, + initialSessionConfigJson: nil, + config: Config.default.inner ) - + print("account: \(account)") - + return Account( address: account.address, uniqueAccountId: account.uniqueAccountId @@ -52,24 +58,23 @@ public func deployAccountWith( public func deployAccountWithUniqueId( params: DeployAccountParameters ) async throws -> Account { - let passkeyParameters = PasskeyParameters( credentialRawAttestationObject: params.credentialRawAttestationObject, credentialRawClientDataJson: params.credentialRawClientDataJson, credentialId: params.credentialId, rpId: .apple(params.rpId), ) - + let uniqueAccountId = params.uniqueAccountId - + let account = try await ZKsyncSSOFFI.deployAccountWithUniqueId( passkeyParameters: passkeyParameters, uniqueAccountId: uniqueAccountId, + initialK1Owners: nil, + initialSessionConfigJson: nil, config: Config.default.inner ) - - print("account: \(account)") - + return Account( address: account.address, uniqueAccountId: account.uniqueAccountId @@ -79,7 +84,7 @@ public func deployAccountWithUniqueId( public struct Account: Sendable { public var address: String public var uniqueAccountId: String - + public init(address: String, uniqueAccountId: String) { self.address = address self.uniqueAccountId = uniqueAccountId diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthentication.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthentication.swift index e584dfa8..7f91cbd2 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthentication.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthentication.swift @@ -1,5 +1,5 @@ -import Foundation import AuthenticationServices +import Foundation import SwiftUI @Sendable func performPasskeyAuthorizationRequest( @@ -46,4 +46,3 @@ private func performASPasskeyAuthorizationRequest( print("performASPasskeyAuthorizationRequest passkeyAssertion: \(passkeyAssertion)") return passkeyAssertion } - diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthenticatorHelper.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthenticatorHelper.swift index 66230c7c..8aaad2b0 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthenticatorHelper.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Authentication/PasskeyAuthenticatorHelper.swift @@ -1,5 +1,5 @@ -import Foundation import AuthenticationServices +import Foundation import SwiftUI public protocol PasskeyAsyncHelperAPI: Sendable { @@ -8,16 +8,15 @@ public protocol PasskeyAsyncHelperAPI: Sendable { @MainActor public struct PasskeyAuthenticatorHelper: PasskeyAsyncHelperAPI { - public typealias ControllerProvider = () -> AuthorizationController - + public var controller: AuthorizationController { controllerProvider() } - + private let controllerProvider: ControllerProvider private let relyingPartyIdentifier: String - + public init( controllerProvider: @escaping ControllerProvider, relyingPartyIdentifier: String @@ -25,7 +24,7 @@ public struct PasskeyAuthenticatorHelper: PasskeyAsyncHelperAPI { self.controllerProvider = controllerProvider self.relyingPartyIdentifier = relyingPartyIdentifier } - + public func authenticate(message: Data) async throws -> Data { print("PasskeyAuthenticatorHelper.authenticate - message: \(message)") let assertion = try await performPasskeyAuthorizationRequest( @@ -33,13 +32,13 @@ public struct PasskeyAuthenticatorHelper: PasskeyAsyncHelperAPI { relyingPartyIdentifier: relyingPartyIdentifier, controller: controller ) - + print("Got assertion: \(assertion)") - + let authAssertionData = try JSONEncoder().encode(assertion) - + print(String(data: authAssertionData, encoding: .utf8) ?? "Couldn't decode assertion data") - + return authAssertionData } } diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Models/AuthorizationCredentialType.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Models/AuthorizationCredentialType.swift index 411c1a43..9b96ff66 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Models/AuthorizationCredentialType.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Models/AuthorizationCredentialType.swift @@ -1,5 +1,5 @@ -import Foundation import AuthenticationServices +import Foundation public enum AuthorizationCredentialType { case appleID(ASAuthorizationAppleIDCredential) diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Registration/PasskeyRegistration.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Registration/PasskeyRegistration.swift index 223007ea..2a465f8d 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Registration/PasskeyRegistration.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Passkey/Registration/PasskeyRegistration.swift @@ -1,5 +1,5 @@ -import Foundation import AuthenticationServices +import Foundation import SwiftUI public func createAccount( @@ -7,6 +7,8 @@ public func createAccount( userID: String, challenge: Data, relyingPartyIdentifier: String, + initialK1Owners: [String]?, + initialSessionConfigJson: String?, controller: AuthorizationController ) async throws -> Account { let authorization = try await performCredentialRegistrationRequest( @@ -40,7 +42,9 @@ public func createAccount( credentialRawClientDataJson: rawClientDataJSON, credentialId: credentialId, rpId: relyingPartyIdentifier, - uniqueAccountId: userID + uniqueAccountId: userID, + initialK1Owners: initialK1Owners, + initialSessionConfigJson: initialSessionConfigJson ) ) } diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/CreateSession.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/CreateSession.swift new file mode 100644 index 00000000..ee878020 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/CreateSession.swift @@ -0,0 +1,44 @@ +import Foundation +@preconcurrency import ZKsyncSSOFFI + +public struct CreateSessionArgs { + let inner: ZKsyncSSOFFI.CreateSessionArgs + + public init(account: String, sessionConfigJSON: String, ownerPrivateKey: String, paymaster: String?) { + inner = ZKsyncSSOFFI.CreateSessionArgs( + account: account, + sessionConfig: sessionConfigJSON, + ownerPrivateKey: ownerPrivateKey, + paymaster: paymaster + ) + } + + public init(account: String, sessionConfig: SessionSpec, ownerPrivateKey: String, paymaster: String?) { + let sessionConfigJSON = try! sessionConfig.toJsonString() + self.init(account: account, sessionConfigJSON: sessionConfigJSON, ownerPrivateKey: ownerPrivateKey, paymaster: paymaster) + } +} + +public func createSession(args: CreateSessionArgs, config: Config) async throws -> String { + // Pretty print the sessionConfig JSON + if let jsonData = args.inner.sessionConfig.data(using: .utf8) { + do { + let jsonObject = try JSONSerialization.jsonObject(with: jsonData) + let prettyJsonData = try JSONSerialization.data( + withJSONObject: jsonObject, options: [.prettyPrinted, .sortedKeys]) + if let prettyJsonString = String(data: prettyJsonData, encoding: .utf8) { + print("📋 Session Config JSON:") + print(prettyJsonString) + } + } catch { + print("⚠️ Failed to pretty print sessionConfig JSON: \(error)") + print("📋 Raw sessionConfig: \(args.inner.sessionConfig)") + } + } else { + print("📋 Raw sessionConfig: \(args.inner.sessionConfig)") + } + + let args = args.inner + let createdSession = try await ZKsyncSSOFFI.createSession(args: args, config: config.inner) + return createdSession.transactionReceiptJson +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/GetSessionState.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/GetSessionState.swift new file mode 100644 index 00000000..512e863a --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/GetSessionState.swift @@ -0,0 +1,22 @@ +import Foundation +@preconcurrency import ZKsyncSSOFFI + +public struct GetSessionStateArgs { + let inner: ZKsyncSSOFFI.GetSessionStateArgs + + public init(account: String, sessionConfig: String) { + inner = ZKsyncSSOFFI.GetSessionStateArgs( + account: account, + sessionConfig: sessionConfig + ) + } +} + +public func getSessionState(args: GetSessionStateArgs, config: Config) async throws -> String { + let args = args.inner + let result = try await ZKsyncSSOFFI.getSessionState( + args: args, + config: config.inner + ) + return result.sessionStateJson +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/RevokeSession.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/RevokeSession.swift new file mode 100644 index 00000000..36c4dab7 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/RevokeSession.swift @@ -0,0 +1,23 @@ +import Foundation +@preconcurrency import ZKsyncSSOFFI + +public struct RevokeSessionArgs { + let inner: ZKsyncSSOFFI.RevokeSessionArgs + + public init(account: String, sessionId: String, ownerPrivateKey: String) { + inner = ZKsyncSSOFFI.RevokeSessionArgs( + account: account, + sessionId: sessionId, + ownerPrivateKey: ownerPrivateKey + ) + } +} + +public func revokeSession(args: RevokeSessionArgs, config: Config) async throws -> String { + let args = args.inner + let createdSession = try await ZKsyncSSOFFI.revokeSession( + args: args, + config: config.inner + ) + return createdSession.transactionReceiptJson +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionClient.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionClient.swift new file mode 100644 index 00000000..0e1d9d4d --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionClient.swift @@ -0,0 +1,50 @@ +import Foundation +@preconcurrency import ZKsyncSSOFFI + +public struct SessionClient { + public var accountAddress: String + public var sessionKey: String + public var sessionConfig: String + public var config: Config + + public init( + accountAddress: String, + sessionKey: String, + sessionConfig: String, + config: Config + ) { + self.accountAddress = accountAddress + self.sessionKey = sessionKey + self.sessionConfig = sessionConfig + self.config = config + } + + @discardableResult + public func sendTransaction( + transaction: TransactionRequest + ) async throws -> String { + try await sendSessionTransaction( + accountAddress: accountAddress, + sessionKeyHex: sessionKey, + sessionConfigJson: sessionConfig, + config: config.inner, + transaction: Transaction( + from: accountAddress, + to: transaction.to, + value: transaction.value, + input: transaction.input + ) + ) + } + + @discardableResult + public func revokeSession() async throws -> String { + // try await ZKsyncSSOFFI.revokeSession( + // accountAddress: accountAddress, + // sessionKeyHex: sessionKey, + // sessionConfigJson: sessionConfig, + // config: config.inner + // ) + fatalError() + } +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/SessionSpec.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/SessionSpec.swift new file mode 100644 index 00000000..9aa7388c --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/SessionSpec.swift @@ -0,0 +1,36 @@ +import Foundation +import ZKsyncSSOFFI + +public struct SessionSpec: Codable, Equatable, Hashable, Sendable { + public var signer: String + public var expiresAt: String + public var feeLimit: UsageLimit + public var callPolicies: [CallSpec] + public var transferPolicies: [TransferSpec] + + public init( + signer: String, + expiresAt: String, + feeLimit: UsageLimit, + callPolicies: [CallSpec], + transferPolicies: [TransferSpec] + ) { + self.signer = signer + self.expiresAt = expiresAt + self.feeLimit = feeLimit + self.callPolicies = callPolicies + self.transferPolicies = transferPolicies + } + + public func toJsonString(pretty: Bool = false) throws -> String { + let encoder = JSONEncoder() + if pretty { encoder.outputFormatting = [.prettyPrinted, .sortedKeys] } + let data = try encoder.encode(self) + return String(decoding: data, as: UTF8.self) + } + + public func sessionHash() throws -> String { + let sessionConfigJson = try toJsonString() + return try getSessionHash(sessionConfigJson: sessionConfigJson) + } +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/CallSpec.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/CallSpec.swift new file mode 100644 index 00000000..d9354014 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/CallSpec.swift @@ -0,0 +1,23 @@ +import Foundation + +public struct CallSpec: Codable, Equatable, Hashable, Sendable { + public var target: String + public var selector: String + public var maxValuePerUse: String + public var valueLimit: UsageLimit + public var constraints: [Constraint] + + public init( + target: String, + selector: String, + maxValuePerUse: String, + valueLimit: UsageLimit, + constraints: [Constraint] + ) { + self.target = target + self.selector = selector + self.maxValuePerUse = maxValuePerUse + self.valueLimit = valueLimit + self.constraints = constraints + } +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/Condition.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/Condition.swift new file mode 100644 index 00000000..a4424e60 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/Condition.swift @@ -0,0 +1,11 @@ +import Foundation + +public enum Condition: String, Codable, Hashable, Sendable { + case unconstrained = "Unconstrained" + case equal = "Equal" + case greater = "Greater" + case less = "Less" + case greaterEqual = "GreaterEqual" + case lessEqual = "LessEqual" + case notEqual = "NotEqual" +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/Constraint.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/Constraint.swift new file mode 100644 index 00000000..fb5ae250 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/Constraint.swift @@ -0,0 +1,15 @@ +import Foundation + +public struct Constraint: Codable, Equatable, Hashable, Sendable { + public var condition: Condition + public var index: UInt64 + public var refValue: String + public var limit: UsageLimit + + public init(condition: Condition, index: UInt64, refValue: String, limit: UsageLimit) { + self.condition = condition + self.index = index + self.refValue = refValue + self.limit = limit + } +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/LimitType.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/LimitType.swift new file mode 100644 index 00000000..28ec8032 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/LimitType.swift @@ -0,0 +1,7 @@ +import Foundation + +public enum LimitType: String, Codable, Hashable, Sendable { + case unlimited = "Unlimited" + case lifetime = "Lifetime" + case allowance = "Allowance" +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/TransferSpec.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/TransferSpec.swift new file mode 100644 index 00000000..840b7828 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/TransferSpec.swift @@ -0,0 +1,13 @@ +import Foundation + +public struct TransferSpec: Codable, Equatable, Hashable, Sendable { + public var target: String + public var maxValuePerUse: String + public var valueLimit: UsageLimit + + public init(target: String, maxValuePerUse: String, valueLimit: UsageLimit) { + self.target = target + self.maxValuePerUse = maxValuePerUse + self.valueLimit = valueLimit + } +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/UsageLimit.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/UsageLimit.swift new file mode 100644 index 00000000..41daf125 --- /dev/null +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSO/Sessions/SessionLib/Types/UsageLimit.swift @@ -0,0 +1,16 @@ +import Foundation + +public struct UsageLimit: Codable, Equatable, Hashable, Sendable { + public var limitType: LimitType + public var limit: String + public var period: String + + public init(limitType: LimitType, limit: String, period: String) { + self.limitType = limitType + self.limit = limit + self.period = period + } + + public static let unlimited = UsageLimit(limitType: .unlimited, limit: "0x0", period: "0x0") + public static let zero = UsageLimit(limitType: .lifetime, limit: "0x0", period: "0x0") +} diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSOFFI/ZKsyncSSOCore.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSOFFI/ZKsyncSSOCore.swift index d762b713..221fd54c 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSOFFI/ZKsyncSSOCore.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Sources/ZKsyncSSOFFI/ZKsyncSSOCore.swift @@ -407,6 +407,30 @@ private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0 private let UNIFFI_CALLBACK_ERROR: Int32 = 1 private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2 +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterBool : FfiConverter { + typealias FfiType = Int8 + typealias SwiftType = Bool + + public static func lift(_ value: Int8) throws -> Bool { + return value != 0 + } + + public static func lower(_ value: Bool) -> Int8 { + return value ? 1 : 0 + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool { + return try lift(readInt(&buf)) + } + + public static func write(_ value: Bool, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -1334,49 +1358,37 @@ public func FfiConverterTypeDeployWallet_lower(_ value: DeployWallet) -> RustBuf } -public struct PasskeyParameters { - public var credentialRawAttestationObject: Data - public var credentialRawClientDataJson: Data - public var credentialId: Data - public var rpId: RpId +public struct GetSessionStateArgs { + public var account: String + public var sessionConfig: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(credentialRawAttestationObject: Data, credentialRawClientDataJson: Data, credentialId: Data, rpId: RpId) { - self.credentialRawAttestationObject = credentialRawAttestationObject - self.credentialRawClientDataJson = credentialRawClientDataJson - self.credentialId = credentialId - self.rpId = rpId + public init(account: String, sessionConfig: String) { + self.account = account + self.sessionConfig = sessionConfig } } #if compiler(>=6) -extension PasskeyParameters: Sendable {} +extension GetSessionStateArgs: Sendable {} #endif -extension PasskeyParameters: Equatable, Hashable { - public static func ==(lhs: PasskeyParameters, rhs: PasskeyParameters) -> Bool { - if lhs.credentialRawAttestationObject != rhs.credentialRawAttestationObject { - return false - } - if lhs.credentialRawClientDataJson != rhs.credentialRawClientDataJson { - return false - } - if lhs.credentialId != rhs.credentialId { +extension GetSessionStateArgs: Equatable, Hashable { + public static func ==(lhs: GetSessionStateArgs, rhs: GetSessionStateArgs) -> Bool { + if lhs.account != rhs.account { return false } - if lhs.rpId != rhs.rpId { + if lhs.sessionConfig != rhs.sessionConfig { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(credentialRawAttestationObject) - hasher.combine(credentialRawClientDataJson) - hasher.combine(credentialId) - hasher.combine(rpId) + hasher.combine(account) + hasher.combine(sessionConfig) } } @@ -1385,22 +1397,18 @@ extension PasskeyParameters: Equatable, Hashable { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePasskeyParameters: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PasskeyParameters { +public struct FfiConverterTypeGetSessionStateArgs: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetSessionStateArgs { return - try PasskeyParameters( - credentialRawAttestationObject: FfiConverterData.read(from: &buf), - credentialRawClientDataJson: FfiConverterData.read(from: &buf), - credentialId: FfiConverterData.read(from: &buf), - rpId: FfiConverterTypeRpId.read(from: &buf) + try GetSessionStateArgs( + account: FfiConverterString.read(from: &buf), + sessionConfig: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: PasskeyParameters, into buf: inout [UInt8]) { - FfiConverterData.write(value.credentialRawAttestationObject, into: &buf) - FfiConverterData.write(value.credentialRawClientDataJson, into: &buf) - FfiConverterData.write(value.credentialId, into: &buf) - FfiConverterTypeRpId.write(value.rpId, into: &buf) + public static func write(_ value: GetSessionStateArgs, into buf: inout [UInt8]) { + FfiConverterString.write(value.account, into: &buf) + FfiConverterString.write(value.sessionConfig, into: &buf) } } @@ -1408,67 +1416,43 @@ public struct FfiConverterTypePasskeyParameters: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePasskeyParameters_lift(_ buf: RustBuffer) throws -> PasskeyParameters { - return try FfiConverterTypePasskeyParameters.lift(buf) +public func FfiConverterTypeGetSessionStateArgs_lift(_ buf: RustBuffer) throws -> GetSessionStateArgs { + return try FfiConverterTypeGetSessionStateArgs.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePasskeyParameters_lower(_ value: PasskeyParameters) -> RustBuffer { - return FfiConverterTypePasskeyParameters.lower(value) +public func FfiConverterTypeGetSessionStateArgs_lower(_ value: GetSessionStateArgs) -> RustBuffer { + return FfiConverterTypeGetSessionStateArgs.lower(value) } -public struct PreparedTransaction { - public var transactionRequestJson: String - public var from: String - public var to: String - public var value: String - public var displayFee: String +public struct GetSessionStateReturnType { + public var sessionStateJson: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(transactionRequestJson: String, from: String, to: String, value: String, displayFee: String) { - self.transactionRequestJson = transactionRequestJson - self.from = from - self.to = to - self.value = value - self.displayFee = displayFee + public init(sessionStateJson: String) { + self.sessionStateJson = sessionStateJson } } #if compiler(>=6) -extension PreparedTransaction: Sendable {} +extension GetSessionStateReturnType: Sendable {} #endif -extension PreparedTransaction: Equatable, Hashable { - public static func ==(lhs: PreparedTransaction, rhs: PreparedTransaction) -> Bool { - if lhs.transactionRequestJson != rhs.transactionRequestJson { - return false - } - if lhs.from != rhs.from { - return false - } - if lhs.to != rhs.to { - return false - } - if lhs.value != rhs.value { - return false - } - if lhs.displayFee != rhs.displayFee { +extension GetSessionStateReturnType: Equatable, Hashable { + public static func ==(lhs: GetSessionStateReturnType, rhs: GetSessionStateReturnType) -> Bool { + if lhs.sessionStateJson != rhs.sessionStateJson { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(transactionRequestJson) - hasher.combine(from) - hasher.combine(to) - hasher.combine(value) - hasher.combine(displayFee) + hasher.combine(sessionStateJson) } } @@ -1477,24 +1461,16 @@ extension PreparedTransaction: Equatable, Hashable { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePreparedTransaction: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PreparedTransaction { +public struct FfiConverterTypeGetSessionStateReturnType: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetSessionStateReturnType { return - try PreparedTransaction( - transactionRequestJson: FfiConverterString.read(from: &buf), - from: FfiConverterString.read(from: &buf), - to: FfiConverterString.read(from: &buf), - value: FfiConverterString.read(from: &buf), - displayFee: FfiConverterString.read(from: &buf) + try GetSessionStateReturnType( + sessionStateJson: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: PreparedTransaction, into buf: inout [UInt8]) { - FfiConverterString.write(value.transactionRequestJson, into: &buf) - FfiConverterString.write(value.from, into: &buf) - FfiConverterString.write(value.to, into: &buf) - FfiConverterString.write(value.value, into: &buf) - FfiConverterString.write(value.displayFee, into: &buf) + public static func write(_ value: GetSessionStateReturnType, into buf: inout [UInt8]) { + FfiConverterString.write(value.sessionStateJson, into: &buf) } } @@ -1502,67 +1478,49 @@ public struct FfiConverterTypePreparedTransaction: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePreparedTransaction_lift(_ buf: RustBuffer) throws -> PreparedTransaction { - return try FfiConverterTypePreparedTransaction.lift(buf) +public func FfiConverterTypeGetSessionStateReturnType_lift(_ buf: RustBuffer) throws -> GetSessionStateReturnType { + return try FfiConverterTypeGetSessionStateReturnType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePreparedTransaction_lower(_ value: PreparedTransaction) -> RustBuffer { - return FfiConverterTypePreparedTransaction.lower(value) +public func FfiConverterTypeGetSessionStateReturnType_lower(_ value: GetSessionStateReturnType) -> RustBuffer { + return FfiConverterTypeGetSessionStateReturnType.lower(value) } -public struct SsoContracts { - public var accountFactory: String - public var passkey: String - public var session: String - public var accountPaymaster: String - public var recovery: String +public struct IsK1OwnerArgs { + public var account: String + public var ownerAddress: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(accountFactory: String, passkey: String, session: String, accountPaymaster: String, recovery: String) { - self.accountFactory = accountFactory - self.passkey = passkey - self.session = session - self.accountPaymaster = accountPaymaster - self.recovery = recovery + public init(account: String, ownerAddress: String) { + self.account = account + self.ownerAddress = ownerAddress } } #if compiler(>=6) -extension SsoContracts: Sendable {} +extension IsK1OwnerArgs: Sendable {} #endif -extension SsoContracts: Equatable, Hashable { - public static func ==(lhs: SsoContracts, rhs: SsoContracts) -> Bool { - if lhs.accountFactory != rhs.accountFactory { - return false - } - if lhs.passkey != rhs.passkey { - return false - } - if lhs.session != rhs.session { - return false - } - if lhs.accountPaymaster != rhs.accountPaymaster { +extension IsK1OwnerArgs: Equatable, Hashable { + public static func ==(lhs: IsK1OwnerArgs, rhs: IsK1OwnerArgs) -> Bool { + if lhs.account != rhs.account { return false } - if lhs.recovery != rhs.recovery { + if lhs.ownerAddress != rhs.ownerAddress { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(accountFactory) - hasher.combine(passkey) - hasher.combine(session) - hasher.combine(accountPaymaster) - hasher.combine(recovery) + hasher.combine(account) + hasher.combine(ownerAddress) } } @@ -1571,24 +1529,18 @@ extension SsoContracts: Equatable, Hashable { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSSOContracts: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SsoContracts { +public struct FfiConverterTypeIsK1OwnerArgs: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IsK1OwnerArgs { return - try SsoContracts( - accountFactory: FfiConverterString.read(from: &buf), - passkey: FfiConverterString.read(from: &buf), - session: FfiConverterString.read(from: &buf), - accountPaymaster: FfiConverterString.read(from: &buf), - recovery: FfiConverterString.read(from: &buf) + try IsK1OwnerArgs( + account: FfiConverterString.read(from: &buf), + ownerAddress: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: SsoContracts, into buf: inout [UInt8]) { - FfiConverterString.write(value.accountFactory, into: &buf) - FfiConverterString.write(value.passkey, into: &buf) - FfiConverterString.write(value.session, into: &buf) - FfiConverterString.write(value.accountPaymaster, into: &buf) - FfiConverterString.write(value.recovery, into: &buf) + public static func write(_ value: IsK1OwnerArgs, into buf: inout [UInt8]) { + FfiConverterString.write(value.account, into: &buf) + FfiConverterString.write(value.ownerAddress, into: &buf) } } @@ -1596,49 +1548,49 @@ public struct FfiConverterTypeSSOContracts: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSSOContracts_lift(_ buf: RustBuffer) throws -> SsoContracts { - return try FfiConverterTypeSSOContracts.lift(buf) +public func FfiConverterTypeIsK1OwnerArgs_lift(_ buf: RustBuffer) throws -> IsK1OwnerArgs { + return try FfiConverterTypeIsK1OwnerArgs.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSSOContracts_lower(_ value: SsoContracts) -> RustBuffer { - return FfiConverterTypeSSOContracts.lower(value) +public func FfiConverterTypeIsK1OwnerArgs_lower(_ value: IsK1OwnerArgs) -> RustBuffer { + return FfiConverterTypeIsK1OwnerArgs.lower(value) } -public struct SendTransactionResult { - public var txHash: String - public var receiptJson: String +public struct IsModuleValidatorArgs { + public var account: String + public var moduleAddress: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(txHash: String, receiptJson: String) { - self.txHash = txHash - self.receiptJson = receiptJson + public init(account: String, moduleAddress: String) { + self.account = account + self.moduleAddress = moduleAddress } } #if compiler(>=6) -extension SendTransactionResult: Sendable {} +extension IsModuleValidatorArgs: Sendable {} #endif -extension SendTransactionResult: Equatable, Hashable { - public static func ==(lhs: SendTransactionResult, rhs: SendTransactionResult) -> Bool { - if lhs.txHash != rhs.txHash { +extension IsModuleValidatorArgs: Equatable, Hashable { + public static func ==(lhs: IsModuleValidatorArgs, rhs: IsModuleValidatorArgs) -> Bool { + if lhs.account != rhs.account { return false } - if lhs.receiptJson != rhs.receiptJson { + if lhs.moduleAddress != rhs.moduleAddress { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txHash) - hasher.combine(receiptJson) + hasher.combine(account) + hasher.combine(moduleAddress) } } @@ -1647,18 +1599,18 @@ extension SendTransactionResult: Equatable, Hashable { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSendTransactionResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SendTransactionResult { +public struct FfiConverterTypeIsModuleValidatorArgs: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IsModuleValidatorArgs { return - try SendTransactionResult( - txHash: FfiConverterString.read(from: &buf), - receiptJson: FfiConverterString.read(from: &buf) + try IsModuleValidatorArgs( + account: FfiConverterString.read(from: &buf), + moduleAddress: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: SendTransactionResult, into buf: inout [UInt8]) { - FfiConverterString.write(value.txHash, into: &buf) - FfiConverterString.write(value.receiptJson, into: &buf) + public static func write(_ value: IsModuleValidatorArgs, into buf: inout [UInt8]) { + FfiConverterString.write(value.account, into: &buf) + FfiConverterString.write(value.moduleAddress, into: &buf) } } @@ -1666,61 +1618,61 @@ public struct FfiConverterTypeSendTransactionResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSendTransactionResult_lift(_ buf: RustBuffer) throws -> SendTransactionResult { - return try FfiConverterTypeSendTransactionResult.lift(buf) +public func FfiConverterTypeIsModuleValidatorArgs_lift(_ buf: RustBuffer) throws -> IsModuleValidatorArgs { + return try FfiConverterTypeIsModuleValidatorArgs.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSendTransactionResult_lower(_ value: SendTransactionResult) -> RustBuffer { - return FfiConverterTypeSendTransactionResult.lower(value) +public func FfiConverterTypeIsModuleValidatorArgs_lower(_ value: IsModuleValidatorArgs) -> RustBuffer { + return FfiConverterTypeIsModuleValidatorArgs.lower(value) } -public struct Transaction { - public var from: String - public var to: String? - public var value: String? - public var input: String? +public struct PasskeyParameters { + public var credentialRawAttestationObject: Data + public var credentialRawClientDataJson: Data + public var credentialId: Data + public var rpId: RpId // Default memberwise initializers are never public by default, so we // declare one manually. - public init(from: String, to: String?, value: String?, input: String?) { - self.from = from - self.to = to - self.value = value - self.input = input + public init(credentialRawAttestationObject: Data, credentialRawClientDataJson: Data, credentialId: Data, rpId: RpId) { + self.credentialRawAttestationObject = credentialRawAttestationObject + self.credentialRawClientDataJson = credentialRawClientDataJson + self.credentialId = credentialId + self.rpId = rpId } } #if compiler(>=6) -extension Transaction: Sendable {} +extension PasskeyParameters: Sendable {} #endif -extension Transaction: Equatable, Hashable { - public static func ==(lhs: Transaction, rhs: Transaction) -> Bool { - if lhs.from != rhs.from { +extension PasskeyParameters: Equatable, Hashable { + public static func ==(lhs: PasskeyParameters, rhs: PasskeyParameters) -> Bool { + if lhs.credentialRawAttestationObject != rhs.credentialRawAttestationObject { return false } - if lhs.to != rhs.to { + if lhs.credentialRawClientDataJson != rhs.credentialRawClientDataJson { return false } - if lhs.value != rhs.value { + if lhs.credentialId != rhs.credentialId { return false } - if lhs.input != rhs.input { + if lhs.rpId != rhs.rpId { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(from) - hasher.combine(to) - hasher.combine(value) - hasher.combine(input) + hasher.combine(credentialRawAttestationObject) + hasher.combine(credentialRawClientDataJson) + hasher.combine(credentialId) + hasher.combine(rpId) } } @@ -1729,22 +1681,825 @@ extension Transaction: Equatable, Hashable { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTransaction: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Transaction { +public struct FfiConverterTypePasskeyParameters: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PasskeyParameters { return - try Transaction( - from: FfiConverterString.read(from: &buf), - to: FfiConverterOptionString.read(from: &buf), - value: FfiConverterOptionString.read(from: &buf), - input: FfiConverterOptionString.read(from: &buf) - ) - } - - public static func write(_ value: Transaction, into buf: inout [UInt8]) { + try PasskeyParameters( + credentialRawAttestationObject: FfiConverterData.read(from: &buf), + credentialRawClientDataJson: FfiConverterData.read(from: &buf), + credentialId: FfiConverterData.read(from: &buf), + rpId: FfiConverterTypeRpId.read(from: &buf) + ) + } + + public static func write(_ value: PasskeyParameters, into buf: inout [UInt8]) { + FfiConverterData.write(value.credentialRawAttestationObject, into: &buf) + FfiConverterData.write(value.credentialRawClientDataJson, into: &buf) + FfiConverterData.write(value.credentialId, into: &buf) + FfiConverterTypeRpId.write(value.rpId, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePasskeyParameters_lift(_ buf: RustBuffer) throws -> PasskeyParameters { + return try FfiConverterTypePasskeyParameters.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePasskeyParameters_lower(_ value: PasskeyParameters) -> RustBuffer { + return FfiConverterTypePasskeyParameters.lower(value) +} + + +public struct PreparedTransaction { + public var transactionRequestJson: String + public var from: String + public var to: String + public var value: String + public var displayFee: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(transactionRequestJson: String, from: String, to: String, value: String, displayFee: String) { + self.transactionRequestJson = transactionRequestJson + self.from = from + self.to = to + self.value = value + self.displayFee = displayFee + } +} + +#if compiler(>=6) +extension PreparedTransaction: Sendable {} +#endif + + +extension PreparedTransaction: Equatable, Hashable { + public static func ==(lhs: PreparedTransaction, rhs: PreparedTransaction) -> Bool { + if lhs.transactionRequestJson != rhs.transactionRequestJson { + return false + } + if lhs.from != rhs.from { + return false + } + if lhs.to != rhs.to { + return false + } + if lhs.value != rhs.value { + return false + } + if lhs.displayFee != rhs.displayFee { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(transactionRequestJson) + hasher.combine(from) + hasher.combine(to) + hasher.combine(value) + hasher.combine(displayFee) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypePreparedTransaction: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PreparedTransaction { + return + try PreparedTransaction( + transactionRequestJson: FfiConverterString.read(from: &buf), + from: FfiConverterString.read(from: &buf), + to: FfiConverterString.read(from: &buf), + value: FfiConverterString.read(from: &buf), + displayFee: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: PreparedTransaction, into buf: inout [UInt8]) { + FfiConverterString.write(value.transactionRequestJson, into: &buf) FfiConverterString.write(value.from, into: &buf) - FfiConverterOptionString.write(value.to, into: &buf) - FfiConverterOptionString.write(value.value, into: &buf) - FfiConverterOptionString.write(value.input, into: &buf) + FfiConverterString.write(value.to, into: &buf) + FfiConverterString.write(value.value, into: &buf) + FfiConverterString.write(value.displayFee, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePreparedTransaction_lift(_ buf: RustBuffer) throws -> PreparedTransaction { + return try FfiConverterTypePreparedTransaction.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypePreparedTransaction_lower(_ value: PreparedTransaction) -> RustBuffer { + return FfiConverterTypePreparedTransaction.lower(value) +} + + +public struct RevokeSessionArgs { + public var account: String + public var sessionId: String + public var ownerPrivateKey: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(account: String, sessionId: String, ownerPrivateKey: String) { + self.account = account + self.sessionId = sessionId + self.ownerPrivateKey = ownerPrivateKey + } +} + +#if compiler(>=6) +extension RevokeSessionArgs: Sendable {} +#endif + + +extension RevokeSessionArgs: Equatable, Hashable { + public static func ==(lhs: RevokeSessionArgs, rhs: RevokeSessionArgs) -> Bool { + if lhs.account != rhs.account { + return false + } + if lhs.sessionId != rhs.sessionId { + return false + } + if lhs.ownerPrivateKey != rhs.ownerPrivateKey { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(account) + hasher.combine(sessionId) + hasher.combine(ownerPrivateKey) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeRevokeSessionArgs: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> RevokeSessionArgs { + return + try RevokeSessionArgs( + account: FfiConverterString.read(from: &buf), + sessionId: FfiConverterString.read(from: &buf), + ownerPrivateKey: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: RevokeSessionArgs, into buf: inout [UInt8]) { + FfiConverterString.write(value.account, into: &buf) + FfiConverterString.write(value.sessionId, into: &buf) + FfiConverterString.write(value.ownerPrivateKey, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeRevokeSessionArgs_lift(_ buf: RustBuffer) throws -> RevokeSessionArgs { + return try FfiConverterTypeRevokeSessionArgs.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeRevokeSessionArgs_lower(_ value: RevokeSessionArgs) -> RustBuffer { + return FfiConverterTypeRevokeSessionArgs.lower(value) +} + + +public struct RevokeSessionReturnType { + public var transactionReceiptJson: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(transactionReceiptJson: String) { + self.transactionReceiptJson = transactionReceiptJson + } +} + +#if compiler(>=6) +extension RevokeSessionReturnType: Sendable {} +#endif + + +extension RevokeSessionReturnType: Equatable, Hashable { + public static func ==(lhs: RevokeSessionReturnType, rhs: RevokeSessionReturnType) -> Bool { + if lhs.transactionReceiptJson != rhs.transactionReceiptJson { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(transactionReceiptJson) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeRevokeSessionReturnType: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> RevokeSessionReturnType { + return + try RevokeSessionReturnType( + transactionReceiptJson: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: RevokeSessionReturnType, into buf: inout [UInt8]) { + FfiConverterString.write(value.transactionReceiptJson, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeRevokeSessionReturnType_lift(_ buf: RustBuffer) throws -> RevokeSessionReturnType { + return try FfiConverterTypeRevokeSessionReturnType.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeRevokeSessionReturnType_lower(_ value: RevokeSessionReturnType) -> RustBuffer { + return FfiConverterTypeRevokeSessionReturnType.lower(value) +} + + +public struct SsoContracts { + public var accountFactory: String + public var passkey: String + public var session: String + public var accountPaymaster: String + public var recovery: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(accountFactory: String, passkey: String, session: String, accountPaymaster: String, recovery: String) { + self.accountFactory = accountFactory + self.passkey = passkey + self.session = session + self.accountPaymaster = accountPaymaster + self.recovery = recovery + } +} + +#if compiler(>=6) +extension SsoContracts: Sendable {} +#endif + + +extension SsoContracts: Equatable, Hashable { + public static func ==(lhs: SsoContracts, rhs: SsoContracts) -> Bool { + if lhs.accountFactory != rhs.accountFactory { + return false + } + if lhs.passkey != rhs.passkey { + return false + } + if lhs.session != rhs.session { + return false + } + if lhs.accountPaymaster != rhs.accountPaymaster { + return false + } + if lhs.recovery != rhs.recovery { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(accountFactory) + hasher.combine(passkey) + hasher.combine(session) + hasher.combine(accountPaymaster) + hasher.combine(recovery) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSSOContracts: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SsoContracts { + return + try SsoContracts( + accountFactory: FfiConverterString.read(from: &buf), + passkey: FfiConverterString.read(from: &buf), + session: FfiConverterString.read(from: &buf), + accountPaymaster: FfiConverterString.read(from: &buf), + recovery: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: SsoContracts, into buf: inout [UInt8]) { + FfiConverterString.write(value.accountFactory, into: &buf) + FfiConverterString.write(value.passkey, into: &buf) + FfiConverterString.write(value.session, into: &buf) + FfiConverterString.write(value.accountPaymaster, into: &buf) + FfiConverterString.write(value.recovery, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSSOContracts_lift(_ buf: RustBuffer) throws -> SsoContracts { + return try FfiConverterTypeSSOContracts.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSSOContracts_lower(_ value: SsoContracts) -> RustBuffer { + return FfiConverterTypeSSOContracts.lower(value) +} + + +public struct SendTransactionResult { + public var txHash: String + public var receiptJson: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(txHash: String, receiptJson: String) { + self.txHash = txHash + self.receiptJson = receiptJson + } +} + +#if compiler(>=6) +extension SendTransactionResult: Sendable {} +#endif + + +extension SendTransactionResult: Equatable, Hashable { + public static func ==(lhs: SendTransactionResult, rhs: SendTransactionResult) -> Bool { + if lhs.txHash != rhs.txHash { + return false + } + if lhs.receiptJson != rhs.receiptJson { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(txHash) + hasher.combine(receiptJson) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeSendTransactionResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SendTransactionResult { + return + try SendTransactionResult( + txHash: FfiConverterString.read(from: &buf), + receiptJson: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: SendTransactionResult, into buf: inout [UInt8]) { + FfiConverterString.write(value.txHash, into: &buf) + FfiConverterString.write(value.receiptJson, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSendTransactionResult_lift(_ buf: RustBuffer) throws -> SendTransactionResult { + return try FfiConverterTypeSendTransactionResult.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSendTransactionResult_lower(_ value: SendTransactionResult) -> RustBuffer { + return FfiConverterTypeSendTransactionResult.lower(value) +} + + +public struct Transaction { + public var from: String + public var to: String? + public var value: String? + public var input: String? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(from: String, to: String?, value: String?, input: String?) { + self.from = from + self.to = to + self.value = value + self.input = input + } +} + +#if compiler(>=6) +extension Transaction: Sendable {} +#endif + + +extension Transaction: Equatable, Hashable { + public static func ==(lhs: Transaction, rhs: Transaction) -> Bool { + if lhs.from != rhs.from { + return false + } + if lhs.to != rhs.to { + return false + } + if lhs.value != rhs.value { + return false + } + if lhs.input != rhs.input { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(from) + hasher.combine(to) + hasher.combine(value) + hasher.combine(input) + } +} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeTransaction: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Transaction { + return + try Transaction( + from: FfiConverterString.read(from: &buf), + to: FfiConverterOptionString.read(from: &buf), + value: FfiConverterOptionString.read(from: &buf), + input: FfiConverterOptionString.read(from: &buf) + ) + } + + public static func write(_ value: Transaction, into buf: inout [UInt8]) { + FfiConverterString.write(value.from, into: &buf) + FfiConverterOptionString.write(value.to, into: &buf) + FfiConverterOptionString.write(value.value, into: &buf) + FfiConverterOptionString.write(value.input, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTransaction_lift(_ buf: RustBuffer) throws -> Transaction { + return try FfiConverterTypeTransaction.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTransaction_lower(_ value: Transaction) -> RustBuffer { + return FfiConverterTypeTransaction.lower(value) +} + + +public enum ConfigError: Swift.Error { + + + + case InvalidContractAddress(String + ) + case InvalidDeployWallet(String + ) + case InvalidNodeUrl(String + ) + case WriteError(String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeConfigError: FfiConverterRustBuffer { + typealias SwiftType = ConfigError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ConfigError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .InvalidContractAddress( + try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidDeployWallet( + try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidNodeUrl( + try FfiConverterString.read(from: &buf) + ) + case 4: return .WriteError( + try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ConfigError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .InvalidContractAddress(v1): + writeInt(&buf, Int32(1)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidDeployWallet(v1): + writeInt(&buf, Int32(2)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidNodeUrl(v1): + writeInt(&buf, Int32(3)) + FfiConverterString.write(v1, into: &buf) + + + case let .WriteError(v1): + writeInt(&buf, Int32(4)) + FfiConverterString.write(v1, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeConfigError_lift(_ buf: RustBuffer) throws -> ConfigError { + return try FfiConverterTypeConfigError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeConfigError_lower(_ value: ConfigError) -> RustBuffer { + return FfiConverterTypeConfigError.lower(value) +} + + +extension ConfigError: Equatable, Hashable {} + + + + +extension ConfigError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +public enum CreateSessionError: Swift.Error { + + + + case CreateSession(String + ) + case InvalidAddress(String + ) + case InvalidSessionConfig(String + ) + case InvalidConfig(String + ) + case InvalidPrivateKey(String + ) + case InvalidPaymaster(String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCreateSessionError: FfiConverterRustBuffer { + typealias SwiftType = CreateSessionError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CreateSessionError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .CreateSession( + try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidAddress( + try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidSessionConfig( + try FfiConverterString.read(from: &buf) + ) + case 4: return .InvalidConfig( + try FfiConverterString.read(from: &buf) + ) + case 5: return .InvalidPrivateKey( + try FfiConverterString.read(from: &buf) + ) + case 6: return .InvalidPaymaster( + try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: CreateSessionError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .CreateSession(v1): + writeInt(&buf, Int32(1)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidAddress(v1): + writeInt(&buf, Int32(2)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidSessionConfig(v1): + writeInt(&buf, Int32(3)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidConfig(v1): + writeInt(&buf, Int32(4)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidPrivateKey(v1): + writeInt(&buf, Int32(5)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidPaymaster(v1): + writeInt(&buf, Int32(6)) + FfiConverterString.write(v1, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCreateSessionError_lift(_ buf: RustBuffer) throws -> CreateSessionError { + return try FfiConverterTypeCreateSessionError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCreateSessionError_lower(_ value: CreateSessionError) -> RustBuffer { + return FfiConverterTypeCreateSessionError.lower(value) +} + + +extension CreateSessionError: Equatable, Hashable {} + + + + +extension CreateSessionError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +public enum DeployAccountError: Swift.Error { + + + + case Deploy(String + ) + case AccountAlreadyDeployed + case InvalidSessionConfig(String + ) + case InvalidK1Owners(String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeDeployAccountError: FfiConverterRustBuffer { + typealias SwiftType = DeployAccountError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DeployAccountError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .Deploy( + try FfiConverterString.read(from: &buf) + ) + case 2: return .AccountAlreadyDeployed + case 3: return .InvalidSessionConfig( + try FfiConverterString.read(from: &buf) + ) + case 4: return .InvalidK1Owners( + try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: DeployAccountError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .Deploy(v1): + writeInt(&buf, Int32(1)) + FfiConverterString.write(v1, into: &buf) + + + case .AccountAlreadyDeployed: + writeInt(&buf, Int32(2)) + + + case let .InvalidSessionConfig(v1): + writeInt(&buf, Int32(3)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidK1Owners(v1): + writeInt(&buf, Int32(4)) + FfiConverterString.write(v1, into: &buf) + + } } } @@ -1752,29 +2507,38 @@ public struct FfiConverterTypeTransaction: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransaction_lift(_ buf: RustBuffer) throws -> Transaction { - return try FfiConverterTypeTransaction.lift(buf) +public func FfiConverterTypeDeployAccountError_lift(_ buf: RustBuffer) throws -> DeployAccountError { + return try FfiConverterTypeDeployAccountError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransaction_lower(_ value: Transaction) -> RustBuffer { - return FfiConverterTypeTransaction.lower(value) +public func FfiConverterTypeDeployAccountError_lower(_ value: DeployAccountError) -> RustBuffer { + return FfiConverterTypeDeployAccountError.lower(value) } -public enum ConfigError: Swift.Error { +extension DeployAccountError: Equatable, Hashable {} + + + + +extension DeployAccountError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +public enum FetchAccountError: Swift.Error { - case InvalidContractAddress(String - ) - case InvalidDeployWallet(String - ) - case InvalidNodeUrl(String - ) - case WriteError(String + case FetchAccount(String ) } @@ -1782,26 +2546,17 @@ public enum ConfigError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeConfigError: FfiConverterRustBuffer { - typealias SwiftType = ConfigError +public struct FfiConverterTypeFetchAccountError: FfiConverterRustBuffer { + typealias SwiftType = FetchAccountError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ConfigError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FetchAccountError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidContractAddress( - try FfiConverterString.read(from: &buf) - ) - case 2: return .InvalidDeployWallet( - try FfiConverterString.read(from: &buf) - ) - case 3: return .InvalidNodeUrl( - try FfiConverterString.read(from: &buf) - ) - case 4: return .WriteError( + case 1: return .FetchAccount( try FfiConverterString.read(from: &buf) ) @@ -1809,32 +2564,17 @@ public struct FfiConverterTypeConfigError: FfiConverterRustBuffer { } } - public static func write(_ value: ConfigError, into buf: inout [UInt8]) { + public static func write(_ value: FetchAccountError, into buf: inout [UInt8]) { switch value { - case let .InvalidContractAddress(v1): + case let .FetchAccount(v1): writeInt(&buf, Int32(1)) FfiConverterString.write(v1, into: &buf) - - case let .InvalidDeployWallet(v1): - writeInt(&buf, Int32(2)) - FfiConverterString.write(v1, into: &buf) - - - case let .InvalidNodeUrl(v1): - writeInt(&buf, Int32(3)) - FfiConverterString.write(v1, into: &buf) - - - case let .WriteError(v1): - writeInt(&buf, Int32(4)) - FfiConverterString.write(v1, into: &buf) - } } } @@ -1843,24 +2583,24 @@ public struct FfiConverterTypeConfigError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeConfigError_lift(_ buf: RustBuffer) throws -> ConfigError { - return try FfiConverterTypeConfigError.lift(buf) +public func FfiConverterTypeFetchAccountError_lift(_ buf: RustBuffer) throws -> FetchAccountError { + return try FfiConverterTypeFetchAccountError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeConfigError_lower(_ value: ConfigError) -> RustBuffer { - return FfiConverterTypeConfigError.lower(value) +public func FfiConverterTypeFetchAccountError_lower(_ value: FetchAccountError) -> RustBuffer { + return FfiConverterTypeFetchAccountError.lower(value) } -extension ConfigError: Equatable, Hashable {} +extension FetchAccountError: Equatable, Hashable {} -extension ConfigError: Foundation.LocalizedError { +extension FetchAccountError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -1870,19 +2610,11 @@ extension ConfigError: Foundation.LocalizedError { -public enum CreateSessionError: Swift.Error { +public enum FundAccountError: Swift.Error { - case CreateSession(String - ) - case InvalidAddress(String - ) - case InvalidSessionConfig(String - ) - case InvalidSessionKey(String - ) - case InvalidPaymaster(String + case FundAccount(String ) } @@ -1890,29 +2622,17 @@ public enum CreateSessionError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCreateSessionError: FfiConverterRustBuffer { - typealias SwiftType = CreateSessionError +public struct FfiConverterTypeFundAccountError: FfiConverterRustBuffer { + typealias SwiftType = FundAccountError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CreateSessionError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FundAccountError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .CreateSession( - try FfiConverterString.read(from: &buf) - ) - case 2: return .InvalidAddress( - try FfiConverterString.read(from: &buf) - ) - case 3: return .InvalidSessionConfig( - try FfiConverterString.read(from: &buf) - ) - case 4: return .InvalidSessionKey( - try FfiConverterString.read(from: &buf) - ) - case 5: return .InvalidPaymaster( + case 1: return .FundAccount( try FfiConverterString.read(from: &buf) ) @@ -1920,35 +2640,91 @@ public struct FfiConverterTypeCreateSessionError: FfiConverterRustBuffer { } } - public static func write(_ value: CreateSessionError, into buf: inout [UInt8]) { + public static func write(_ value: FundAccountError, into buf: inout [UInt8]) { switch value { - case let .CreateSession(v1): + case let .FundAccount(v1): writeInt(&buf, Int32(1)) FfiConverterString.write(v1, into: &buf) + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeFundAccountError_lift(_ buf: RustBuffer) throws -> FundAccountError { + return try FfiConverterTypeFundAccountError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeFundAccountError_lower(_ value: FundAccountError) -> RustBuffer { + return FfiConverterTypeFundAccountError.lower(value) +} + + +extension FundAccountError: Equatable, Hashable {} + + + + +extension FundAccountError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +public enum GetAccountBalanceError: Swift.Error { + + + + case GetBalance(String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeGetAccountBalanceError: FfiConverterRustBuffer { + typealias SwiftType = GetAccountBalanceError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetAccountBalanceError { + let variant: Int32 = try readInt(&buf) + switch variant { + - case let .InvalidAddress(v1): - writeInt(&buf, Int32(2)) - FfiConverterString.write(v1, into: &buf) - + - case let .InvalidSessionConfig(v1): - writeInt(&buf, Int32(3)) - FfiConverterString.write(v1, into: &buf) - + case 1: return .GetBalance( + try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: GetAccountBalanceError, into buf: inout [UInt8]) { + switch value { + - case let .InvalidSessionKey(v1): - writeInt(&buf, Int32(4)) - FfiConverterString.write(v1, into: &buf) - + - case let .InvalidPaymaster(v1): - writeInt(&buf, Int32(5)) + + case let .GetBalance(v1): + writeInt(&buf, Int32(1)) FfiConverterString.write(v1, into: &buf) } @@ -1959,24 +2735,24 @@ public struct FfiConverterTypeCreateSessionError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCreateSessionError_lift(_ buf: RustBuffer) throws -> CreateSessionError { - return try FfiConverterTypeCreateSessionError.lift(buf) +public func FfiConverterTypeGetAccountBalanceError_lift(_ buf: RustBuffer) throws -> GetAccountBalanceError { + return try FfiConverterTypeGetAccountBalanceError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCreateSessionError_lower(_ value: CreateSessionError) -> RustBuffer { - return FfiConverterTypeCreateSessionError.lower(value) +public func FfiConverterTypeGetAccountBalanceError_lower(_ value: GetAccountBalanceError) -> RustBuffer { + return FfiConverterTypeGetAccountBalanceError.lower(value) } -extension CreateSessionError: Equatable, Hashable {} +extension GetAccountBalanceError: Equatable, Hashable {} -extension CreateSessionError: Foundation.LocalizedError { +extension GetAccountBalanceError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -1986,53 +2762,57 @@ extension CreateSessionError: Foundation.LocalizedError { -public enum DeployAccountError: Swift.Error { +public enum GetSessionHashError: Swift.Error { - case Deploy(String + case GetSessionHash(String + ) + case InvalidSessionConfig(String ) - case AccountAlreadyDeployed } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeDeployAccountError: FfiConverterRustBuffer { - typealias SwiftType = DeployAccountError +public struct FfiConverterTypeGetSessionHashError: FfiConverterRustBuffer { + typealias SwiftType = GetSessionHashError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DeployAccountError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetSessionHashError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .Deploy( + case 1: return .GetSessionHash( + try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidSessionConfig( try FfiConverterString.read(from: &buf) ) - case 2: return .AccountAlreadyDeployed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: DeployAccountError, into buf: inout [UInt8]) { + public static func write(_ value: GetSessionHashError, into buf: inout [UInt8]) { switch value { - case let .Deploy(v1): + case let .GetSessionHash(v1): writeInt(&buf, Int32(1)) FfiConverterString.write(v1, into: &buf) - case .AccountAlreadyDeployed: + case let .InvalidSessionConfig(v1): writeInt(&buf, Int32(2)) - + FfiConverterString.write(v1, into: &buf) + } } } @@ -2041,24 +2821,24 @@ public struct FfiConverterTypeDeployAccountError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDeployAccountError_lift(_ buf: RustBuffer) throws -> DeployAccountError { - return try FfiConverterTypeDeployAccountError.lift(buf) +public func FfiConverterTypeGetSessionHashError_lift(_ buf: RustBuffer) throws -> GetSessionHashError { + return try FfiConverterTypeGetSessionHashError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDeployAccountError_lower(_ value: DeployAccountError) -> RustBuffer { - return FfiConverterTypeDeployAccountError.lower(value) +public func FfiConverterTypeGetSessionHashError_lower(_ value: GetSessionHashError) -> RustBuffer { + return FfiConverterTypeGetSessionHashError.lower(value) } -extension DeployAccountError: Equatable, Hashable {} +extension GetSessionHashError: Equatable, Hashable {} -extension DeployAccountError: Foundation.LocalizedError { +extension GetSessionHashError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -2068,11 +2848,15 @@ extension DeployAccountError: Foundation.LocalizedError { -public enum FetchAccountError: Swift.Error { +public enum GetSessionStateError: Swift.Error { - case FetchAccount(String + case GetSessionState(String + ) + case InvalidAddress(String + ) + case InvalidSessionConfig(String ) } @@ -2080,17 +2864,23 @@ public enum FetchAccountError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeFetchAccountError: FfiConverterRustBuffer { - typealias SwiftType = FetchAccountError +public struct FfiConverterTypeGetSessionStateError: FfiConverterRustBuffer { + typealias SwiftType = GetSessionStateError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FetchAccountError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetSessionStateError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .FetchAccount( + case 1: return .GetSessionState( + try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidAddress( + try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidSessionConfig( try FfiConverterString.read(from: &buf) ) @@ -2098,17 +2888,27 @@ public struct FfiConverterTypeFetchAccountError: FfiConverterRustBuffer { } } - public static func write(_ value: FetchAccountError, into buf: inout [UInt8]) { + public static func write(_ value: GetSessionStateError, into buf: inout [UInt8]) { switch value { - case let .FetchAccount(v1): + case let .GetSessionState(v1): writeInt(&buf, Int32(1)) FfiConverterString.write(v1, into: &buf) + + case let .InvalidAddress(v1): + writeInt(&buf, Int32(2)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidSessionConfig(v1): + writeInt(&buf, Int32(3)) + FfiConverterString.write(v1, into: &buf) + } } } @@ -2117,24 +2917,24 @@ public struct FfiConverterTypeFetchAccountError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFetchAccountError_lift(_ buf: RustBuffer) throws -> FetchAccountError { - return try FfiConverterTypeFetchAccountError.lift(buf) +public func FfiConverterTypeGetSessionStateError_lift(_ buf: RustBuffer) throws -> GetSessionStateError { + return try FfiConverterTypeGetSessionStateError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFetchAccountError_lower(_ value: FetchAccountError) -> RustBuffer { - return FfiConverterTypeFetchAccountError.lower(value) +public func FfiConverterTypeGetSessionStateError_lower(_ value: GetSessionStateError) -> RustBuffer { + return FfiConverterTypeGetSessionStateError.lower(value) } -extension FetchAccountError: Equatable, Hashable {} +extension GetSessionStateError: Equatable, Hashable {} -extension FetchAccountError: Foundation.LocalizedError { +extension GetSessionStateError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -2144,11 +2944,17 @@ extension FetchAccountError: Foundation.LocalizedError { -public enum FundAccountError: Swift.Error { +public enum IsK1OwnerError: Swift.Error { - case FundAccount(String + case IsK1Owner(String + ) + case InvalidAccountAddress(String + ) + case InvalidOwnerAddress(String + ) + case InvalidConfig(String ) } @@ -2156,17 +2962,26 @@ public enum FundAccountError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeFundAccountError: FfiConverterRustBuffer { - typealias SwiftType = FundAccountError +public struct FfiConverterTypeIsK1OwnerError: FfiConverterRustBuffer { + typealias SwiftType = IsK1OwnerError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FundAccountError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IsK1OwnerError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .FundAccount( + case 1: return .IsK1Owner( + try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidAccountAddress( + try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidOwnerAddress( + try FfiConverterString.read(from: &buf) + ) + case 4: return .InvalidConfig( try FfiConverterString.read(from: &buf) ) @@ -2174,17 +2989,32 @@ public struct FfiConverterTypeFundAccountError: FfiConverterRustBuffer { } } - public static func write(_ value: FundAccountError, into buf: inout [UInt8]) { + public static func write(_ value: IsK1OwnerError, into buf: inout [UInt8]) { switch value { - case let .FundAccount(v1): + case let .IsK1Owner(v1): writeInt(&buf, Int32(1)) FfiConverterString.write(v1, into: &buf) + + case let .InvalidAccountAddress(v1): + writeInt(&buf, Int32(2)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidOwnerAddress(v1): + writeInt(&buf, Int32(3)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidConfig(v1): + writeInt(&buf, Int32(4)) + FfiConverterString.write(v1, into: &buf) + } } } @@ -2193,24 +3023,24 @@ public struct FfiConverterTypeFundAccountError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFundAccountError_lift(_ buf: RustBuffer) throws -> FundAccountError { - return try FfiConverterTypeFundAccountError.lift(buf) +public func FfiConverterTypeIsK1OwnerError_lift(_ buf: RustBuffer) throws -> IsK1OwnerError { + return try FfiConverterTypeIsK1OwnerError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFundAccountError_lower(_ value: FundAccountError) -> RustBuffer { - return FfiConverterTypeFundAccountError.lower(value) +public func FfiConverterTypeIsK1OwnerError_lower(_ value: IsK1OwnerError) -> RustBuffer { + return FfiConverterTypeIsK1OwnerError.lower(value) } -extension FundAccountError: Equatable, Hashable {} +extension IsK1OwnerError: Equatable, Hashable {} -extension FundAccountError: Foundation.LocalizedError { +extension IsK1OwnerError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -2220,11 +3050,17 @@ extension FundAccountError: Foundation.LocalizedError { -public enum GetAccountBalanceError: Swift.Error { +public enum IsModuleValidatorError: Swift.Error { - case GetBalance(String + case IsModuleValidator(String + ) + case InvalidAccountAddress(String + ) + case InvalidModuleAddress(String + ) + case InvalidConfig(String ) } @@ -2232,17 +3068,26 @@ public enum GetAccountBalanceError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeGetAccountBalanceError: FfiConverterRustBuffer { - typealias SwiftType = GetAccountBalanceError +public struct FfiConverterTypeIsModuleValidatorError: FfiConverterRustBuffer { + typealias SwiftType = IsModuleValidatorError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetAccountBalanceError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IsModuleValidatorError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .GetBalance( + case 1: return .IsModuleValidator( + try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidAccountAddress( + try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidModuleAddress( + try FfiConverterString.read(from: &buf) + ) + case 4: return .InvalidConfig( try FfiConverterString.read(from: &buf) ) @@ -2250,17 +3095,32 @@ public struct FfiConverterTypeGetAccountBalanceError: FfiConverterRustBuffer { } } - public static func write(_ value: GetAccountBalanceError, into buf: inout [UInt8]) { + public static func write(_ value: IsModuleValidatorError, into buf: inout [UInt8]) { switch value { - case let .GetBalance(v1): + case let .IsModuleValidator(v1): writeInt(&buf, Int32(1)) FfiConverterString.write(v1, into: &buf) + + case let .InvalidAccountAddress(v1): + writeInt(&buf, Int32(2)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidModuleAddress(v1): + writeInt(&buf, Int32(3)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidConfig(v1): + writeInt(&buf, Int32(4)) + FfiConverterString.write(v1, into: &buf) + } } } @@ -2269,24 +3129,24 @@ public struct FfiConverterTypeGetAccountBalanceError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeGetAccountBalanceError_lift(_ buf: RustBuffer) throws -> GetAccountBalanceError { - return try FfiConverterTypeGetAccountBalanceError.lift(buf) +public func FfiConverterTypeIsModuleValidatorError_lift(_ buf: RustBuffer) throws -> IsModuleValidatorError { + return try FfiConverterTypeIsModuleValidatorError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeGetAccountBalanceError_lower(_ value: GetAccountBalanceError) -> RustBuffer { - return FfiConverterTypeGetAccountBalanceError.lower(value) +public func FfiConverterTypeIsModuleValidatorError_lower(_ value: IsModuleValidatorError) -> RustBuffer { + return FfiConverterTypeIsModuleValidatorError.lower(value) } -extension GetAccountBalanceError: Equatable, Hashable {} +extension IsModuleValidatorError: Equatable, Hashable {} -extension GetAccountBalanceError: Foundation.LocalizedError { +extension IsModuleValidatorError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -2566,6 +3426,92 @@ extension PrepareTransactionError: Foundation.LocalizedError { + +public enum RevokeSessionError: Swift.Error { + + + + case RevokeSession(String + ) + case InvalidAddress(String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeRevokeSessionError: FfiConverterRustBuffer { + typealias SwiftType = RevokeSessionError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> RevokeSessionError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .RevokeSession( + try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidAddress( + try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: RevokeSessionError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .RevokeSession(v1): + writeInt(&buf, Int32(1)) + FfiConverterString.write(v1, into: &buf) + + + case let .InvalidAddress(v1): + writeInt(&buf, Int32(2)) + FfiConverterString.write(v1, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeRevokeSessionError_lift(_ buf: RustBuffer) throws -> RevokeSessionError { + return try FfiConverterTypeRevokeSessionError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeRevokeSessionError_lower(_ value: RevokeSessionError) -> RustBuffer { + return FfiConverterTypeRevokeSessionError.lower(value) +} + + +extension RevokeSessionError: Equatable, Hashable {} + + + + +extension RevokeSessionError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. @@ -2987,6 +3933,55 @@ fileprivate struct FfiConverterOptionTypeDeployWallet: FfiConverterRustBuffer { } } } + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [String]? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterSequenceString.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterSequenceString.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [String] + + public static func write(_ value: [String], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterString.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] { + let len: Int32 = try readInt(&buf) + var seq = [String]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterString.read(from: &buf)) + } + return seq + } +} private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0 private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1 @@ -3113,11 +4108,11 @@ public func createSession(args: CreateSessionArgs, config: Config)async throws errorHandler: FfiConverterTypeCreateSessionError_lift ) } -public func deployAccount(passkeyParameters: PasskeyParameters, config: Config)async throws -> Account { +public func deployAccount(passkeyParameters: PasskeyParameters, initialK1Owners: [String]?, initialSessionConfigJson: String?, config: Config)async throws -> Account { return try await uniffiRustCallAsync( rustFutureFunc: { - uniffi_ffi_fn_func_deploy_account(FfiConverterTypePasskeyParameters_lower(passkeyParameters),FfiConverterTypeConfig_lower(config) + uniffi_ffi_fn_func_deploy_account(FfiConverterTypePasskeyParameters_lower(passkeyParameters),FfiConverterOptionSequenceString.lower(initialK1Owners),FfiConverterOptionString.lower(initialSessionConfigJson),FfiConverterTypeConfig_lower(config) ) }, pollFunc: ffi_ffi_rust_future_poll_rust_buffer, @@ -3127,11 +4122,11 @@ public func deployAccount(passkeyParameters: PasskeyParameters, config: Config)a errorHandler: FfiConverterTypeDeployAccountError_lift ) } -public func deployAccountWithUniqueId(passkeyParameters: PasskeyParameters, uniqueAccountId: String, config: Config)async throws -> Account { +public func deployAccountWithUniqueId(passkeyParameters: PasskeyParameters, uniqueAccountId: String, initialK1Owners: [String]?, initialSessionConfigJson: String?, config: Config)async throws -> Account { return try await uniffiRustCallAsync( rustFutureFunc: { - uniffi_ffi_fn_func_deploy_account_with_unique_id(FfiConverterTypePasskeyParameters_lower(passkeyParameters),FfiConverterString.lower(uniqueAccountId),FfiConverterTypeConfig_lower(config) + uniffi_ffi_fn_func_deploy_account_with_unique_id(FfiConverterTypePasskeyParameters_lower(passkeyParameters),FfiConverterString.lower(uniqueAccountId),FfiConverterOptionSequenceString.lower(initialK1Owners),FfiConverterOptionString.lower(initialSessionConfigJson),FfiConverterTypeConfig_lower(config) ) }, pollFunc: ffi_ffi_rust_future_poll_rust_buffer, @@ -3203,6 +4198,27 @@ public func getBalance(address: String, config: Config)async throws -> AccountB errorHandler: FfiConverterTypeGetAccountBalanceError_lift ) } +public func getSessionHash(sessionConfigJson: String)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeGetSessionHashError_lift) { + uniffi_ffi_fn_func_get_session_hash( + FfiConverterString.lower(sessionConfigJson),$0 + ) +}) +} +public func getSessionState(args: GetSessionStateArgs, config: Config)async throws -> GetSessionStateReturnType { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_ffi_fn_func_get_session_state(FfiConverterTypeGetSessionStateArgs_lower(args),FfiConverterTypeConfig_lower(config) + ) + }, + pollFunc: ffi_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeGetSessionStateReturnType_lift, + errorHandler: FfiConverterTypeGetSessionStateError_lift + ) +} /** * Initialize the Android logger */ @@ -3222,6 +4238,34 @@ public func initAppleLogger(bundleIdentifier: String, level: LogLevel) {try! ru ) } } +public func isK1Owner(args: IsK1OwnerArgs, config: Config)async throws -> Bool { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_ffi_fn_func_is_k1_owner(FfiConverterTypeIsK1OwnerArgs_lower(args),FfiConverterTypeConfig_lower(config) + ) + }, + pollFunc: ffi_ffi_rust_future_poll_i8, + completeFunc: ffi_ffi_rust_future_complete_i8, + freeFunc: ffi_ffi_rust_future_free_i8, + liftFunc: FfiConverterBool.lift, + errorHandler: FfiConverterTypeIsK1OwnerError_lift + ) +} +public func isModuleValidator(args: IsModuleValidatorArgs, config: Config)async throws -> Bool { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_ffi_fn_func_is_module_validator(FfiConverterTypeIsModuleValidatorArgs_lower(args),FfiConverterTypeConfig_lower(config) + ) + }, + pollFunc: ffi_ffi_rust_future_poll_i8, + completeFunc: ffi_ffi_rust_future_complete_i8, + freeFunc: ffi_ffi_rust_future_free_i8, + liftFunc: FfiConverterBool.lift, + errorHandler: FfiConverterTypeIsModuleValidatorError_lift + ) +} public func prepareSendTransaction(transaction: Transaction, config: Config)async throws -> PreparedTransaction { return try await uniffiRustCallAsync( @@ -3236,6 +4280,20 @@ public func prepareSendTransaction(transaction: Transaction, config: Config)asyn errorHandler: FfiConverterTypePrepareTransactionError_lift ) } +public func revokeSession(args: RevokeSessionArgs, config: Config)async throws -> RevokeSessionReturnType { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_ffi_fn_func_revoke_session(FfiConverterTypeRevokeSessionArgs_lower(args),FfiConverterTypeConfig_lower(config) + ) + }, + pollFunc: ffi_ffi_rust_future_poll_rust_buffer, + completeFunc: ffi_ffi_rust_future_complete_rust_buffer, + freeFunc: ffi_ffi_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeRevokeSessionReturnType_lift, + errorHandler: FfiConverterTypeRevokeSessionError_lift + ) +} public func sendSessionTransaction(accountAddress: String, sessionKeyHex: String, sessionConfigJson: String, config: Config, transaction: Transaction)async throws -> String { return try await uniffiRustCallAsync( @@ -3297,10 +4355,10 @@ private let initializationResult: InitializationResult = { if (uniffi_ffi_checksum_func_create_session() != 34501) { return InitializationResult.apiChecksumMismatch } - if (uniffi_ffi_checksum_func_deploy_account() != 40553) { + if (uniffi_ffi_checksum_func_deploy_account() != 65329) { return InitializationResult.apiChecksumMismatch } - if (uniffi_ffi_checksum_func_deploy_account_with_unique_id() != 10501) { + if (uniffi_ffi_checksum_func_deploy_account_with_unique_id() != 55552) { return InitializationResult.apiChecksumMismatch } if (uniffi_ffi_checksum_func_fetch_account() != 42263) { @@ -3318,15 +4376,30 @@ private let initializationResult: InitializationResult = { if (uniffi_ffi_checksum_func_get_balance() != 46562) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ffi_checksum_func_get_session_hash() != 65156) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ffi_checksum_func_get_session_state() != 4047) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ffi_checksum_func_init_android_logger() != 11407) { return InitializationResult.apiChecksumMismatch } if (uniffi_ffi_checksum_func_init_apple_logger() != 51227) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ffi_checksum_func_is_k1_owner() != 54082) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ffi_checksum_func_is_module_validator() != 12607) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ffi_checksum_func_prepare_send_transaction() != 13974) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ffi_checksum_func_revoke_session() != 28098) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ffi_checksum_func_send_session_transaction() != 1510) { return InitializationResult.apiChecksumMismatch } diff --git a/packages/sdk-platforms/swift/ZKsyncSSO/Tests/ZKsyncSSOTests/ZKsyncSSOTests.swift b/packages/sdk-platforms/swift/ZKsyncSSO/Tests/ZKsyncSSOTests/ZKsyncSSOTests.swift index 8c936b52..09e2345c 100644 --- a/packages/sdk-platforms/swift/ZKsyncSSO/Tests/ZKsyncSSOTests/ZKsyncSSOTests.swift +++ b/packages/sdk-platforms/swift/ZKsyncSSO/Tests/ZKsyncSSOTests/ZKsyncSSOTests.swift @@ -1,6 +1,4 @@ import Testing @testable import ZKsyncSSO -@Test func example() async throws { - -} +@Test func example() async throws {}