mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 21:47:56 +00:00
1295c36241
- Fixed TypeScript type assertion issues - Updated imports from api-augment/substrate to api-augment/bizinikiwi - Fixed imgConvert.mjs header and imports - Added @ts-expect-error for runtime-converted types - Fixed all @polkadot copyright headers to @pezkuwi
315 lines
9.1 KiB
TypeScript
315 lines
9.1 KiB
TypeScript
// ========================================
|
|
// USDT Bridge Utilities
|
|
// ========================================
|
|
// Handles wUSDT minting, burning, and reserve management
|
|
|
|
import type { ApiPromise } from '@pezkuwi/api';
|
|
import { ASSET_IDS, ASSET_CONFIGS } from './wallet';
|
|
import { getMultisigMembers, createMultisigTx } from './multisig';
|
|
|
|
// ========================================
|
|
// CONSTANTS
|
|
// ========================================
|
|
|
|
export const WUSDT_ASSET_ID = ASSET_CONFIGS.WUSDT.id;
|
|
export const WUSDT_DECIMALS = ASSET_CONFIGS.WUSDT.decimals;
|
|
|
|
// Withdrawal limits and timeouts
|
|
export const WITHDRAWAL_LIMITS = {
|
|
instant: {
|
|
maxAmount: 1000, // $1,000
|
|
delay: 0, // No delay
|
|
},
|
|
standard: {
|
|
maxAmount: 10000, // $10,000
|
|
delay: 3600, // 1 hour in seconds
|
|
},
|
|
large: {
|
|
maxAmount: Infinity,
|
|
delay: 86400, // 24 hours
|
|
},
|
|
};
|
|
|
|
// ========================================
|
|
// ASSET QUERIES
|
|
// ========================================
|
|
|
|
/**
|
|
* Get wUSDT balance for an account
|
|
* @param api - Polkadot API instance
|
|
* @param address - Account address
|
|
* @returns Balance in human-readable format
|
|
*/
|
|
export async function getWUSDTBalance(api: ApiPromise, address: string): Promise<number> {
|
|
try {
|
|
const balance = await api.query.assets.account(WUSDT_ASSET_ID, address);
|
|
|
|
if (balance.isSome) {
|
|
const balanceData = balance.unwrap().toJSON() as any;
|
|
return Number(balanceData.balance) / Math.pow(10, WUSDT_DECIMALS);
|
|
}
|
|
|
|
return 0;
|
|
} catch (error) {
|
|
console.error('Error fetching wUSDT balance:', error);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get total wUSDT supply
|
|
* @param api - Polkadot API instance
|
|
* @returns Total supply in human-readable format
|
|
*/
|
|
export async function getWUSDTTotalSupply(api: ApiPromise): Promise<number> {
|
|
try {
|
|
const assetDetails = await api.query.assets.asset(WUSDT_ASSET_ID);
|
|
|
|
if (assetDetails.isSome) {
|
|
const details = assetDetails.unwrap().toJSON() as any;
|
|
return Number(details.supply) / Math.pow(10, WUSDT_DECIMALS);
|
|
}
|
|
|
|
return 0;
|
|
} catch (error) {
|
|
console.error('Error fetching wUSDT supply:', error);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get wUSDT asset metadata
|
|
* @param api - Polkadot API instance
|
|
* @returns Asset metadata
|
|
*/
|
|
export async function getWUSDTMetadata(api: ApiPromise) {
|
|
try {
|
|
const metadata = await api.query.assets.metadata(WUSDT_ASSET_ID);
|
|
return metadata.toJSON();
|
|
} catch (error) {
|
|
console.error('Error fetching wUSDT metadata:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// MULTISIG OPERATIONS
|
|
// ========================================
|
|
|
|
/**
|
|
* Create multisig transaction to mint wUSDT
|
|
* @param api - Polkadot API instance
|
|
* @param beneficiary - Who will receive the wUSDT
|
|
* @param amount - Amount in human-readable format (e.g., 100.50 USDT)
|
|
* @param signerAddress - Address of the signer creating this tx
|
|
* @param specificAddresses - Addresses for non-unique multisig members
|
|
* @returns Multisig transaction
|
|
*/
|
|
export async function createMintWUSDTTx(
|
|
api: ApiPromise,
|
|
beneficiary: string,
|
|
amount: number,
|
|
signerAddress: string,
|
|
specificAddresses: Record<string, string> = {}
|
|
) {
|
|
// Convert to smallest unit
|
|
const amountBN = BigInt(Math.floor(amount * Math.pow(10, WUSDT_DECIMALS)));
|
|
|
|
// Create the mint call
|
|
const mintCall = api.tx.assets.mint(WUSDT_ASSET_ID, beneficiary, amountBN.toString());
|
|
|
|
// Get all multisig members
|
|
const allMembers = await getMultisigMembers(api, specificAddresses);
|
|
|
|
// Other signatories (excluding current signer)
|
|
const otherSignatories = allMembers.filter((addr) => addr !== signerAddress);
|
|
|
|
// Create multisig transaction
|
|
return createMultisigTx(api, mintCall, otherSignatories);
|
|
}
|
|
|
|
/**
|
|
* Create multisig transaction to burn wUSDT
|
|
* @param api - Polkadot API instance
|
|
* @param from - Who will have their wUSDT burned
|
|
* @param amount - Amount in human-readable format
|
|
* @param signerAddress - Address of the signer creating this tx
|
|
* @param specificAddresses - Addresses for non-unique multisig members
|
|
* @returns Multisig transaction
|
|
*/
|
|
export async function createBurnWUSDTTx(
|
|
api: ApiPromise,
|
|
from: string,
|
|
amount: number,
|
|
signerAddress: string,
|
|
specificAddresses: Record<string, string> = {}
|
|
) {
|
|
const amountBN = BigInt(Math.floor(amount * Math.pow(10, WUSDT_DECIMALS)));
|
|
|
|
const burnCall = api.tx.assets.burn(WUSDT_ASSET_ID, from, amountBN.toString());
|
|
|
|
const allMembers = await getMultisigMembers(api, specificAddresses);
|
|
const otherSignatories = allMembers.filter((addr) => addr !== signerAddress);
|
|
|
|
return createMultisigTx(api, burnCall, otherSignatories);
|
|
}
|
|
|
|
// ========================================
|
|
// WITHDRAWAL HELPERS
|
|
// ========================================
|
|
|
|
/**
|
|
* Calculate withdrawal delay based on amount
|
|
* @param amount - Withdrawal amount in USDT
|
|
* @returns Delay in seconds
|
|
*/
|
|
export function calculateWithdrawalDelay(amount: number): number {
|
|
if (amount <= WITHDRAWAL_LIMITS.instant.maxAmount) {
|
|
return WITHDRAWAL_LIMITS.instant.delay;
|
|
} else if (amount <= WITHDRAWAL_LIMITS.standard.maxAmount) {
|
|
return WITHDRAWAL_LIMITS.standard.delay;
|
|
} else {
|
|
return WITHDRAWAL_LIMITS.large.delay;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get withdrawal tier name
|
|
* @param amount - Withdrawal amount
|
|
* @returns Tier name
|
|
*/
|
|
export function getWithdrawalTier(amount: number): string {
|
|
if (amount <= WITHDRAWAL_LIMITS.instant.maxAmount) return 'Instant';
|
|
if (amount <= WITHDRAWAL_LIMITS.standard.maxAmount) return 'Standard';
|
|
return 'Large';
|
|
}
|
|
|
|
/**
|
|
* Format delay time for display
|
|
* @param seconds - Delay in seconds
|
|
* @returns Human-readable format
|
|
*/
|
|
export function formatDelay(seconds: number): string {
|
|
if (seconds === 0) return 'Instant';
|
|
if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes`;
|
|
if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours`;
|
|
return `${Math.floor(seconds / 86400)} days`;
|
|
}
|
|
|
|
// ========================================
|
|
// RESERVE CHECKING
|
|
// ========================================
|
|
|
|
export interface ReserveStatus {
|
|
wusdtSupply: number;
|
|
offChainReserve: number; // This would come from off-chain oracle/API
|
|
collateralRatio: number;
|
|
isHealthy: boolean;
|
|
}
|
|
|
|
/**
|
|
* Check reserve health
|
|
* @param api - Polkadot API instance
|
|
* @param offChainReserve - Off-chain USDT reserve amount (from treasury)
|
|
* @returns Reserve status
|
|
*/
|
|
export async function checkReserveHealth(
|
|
api: ApiPromise,
|
|
offChainReserve: number
|
|
): Promise<ReserveStatus> {
|
|
const wusdtSupply = await getWUSDTTotalSupply(api);
|
|
|
|
const collateralRatio = wusdtSupply > 0 ? (offChainReserve / wusdtSupply) * 100 : 0;
|
|
|
|
return {
|
|
wusdtSupply,
|
|
offChainReserve,
|
|
collateralRatio,
|
|
isHealthy: collateralRatio >= 100, // At least 100% backed
|
|
};
|
|
}
|
|
|
|
// ========================================
|
|
// EVENT MONITORING
|
|
// ========================================
|
|
|
|
/**
|
|
* Subscribe to wUSDT mint events
|
|
* @param api - Polkadot API instance
|
|
* @param callback - Callback function for each mint event
|
|
*/
|
|
export function subscribeToMintEvents(
|
|
api: ApiPromise,
|
|
callback: (beneficiary: string, amount: number, txHash: string) => void
|
|
) {
|
|
return api.query.system.events((events) => {
|
|
events.forEach((record) => {
|
|
const { event } = record;
|
|
|
|
if (api.events.assets.Issued.is(event)) {
|
|
const [assetId, beneficiary, amount] = event.data;
|
|
|
|
if (assetId.toNumber() === WUSDT_ASSET_ID) {
|
|
const amountNum = Number(amount.toString()) / Math.pow(10, WUSDT_DECIMALS);
|
|
callback(beneficiary.toString(), amountNum, record.hash.toHex());
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Subscribe to wUSDT burn events
|
|
* @param api - Polkadot API instance
|
|
* @param callback - Callback function for each burn event
|
|
*/
|
|
export function subscribeToBurnEvents(
|
|
api: ApiPromise,
|
|
callback: (account: string, amount: number, txHash: string) => void
|
|
) {
|
|
return api.query.system.events((events) => {
|
|
events.forEach((record) => {
|
|
const { event } = record;
|
|
|
|
if (api.events.assets.Burned.is(event)) {
|
|
const [assetId, account, amount] = event.data;
|
|
|
|
if (assetId.toNumber() === WUSDT_ASSET_ID) {
|
|
const amountNum = Number(amount.toString()) / Math.pow(10, WUSDT_DECIMALS);
|
|
callback(account.toString(), amountNum, record.hash.toHex());
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// ========================================
|
|
// DISPLAY HELPERS
|
|
// ========================================
|
|
|
|
/**
|
|
* Format wUSDT amount for display
|
|
* @param amount - Amount in smallest unit or human-readable
|
|
* @param fromSmallestUnit - Whether input is in smallest unit
|
|
* @returns Formatted string
|
|
*/
|
|
export function formatWUSDT(amount: number | string, fromSmallestUnit = false): string {
|
|
const value = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
|
|
if (fromSmallestUnit) {
|
|
return (value / Math.pow(10, WUSDT_DECIMALS)).toFixed(2);
|
|
}
|
|
|
|
return value.toFixed(2);
|
|
}
|
|
|
|
/**
|
|
* Parse human-readable USDT to smallest unit
|
|
* @param amount - Human-readable amount
|
|
* @returns Amount in smallest unit (BigInt)
|
|
*/
|
|
export function parseWUSDT(amount: number | string): bigint {
|
|
const value = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
return BigInt(Math.floor(value * Math.pow(10, WUSDT_DECIMALS)));
|
|
}
|