UNPKG

@renproject/rentx

Version:

XState Statemachines for tracking RenVM transactions reactively

152 lines (133 loc) 4.55 kB
import { BurnMachineContext, BurnMachineEvent } from "../machines/burn"; import BigNumber from "bignumber.js"; import { GatewaySession } from "../types/transaction"; import { Sender, MachineOptions, Receiver } from "xstate"; /* Sample burnChainMap / releaseChainMap implementations We don't implement these to prevent mandating specific chains const burnChainMap: BurnMachineContext["fromChainMap"] = { ethereum: (context): MintChain<any> => { return Ethereum(context.providers.ethereum, RenNetwork.Testnet).Account( { address: context.tx.userAddress, value: context.tx.suggestedAmount, } ) as MintChain<any>; }, }; const releaseChainMap: BurnMachineContext["toChainMap"] = { bitcoin: (context): LockChain => { return Bitcoin().Address(context.tx.destAddress) as any; }, }; */ const burnAndRelease = async (context: BurnMachineContext) => { const txHash = Object.keys(context.tx.transactions)[0]; return await context.sdk.burnAndRelease({ asset: "BTC", to: context.toChainMap[context.tx.destNetwork](context), from: context.fromChainMap[context.tx.sourceNetwork](context), ...(txHash ? { txHash } : {}), }); }; // Format a transaction and prompt the user to sign const txCreator = async ( context: BurnMachineContext ): Promise<GatewaySession> => { const asset = context.tx.sourceAsset; let suggestedAmount = new BigNumber(Number(context.tx.targetAmount) * 1e8) .decimalPlaces(8) .toFixed(); try { const fees = await context.sdk.getFees(); suggestedAmount = new BigNumber( Math.floor( fees[asset.toLowerCase()].release + Number(context.tx.targetAmount) * 1e8 ) ) .decimalPlaces(0) .toFixed(); } catch (e) {} const newTx: GatewaySession = { ...context.tx, suggestedAmount, }; context.tx = newTx; const burn = await burnAndRelease(context); // We immediately submit the burn request const hash: string = await new Promise(async (resolve, reject) => { try { await burn .burn() .on("transactionHash", (hash: string) => resolve(hash)); } catch (e) { reject(e); } }); return { ...newTx, transactions: { [hash]: { sourceTxHash: hash, sourceTxConfs: 0, sourceTxAmount: Number(suggestedAmount), rawSourceTx: {}, }, }, }; }; const burnTransactionListener = (context: BurnMachineContext) => ( callback: Sender<BurnMachineEvent>, _receive: Receiver<any> ) => { const cleaners: Array<() => void> = []; burnAndRelease(context).then(async (burn) => { let confirmations = 0; const tx = Object.values(context.tx.transactions)[0]; const burnRef = burn.burn(); const burnListener = (confs: number) => { confirmations = confs; callback({ type: "CONFIRMATION", data: { ...tx, sourceTxConfs: confs }, }); }; cleaners.push(() => { burnRef._cancel(); burnRef.removeListener("confirmation", burnListener); }); await burnRef ?.on("confirmation", burnListener) .catch((error) => callback({ type: "BURN_ERROR", data: error })); const releaseListener = (status: string) => status === "confirming" ? console.log(`confirming (${confirmations}/15)`) : console.log(status); const hashListener = (hash: string) => console.log("hash", hash); const releaseRef = burn.release(); cleaners.push(() => { releaseRef._cancel(); releaseRef.removeListener("status", releaseListener); releaseRef.removeListener("txHash", hashListener); }); await releaseRef .on("status", releaseListener) .on("txHash", hashListener) .catch((error) => callback({ type: "RELEASE_ERROR", data: error })); }); return () => { for (let i of cleaners) { i(); } }; }; export const burnConfig: Partial<MachineOptions< BurnMachineContext, BurnMachineEvent >> = { services: { burnCreator: txCreator, burnListener: burnTransactionListener, }, };