UNPKG

onesec-bridge

Version:
378 lines (377 loc) 11.2 kB
import { Principal } from '@dfinity/principal'; /** * Specifies a supported EVM chain. */ export type EvmChain = "Base" | "Arbitrum" | "Ethereum"; /** * Specifies a supported chain. */ export type Chain = "ICP" | EvmChain; /** * Specifies a supported token. */ export type Token = "ICP" | "USDC" | "USDT" | "cbBTC" | "ckBTC" | "BOB" | "GLDT"; /** * Specifies the network where the bridging smart contract is deployed. */ export type Deployment = "Mainnet" | "Testnet" | "Local"; /** * Specifies how a token operates on EVM chains. * - "minter": Token contract can mint/burn tokens (native tokens like ICP) * - "locker": Tokens are locked/unlocked in a vault contract (wrapped tokens like USDC) */ export type OperatingMode = "minter" | "locker"; /** * An EVM transaction hash with an optional log index. */ export interface EvmTx { hash: string; logIndex?: bigint; } /** * An ICRC2 ledger transaction with the block index and the ledger canister id. */ export interface IcpTx { blockIndex: bigint; ledger: Principal; } /** * A general transaction type that can be either an EVM or an ICP transaction. */ export type Tx = { Evm: EvmTx; } | { Icp: IcpTx; }; /** * The unique identifier of an accepted transfer. */ export interface TransferId { id: bigint; } /** * An error message. */ export interface ErrorMessage { error: string; } /** * The status of the forwarding operation: * - `CheckingBalance` means that the forwarding request has been received and * the balance of the forwarding address is being checked. * - `LowBalance` means that the forwarding address does not have enough tokens * to start bridging. * - `Forwarding` means that the forwarding address has enough tokens and the * actual forwarding has started. * - `Forwarded` means that the bridging helper contract has been invoked in the * given transaction. Once the main contract accepts that transaction, this * forwarding operation completes and the status moves back to `CheckingBalance` * for the next forwarding operation. The `done` field of `ForwardingResponse` * will contain the transfer id of the transfer. */ export type ForwardingStatus = { CheckingBalance: null; } | { LowBalance: { balance: bigint; minAmount: bigint; }; } | { Forwarding: null; } | { Forwarded: EvmTx; }; /** * A result of the `forward_evm_to_icp()` method */ export interface ForwardingResponse { /** * The result of the latest completed forwarding operation. * It is the id of the bridging transfer that was initiated by the forwarding * operation. The details of the transfer can be fetched using the * `getTransfer()` method. */ done?: TransferId; /** * The status of the currently pending forwarding operation. * Note that when the current forwarding operation completes and stores the * new transfer id in the `done` field, then the status moves back to * `CheckingBalance` for the next forwarding operation. */ status?: ForwardingStatus; } /** * An ICRC2 address. */ export interface IcrcAccount { owner: Principal; subaccount?: Uint8Array; } /** * An ICP ledger account id. */ export interface IcpAccountId { accountId: string; } /** * A general ICP address that can be either an ICRC2 address or an account-id. */ export type IcpAccount = { ICRC: IcrcAccount; } | { AccountId: IcpAccountId; }; /** * An EVM address. */ export interface EvmAccount { address: string; } /** * A general address that can be either an EVM or an ICP address. */ export type Account = { Evm: EvmAccount; } | { Icp: IcpAccount; }; /** * Details about bridged tokens. */ export interface AssetInfo { chain?: Chain; token?: Token; account?: Account; amount: bigint; tx?: Tx; } /** * The status of a bridging transfer */ export type Status = { PendingSourceTx: null; } | { PendingDestinationTx: null; } | { PendingRefundTx: null; } | { Failed: ErrorMessage; } | { Refunded: { tx?: Tx; }; } | { Succeeded: null; }; /** * Details of a bridging transfer. */ export interface Transfer { source: AssetInfo; destination: AssetInfo; status?: Status; } /** * The response from a transfer operation. */ export type TransferResponse = { Failed: ErrorMessage; } | { Accepted: TransferId; } | { Fetching: { blockHeight: bigint; }; }; /** * A helper for bridging using forwarding addresses. */ export interface OneSecForwarding { /** * Computes the EVM forwarding address that can be used for bridging tokens * to the given ICP address. * @param receiver - the ICP address for receiving tokens on the ICP chain. * @returns the EVM forwarding address. */ addressFor: (receiver: IcrcAccount) => Promise<string>; /** * Checks the current status of a potential forwarding request. * * This function should be called **after** `getForwardingStatus`. * It does **not** initiate a new forwarding request; it only inspects the current status. * * @param token - the token that is bridged. * @param chain - the source EVM chain. * @param forwardingAddress - the EVM forwarding address. * @param receiver - the ICP receiving address. * */ getForwardingStatus: (token: Token, chain: EvmChain, forwardingAddress: string, receiver: IcrcAccount) => Promise<ForwardingResponse>; /** * Initiates bridging of tokens from the forwarding address on the EVM chain * to the receiving address on the ICP chain. * * The function should be called after the user transfers tokens to the * forwarding address. * * @param token - the token that is bridged. * @param chain - the source EVM chain. * @param forwardingAddress - the EVM forwarding address. * @param receiver - the ICP receiving address. */ forwardEvmToIcp: (token: Token, chain: EvmChain, forwardingAddress: string, receiver: IcrcAccount) => Promise<ForwardingResponse>; /** * Fetches details of a bridging transfer by its id. * * @param transferId - the id of the transfer. */ getTransfer: (transferId: TransferId) => Promise<Transfer>; } /** * The response of a get transfer fee query. */ export interface TransferFee { token: Token; sourceChain: Chain; destinationChain: Chain; minAmount: bigint; maxAmount: bigint; available?: bigint; latestTransferFee: bigint; averageTransferFee: bigint; protocolFeeInPercent: number; } /** * Represents a token amount in both human-readable and contract-friendly formats. */ export interface Amount { /** Amount in human-readable token units (e.g., 1.5 for 1.5 USDC) */ inTokens: number; /** Amount in the token's smallest units (e.g., 1500000 for 1.5 USDC with 6 decimals) */ inUnits: bigint; } /** * Descriptive information about a step or operation. */ export interface About { /** Short, one-line description */ concise: string; /** Detailed description with context */ verbose: string; } /** * Interface for calculating various fees associated with a bridging operation. */ export interface ExpectedFee { /** Get the base transfer fee */ transferFee: () => Amount; /** Calculate protocol fee for a given amount */ protocolFee: (amount: Amount) => Amount; /** Get protocol fee as a percentage (e.g., 0.001 for 0.1%) */ protocolFeeInPercent: () => number; /** Calculate total fee (transfer + protocol) for a given amount */ totalFee: (amount: Amount) => Amount; } /** * The execution state of a bridging step. * - "planned": Step has not been executed yet * - "running": Step is currently executing * - "succeeded": Step completed successfully * - "failed": Step failed and cannot proceed * - "refunded": Step was refunded due to an issue */ export type StepState = "planned" | "running" | "succeeded" | "failed" | "refunded"; /** * The current status and results of a step execution. */ export interface StepStatus { /** Current execution state */ state: StepState; /** Short status message */ concise: string; /** Detailed status message */ verbose: string; /** Token amount involved in this step (if applicable) */ amount?: Amount; /** Expected fees for this operation (if applicable) */ expectedFee?: ExpectedFee; /** Transaction details if this step submitted a transaction */ transaction?: Tx; /** Transfer ID if this step initiated a transfer */ transferId?: TransferId; /** Generated forwarding address (for forwarding steps) */ forwardingAddress?: string; /** Error details if the step failed */ error?: Error; } /** * A single step in a token bridging process. * * Each step represents one operation (like transaction submission, * approval, or status checking) with its own execution state and progress tracking. * Steps are designed to be retryable and provide detailed status information. * * @example * ```typescript * // Get step information before execution * const step = plan.nextStepToRun(); * console.log(step.about().verbose); // "Submit transaction on Base" * * // Execute the step with error handling * try { * const result = await step.run(); * * // Check the result * if (result.state === "succeeded") { * console.log("Step completed:", result.verbose); * if (result.transaction) { * console.log("Transaction:", result.transaction); * } * } else if (result.state === "failed") { * console.error("Step failed:", result.error); * } * } catch (error) { * // Maybe retry the step. * console.error("Unexpected error during step execution:", error); * } * ``` */ export interface Step { /** * Get descriptive information about what this step does. * @returns Concise and verbose descriptions of the step's purpose */ about: () => About; /** * Get the step's position in the execution plan. * @returns Zero-based index of this step */ index: () => number; /** * Get the current execution status of this step. * @returns Current state, progress messages, and any results */ status: () => StepStatus; /** * @returns `true` if it is this step's turn to run and if its state allows * running. Otherwise returns `false`. */ canRun: () => boolean; /** * Execute this step's operation. * * This method is idempotent - calling it multiple times will not * cause duplicate operations. Failed steps can be retried by calling run() again. * * If `canRun()` is `false`, then calling this function does nothing. * * @returns Promise resolving to the step's final status * @throws Error if the step encounters an unexpected error during execution */ run: () => Promise<StepStatus>; /** * Get the expected duration for this step to complete. * @returns Expected duration in milliseconds */ expectedDurationMs: () => number; }