Skip to content

Commit 3d271ce

Browse files
authored
Merge pull request #329 from midexol/feature/token-locking-sdk-retry
feat: Add token locking mechanism and SDK retry logic (#191, #170)
2 parents e217994 + e8d1af2 commit 3d271ce

4 files changed

Lines changed: 50 additions & 10 deletions

File tree

contracts/property-token/src/events.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,7 @@ pub struct DividendsWithdrawn {
172172
pub amount: u128,
173173
}
174174

175-
#[ink(event)]
176-
pub struct VestedTokensClaimed {
177-
#[ink(topic)]
178-
pub token_id: TokenId,
179-
#[ink(topic)]
180-
pub account: AccountId,
181-
pub amount: u128,
182-
}
175+
183176

184177
// =========================================================================
185178
// Metadata Events
@@ -206,6 +199,8 @@ pub struct TokenURIUpdated {
206199
// Governance Events
207200
// =========================================================================
208201

202+
203+
209204
#[ink(event)]
210205
pub struct ProposalCreated {
211206
#[ink(topic)]

contracts/property-token/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,8 @@ pub mod property_token {
588588
}
589589
}
590590

591+
592+
591593
/// ERC-721: Returns the balance of tokens owned by an account
592594
#[ink(message)]
593595
pub fn balance_of(&self, owner: AccountId) -> u32 {
@@ -646,6 +648,8 @@ pub mod property_token {
646648
return Err(Error::Unauthorized);
647649
}
648650

651+
652+
649653
// Perform the transfer
650654
self.remove_token_from_owner(from, token_id)?;
651655
self.add_token_to_owner(to, token_id)?;
@@ -803,6 +807,7 @@ pub mod property_token {
803807
let token_id = ids[i];
804808
let amount = amounts[i];
805809
let from_balance = self.balances.get((&from, &token_id)).unwrap_or(0);
810+
806811
self.balances
807812
.insert((&from, &token_id), &(from_balance - amount));
808813
let to_balance = self.balances.get((&to, &token_id)).unwrap_or(0);

sdk/frontend/src/client/PropertyTokenClient.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import type {
3535
import { TxProgressStatus } from '../types';
3636
import { decodeContractError, TransactionError, GasEstimationError } from '../utils/errors';
3737
import { decodeTransactionEvents, subscribeToNamedEvent } from '../utils/events';
38+
import { withExponentialBackoff } from '../utils/connection';
3839
import type { PropChainEventName, PropChainEventMap } from '../types/events';
3940

4041
export type Signer = KeyringPair | string;
@@ -610,7 +611,8 @@ export class PropertyTokenClient {
610611
// ==========================================================================
611612

612613
private async query(method: string, args: unknown[]): Promise<unknown> {
613-
const queryFn = this.contract.query[method];
614+
return withExponentialBackoff(async () => {
615+
const queryFn = this.contract.query[method];
614616
if (!queryFn) {
615617
throw new Error(`Unknown query method: ${method}`);
616618
}
@@ -623,7 +625,8 @@ export class PropertyTokenClient {
623625
throw decodeContractError(errorVariant);
624626
}
625627

626-
return output ? output.toJSON() : null;
628+
return output ? output.toJSON() : null;
629+
});
627630
}
628631

629632
private async submitTx(

sdk/frontend/src/utils/connection.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,43 @@ export async function connectToNetwork(networkName: string): Promise<ApiPromise>
142142
return createApi(config.wsEndpoint);
143143
}
144144

145+
/**
146+
* Executes a Promise-returning operation with exponential backoff retry logic.
147+
*
148+
* @param operation - The function to execute
149+
* @param maxRetries - Maximum number of retries (default: 3)
150+
* @param baseDelayMs - Base delay between retries in ms (default: 1000)
151+
* @returns The result of the operation
152+
* @throws The last error encountered after exhausting all retries
153+
*/
154+
export async function withExponentialBackoff<T>(
155+
operation: () => Promise<T>,
156+
maxRetries: number = 3,
157+
baseDelayMs: number = 1000,
158+
): Promise<T> {
159+
let attempt = 0;
160+
let lastError: Error | undefined;
161+
162+
while (attempt <= maxRetries) {
163+
try {
164+
return await operation();
165+
} catch (error) {
166+
lastError = error instanceof Error ? error : new Error(String(error));
167+
168+
// Do not retry if we've reached maxRetries
169+
if (attempt === maxRetries) {
170+
break;
171+
}
172+
173+
const delay = baseDelayMs * Math.pow(2, attempt);
174+
await sleep(delay);
175+
attempt++;
176+
}
177+
}
178+
179+
throw lastError;
180+
}
181+
145182
/**
146183
* Gets the network configuration for a preset name.
147184
*

0 commit comments

Comments
 (0)