diff --git a/packages/walletkit/src/client.ts b/packages/walletkit/src/client.ts index dbdd4e3..45885ba 100644 --- a/packages/walletkit/src/client.ts +++ b/packages/walletkit/src/client.ts @@ -186,9 +186,9 @@ export class WalletKit extends IWalletKit { } }; - public canFulfil: IWalletKit["canFulfil"] = async (params) => { + public prepareFulfilment: IWalletKit["prepareFulfilment"] = async (params) => { try { - return await this.engine.canFulfil(params); + return await this.engine.prepareFulfilment(params); } catch (error: any) { this.logger.error(error.message); throw error; @@ -204,6 +204,33 @@ export class WalletKit extends IWalletKit { } }; + public estimateFees: IWalletKit["estimateFees"] = async (params) => { + try { + return await this.engine.estimateFees(params); + } catch (error: any) { + this.logger.error(error.message); + throw error; + } + }; + + public getERC20Balance: IWalletKit["getERC20Balance"] = async (params) => { + try { + return await this.engine.getERC20Balance(params); + } catch (error: any) { + this.logger.error(error.message); + throw error; + } + }; + + public getFulfilmentDetails: IWalletKit["getFulfilmentDetails"] = async (params) => { + try { + return await this.engine.getFulfilmentDetails(params); + } catch (error: any) { + this.logger.error(error.message); + throw error; + } + }; + // ---------- Private ----------------------------------------------- // private async initialize() { diff --git a/packages/walletkit/src/controllers/chainAbstraction.ts b/packages/walletkit/src/controllers/chainAbstraction.ts index cee4245..1167375 100644 --- a/packages/walletkit/src/controllers/chainAbstraction.ts +++ b/packages/walletkit/src/controllers/chainAbstraction.ts @@ -4,8 +4,12 @@ import { ChainAbstractionTypes, IChainAbstraction, IWalletKitEngine } from "../t import { FULFILMENT_STATUS, CAN_FULFIL_STATUS } from "../constants"; export class ChainAbstraction extends IChainAbstraction { - private canFulfilHandler: any; + private prepareFulfilmentHandler: any; private fulfilmentStatusHandler: any; + private estimateFeesHandler: any; + private getERC20BalanceHandler: any; + private getFulfilmentDetailsHandler: any; + private projectId: string; constructor(public engine: IWalletKitEngine) { @@ -14,19 +18,19 @@ export class ChainAbstraction extends IChainAbstraction { this.projectId = this.engine.client.core.projectId || ""; } - public canFulfil: IChainAbstraction["canFulfil"] = async (params) => { - console.log("canFulfil", params); - if (!this.canFulfilHandler) { - throw new Error(`canFulfilHandler not found for environment: '${getEnvironment()}'`); + public prepareFulfilment: IChainAbstraction["prepareFulfilment"] = async (params) => { + console.log("prepareFulfilment", params); + if (!this.prepareFulfilmentHandler) { + throw new Error(`prepareFulfilmentHandler not found for environment: '${getEnvironment()}'`); } const { transaction } = params; - const result = (await this.canFulfilHandler({ + const result = (await this.prepareFulfilmentHandler({ transaction, projectId: this.projectId, - })) as ChainAbstractionTypes.CanFulfilHandlerResult; - console.log("canFulfil processing result..", result); + })) as ChainAbstractionTypes.PrepareFulfilmentHandlerResult; + console.log("prepareFulfilment processing result..", result); switch (result.status) { case CAN_FULFIL_STATUS.error: return { status: CAN_FULFIL_STATUS.error, reason: result.reason }; @@ -34,26 +38,20 @@ export class ChainAbstraction extends IChainAbstraction { return { status: CAN_FULFIL_STATUS.not_required }; case CAN_FULFIL_STATUS.available: // eslint-disable-next-line no-case-declarations - const routes = result.data.routes; - // eslint-disable-next-line no-case-declarations - const routesDetails = result.data.routesDetails; + const routes = result.data; return { status: CAN_FULFIL_STATUS.available, data: { - routes: { - fulfilmentId: routes.orchestrationId, - checkIn: routes.checkIn, - transactions: routes.transactions, - funding: routes.metadata.fundingFrom, - initialTransaction: routes.initialTransaction, - }, - routesDetails: { - totalFees: routesDetails.localTotal, - }, + fulfilmentId: routes.orchestrationId, + checkIn: routes.checkIn, + transactions: routes.transactions, + funding: routes.metadata.fundingFrom, + initialTransaction: routes.initialTransaction, + initialTransactionMetadata: routes.metadata.initialTransaction, }, }; default: - throw new Error(`Invalid canFulfil status: ${JSON.stringify(result)}`); + throw new Error(`Invalid prepareFulfilment status: ${JSON.stringify(result)}`); } }; @@ -77,6 +75,92 @@ export class ChainAbstraction extends IChainAbstraction { return result; }; + /** + * TODO: pass projectId to yttrium handlers + */ + + public estimateFees: IChainAbstraction["estimateFees"] = async (params) => { + if (!this.estimateFeesHandler) { + throw new Error(`estimateFeesHandler not found for environment: '${getEnvironment()}'`); + } + const result = await this.estimateFeesHandler({ + ...params, + projectId: this.projectId, + }); + + console.log("estimateFees result", result); + return result; + }; + + public getERC20Balance: IChainAbstraction["getERC20Balance"] = async (params) => { + if (!this.getERC20BalanceHandler) { + throw new Error(`getERC20BalanceHandler not found for environment: '${getEnvironment()}'`); + } + const result = await this.getERC20BalanceHandler({ + ...params, + projectId: this.projectId, + }); + + console.log("getERC20Balance result", result); + return result; + }; + + public getFulfilmentDetails: IChainAbstraction["getFulfilmentDetails"] = async (params) => { + if (!this.getFulfilmentDetailsHandler) { + throw new Error( + `getFulfilmentDetailsHandler not found for environment: '${getEnvironment()}'`, + ); + } + const { fulfilmentId } = params; + const result = await this.getFulfilmentDetailsHandler({ + ...params, + orchestrationId: fulfilmentId, + projectId: this.projectId, + }); + + console.log("getFulfilmentDetails handler result", result); + const bridgeDetails: ChainAbstractionTypes.TransactionFee[] = []; + + for (const fees of result.bridge) { + bridgeDetails.push({ + fee: fees.fee, + localFee: fees.localFee, + }); + } + + const routeDetails: ChainAbstractionTypes.TransactionDetails[] = []; + + for (const transaction of result.route) { + routeDetails.push({ + transaction: transaction.transaction, + eip1559: transaction.estimate, + transactionFee: transaction.fee, + }); + } + + const initialTransactionDetails: ChainAbstractionTypes.TransactionDetails = { + transaction: result.initial.transaction, + eip1559: result.initial.estimate, + transactionFee: result.initial.fee, + }; + + const totalFee: ChainAbstractionTypes.TotalFee = result.localTotal; + + console.log("getFulfilmentDetails parsed result", { + routeDetails, + initialTransactionDetails, + bridgeDetails, + totalFee, + }); + + return { + routeDetails, + initialTransactionDetails, + bridgeDetails, + totalFee, + }; + }; + private loadHandlers = () => { const env = getEnvironment(); switch (env) { @@ -95,8 +179,16 @@ export class ChainAbstraction extends IChainAbstraction { console.warn("React Native Yttrium not found in global scope"); return; } - this.canFulfilHandler = yttrium.checkRoute; - this.fulfilmentStatusHandler = yttrium.checkStatus; + this.prepareFulfilmentHandler = async (params: any) => + this.parseResult(await yttrium.prepare(params)); + this.fulfilmentStatusHandler = async (params: any) => + this.parseResult(await yttrium.status(params)); + this.estimateFeesHandler = async (params: any) => + this.parseResult(await yttrium.estimateFees(params)); + this.getERC20BalanceHandler = async (params: any) => + this.parseResult(await yttrium.getERC20Balance(params)); + this.getFulfilmentDetailsHandler = async (params: any) => + this.parseResult(await yttrium.getBridgeDetails(params)); }; private Browser = () => { @@ -108,7 +200,28 @@ export class ChainAbstraction extends IChainAbstraction { if (!yttrium) { console.warn("Yttrium not available in node environment"); } - this.canFulfilHandler = yttrium.checkRoute; - this.fulfilmentStatusHandler = yttrium.checkStatus; + + this.prepareFulfilmentHandler = async (params: any) => + this.parseResult(await yttrium.prepare(params)); + this.fulfilmentStatusHandler = async (params: any) => + this.parseResult(await yttrium.status(params)); + this.estimateFeesHandler = async (params: any) => + this.parseResult(await yttrium.estimateFees(params)); + this.getERC20BalanceHandler = async (params: any) => + this.parseResult(await yttrium.getERC20Balance(params)); + this.getFulfilmentDetailsHandler = async (params: any) => + this.parseResult(await yttrium.getBridgeDetails(params)); + }; + + private parseResult = (result: any) => { + if (typeof result === "undefined") return; + + // iOS returns parsed JSON object, while Android returns stringified + if (typeof result === "string") { + try { + return JSON.parse(result); + } catch (e) {} + } + return result; }; } diff --git a/packages/walletkit/src/controllers/engine.ts b/packages/walletkit/src/controllers/engine.ts index 9f501e0..fe9d114 100644 --- a/packages/walletkit/src/controllers/engine.ts +++ b/packages/walletkit/src/controllers/engine.ts @@ -126,13 +126,26 @@ export class Engine extends IWalletKitEngine { }; // Chain Abstraction // - public canFulfil: IWalletKitEngine["canFulfil"] = async (params) => { - return await this.chainAbstraction.canFulfil(params); + public prepareFulfilment: IWalletKitEngine["prepareFulfilment"] = async (params) => { + return await this.chainAbstraction.prepareFulfilment(params); }; public fulfilmentStatus: IWalletKitEngine["fulfilmentStatus"] = async (params) => { return await this.chainAbstraction.fulfilmentStatus(params); }; + + public estimateFees: IWalletKitEngine["estimateFees"] = async (params) => { + return await this.chainAbstraction.estimateFees(params); + }; + + public getERC20Balance: IWalletKitEngine["getERC20Balance"] = async (params) => { + return await this.chainAbstraction.getERC20Balance(params); + }; + + public getFulfilmentDetails: IWalletKitEngine["getFulfilmentDetails"] = async (params) => { + return await this.chainAbstraction.getFulfilmentDetails(params); + }; + // ---------- Private ----------------------------------------------- // private onSessionRequest = (event: WalletKitTypes.SessionRequest) => { diff --git a/packages/walletkit/src/types/chainAbstraction.ts b/packages/walletkit/src/types/chainAbstraction.ts index 0967192..dc07e9b 100644 --- a/packages/walletkit/src/types/chainAbstraction.ts +++ b/packages/walletkit/src/types/chainAbstraction.ts @@ -1,17 +1,31 @@ import { IWalletKitEngine } from "./engine"; export declare namespace ChainAbstractionTypes { + type InitialTransaction = { + from: string; + to: string; + data: string; + chainId: string; + }; + type Transaction = { from: string; to: string; value: string; chainId: string; - gas?: string; - gasPrice?: string; - data?: string; - nonce?: string; - maxFeePerGas?: string; - maxPriorityFeePerGas?: string; + gasLimit: string; + input: string; + nonce: string; + maxFeePerGas: string; + maxPriorityFeePerGas: string; + }; + + type InitialTransactionMetadata = { + symbol: string; + amount: string; + decimals: number; + tokenContract: string; + transferTo: string; }; type FulfilmentStatusResponse = { @@ -33,29 +47,19 @@ export declare namespace ChainAbstractionTypes { | { status: "error"; reason: string } ); - type CanFulfilResponse = + type PrepareFulfilmentResponse = | { status: "not_required"; } | { status: "available"; data: { - routes: { - fulfilmentId: string; - checkIn: number; - transactions: ChainAbstractionTypes.Transaction[]; - funding: FundingFrom[]; - initialTransaction: ChainAbstractionTypes.Transaction; - }; - routesDetails: { - totalFees: { - amount: string; - formatted: string; - formattedAlt: string; - symbol: string; - unit: number; - }; - }; + fulfilmentId: string; + checkIn: number; + transactions: Transaction[]; + funding: FundingFrom[]; + initialTransaction: Transaction; + initialTransactionMetadata: InitialTransactionMetadata; }; } | { @@ -70,7 +74,7 @@ export declare namespace ChainAbstractionTypes { symbol: string; }; - type CanFulfilHandlerResult = + type PrepareFulfilmentHandlerResult = | { status: "error"; reason: string; @@ -81,35 +85,74 @@ export declare namespace ChainAbstractionTypes { | { status: "available"; data: { - routes: { - orchestrationId: string; - checkIn: number; - metadata: { - fundingFrom: FundingFrom[]; - }; - transactions: ChainAbstractionTypes.Transaction[]; - initialTransaction: ChainAbstractionTypes.Transaction; - }; - routesDetails: { - localTotal: { - amount: string; - formatted: string; - formattedAlt: string; - symbol: string; - unit: number; - }; + orchestrationId: string; + checkIn: number; + metadata: { + fundingFrom: FundingFrom[]; + initialTransaction: InitialTransactionMetadata; }; + transactions: Transaction[]; + initialTransaction: Transaction; }; }; + + type EstimateFeesResponse = { + maxFeePerGas: string; + maxPriorityFeePerGas: string; + }; + + type ERC20BalanceResponse = { + balance: string; + }; + + type TotalFee = { + symbol: string; + amount: string; + unit: number; + formatted: string; + formattedAlt: string; + }; + + type TransactionFee = { + fee: TotalFee; + localFee: TotalFee; + }; + + type TransactionDetails = { + transaction: Transaction; + eip1559: EstimateFeesResponse; + transactionFee: TransactionFee; + }; + + type FulfilmentDetailsResponse = { + routeDetails: TransactionDetails[]; + initialTransactionDetails: TransactionDetails; + bridgeDetails: TransactionFee[]; + totalFee: TotalFee; + }; } export abstract class IChainAbstraction { constructor(public engine: IWalletKitEngine) {} - public abstract canFulfil(params: { - transaction: ChainAbstractionTypes.Transaction; - }): Promise; + public abstract prepareFulfilment(params: { + transaction: ChainAbstractionTypes.InitialTransaction; + }): Promise; public abstract fulfilmentStatus(params: { fulfilmentId: string; }): Promise; + + public abstract estimateFees(params: { + chainId: string; + }): Promise; + + public abstract getERC20Balance(params: { + chainId: string; + tokenAddress: string; + ownerAddress: string; + }): Promise; + + public abstract getFulfilmentDetails(params: { + fulfilmentId: string; + }): Promise; } diff --git a/packages/walletkit/src/types/client.ts b/packages/walletkit/src/types/client.ts index 3a2dfaa..a68188a 100644 --- a/packages/walletkit/src/types/client.ts +++ b/packages/walletkit/src/types/client.ts @@ -130,8 +130,11 @@ export abstract class IWalletKit { public abstract rejectSessionAuthenticate: IWalletKitEngine["rejectSessionAuthenticate"]; // chain abstraction // - public abstract canFulfil: IWalletKitEngine["canFulfil"]; + public abstract prepareFulfilment: IWalletKitEngine["prepareFulfilment"]; public abstract fulfilmentStatus: IWalletKitEngine["fulfilmentStatus"]; + public abstract estimateFees: IWalletKitEngine["estimateFees"]; + public abstract getERC20Balance: IWalletKitEngine["getERC20Balance"]; + public abstract getFulfilmentDetails: IWalletKitEngine["getFulfilmentDetails"]; // ---------- Event Handlers ----------------------------------------------- // public abstract on: ( diff --git a/packages/walletkit/src/types/engine.ts b/packages/walletkit/src/types/engine.ts index 29bbe1c..9e7a361 100644 --- a/packages/walletkit/src/types/engine.ts +++ b/packages/walletkit/src/types/engine.ts @@ -100,10 +100,16 @@ export abstract class IWalletKitEngine { // ---------- Chain Abstraction ------------------------------------ // - public abstract canFulfil: IChainAbstraction["canFulfil"]; + public abstract prepareFulfilment: IChainAbstraction["prepareFulfilment"]; public abstract fulfilmentStatus: IChainAbstraction["fulfilmentStatus"]; + public abstract estimateFees: IChainAbstraction["estimateFees"]; + + public abstract getERC20Balance: IChainAbstraction["getERC20Balance"]; + + public abstract getFulfilmentDetails: IChainAbstraction["getFulfilmentDetails"]; + // ---------- Event Handlers ----------------------------------------------- // public abstract on: ( event: E,