diff --git a/contracts/property-token/src/events.rs b/contracts/property-token/src/events.rs index 3f4c6c3a2..22f23a7c2 100644 --- a/contracts/property-token/src/events.rs +++ b/contracts/property-token/src/events.rs @@ -172,14 +172,7 @@ pub struct DividendsWithdrawn { pub amount: u128, } -#[ink(event)] -pub struct VestedTokensClaimed { - #[ink(topic)] - pub token_id: TokenId, - #[ink(topic)] - pub account: AccountId, - pub amount: u128, -} + // ========================================================================= // Metadata Events @@ -206,6 +199,8 @@ pub struct TokenURIUpdated { // Governance Events // ========================================================================= + + #[ink(event)] pub struct ProposalCreated { #[ink(topic)] diff --git a/contracts/property-token/src/lib.rs b/contracts/property-token/src/lib.rs index 4014ecb31..bc1542cbd 100644 --- a/contracts/property-token/src/lib.rs +++ b/contracts/property-token/src/lib.rs @@ -588,6 +588,8 @@ pub mod property_token { } } + + /// ERC-721: Returns the balance of tokens owned by an account #[ink(message)] pub fn balance_of(&self, owner: AccountId) -> u32 { @@ -646,6 +648,8 @@ pub mod property_token { return Err(Error::Unauthorized); } + + // Perform the transfer self.remove_token_from_owner(from, token_id)?; self.add_token_to_owner(to, token_id)?; @@ -803,6 +807,7 @@ pub mod property_token { let token_id = ids[i]; let amount = amounts[i]; let from_balance = self.balances.get((&from, &token_id)).unwrap_or(0); + self.balances .insert((&from, &token_id), &(from_balance - amount)); let to_balance = self.balances.get((&to, &token_id)).unwrap_or(0); diff --git a/sdk/frontend/src/client/PropertyTokenClient.ts b/sdk/frontend/src/client/PropertyTokenClient.ts index 1495a2b7f..ec2ac8594 100644 --- a/sdk/frontend/src/client/PropertyTokenClient.ts +++ b/sdk/frontend/src/client/PropertyTokenClient.ts @@ -35,6 +35,7 @@ import type { import { TxProgressStatus } from '../types'; import { decodeContractError, TransactionError, GasEstimationError } from '../utils/errors'; import { decodeTransactionEvents, subscribeToNamedEvent } from '../utils/events'; +import { withExponentialBackoff } from '../utils/connection'; import type { PropChainEventName, PropChainEventMap } from '../types/events'; export type Signer = KeyringPair | string; @@ -610,7 +611,8 @@ export class PropertyTokenClient { // ========================================================================== private async query(method: string, args: unknown[]): Promise { - const queryFn = this.contract.query[method]; + return withExponentialBackoff(async () => { + const queryFn = this.contract.query[method]; if (!queryFn) { throw new Error(`Unknown query method: ${method}`); } @@ -623,7 +625,8 @@ export class PropertyTokenClient { throw decodeContractError(errorVariant); } - return output ? output.toJSON() : null; + return output ? output.toJSON() : null; + }); } private async submitTx( diff --git a/sdk/frontend/src/utils/connection.ts b/sdk/frontend/src/utils/connection.ts index 3d10b1ca5..4dd189ed0 100644 --- a/sdk/frontend/src/utils/connection.ts +++ b/sdk/frontend/src/utils/connection.ts @@ -142,6 +142,43 @@ export async function connectToNetwork(networkName: string): Promise return createApi(config.wsEndpoint); } +/** + * Executes a Promise-returning operation with exponential backoff retry logic. + * + * @param operation - The function to execute + * @param maxRetries - Maximum number of retries (default: 3) + * @param baseDelayMs - Base delay between retries in ms (default: 1000) + * @returns The result of the operation + * @throws The last error encountered after exhausting all retries + */ +export async function withExponentialBackoff( + operation: () => Promise, + maxRetries: number = 3, + baseDelayMs: number = 1000, +): Promise { + let attempt = 0; + let lastError: Error | undefined; + + while (attempt <= maxRetries) { + try { + return await operation(); + } catch (error) { + lastError = error instanceof Error ? error : new Error(String(error)); + + // Do not retry if we've reached maxRetries + if (attempt === maxRetries) { + break; + } + + const delay = baseDelayMs * Math.pow(2, attempt); + await sleep(delay); + attempt++; + } + } + + throw lastError; +} + /** * Gets the network configuration for a preset name. *