onesec-bridge
Version:
A library for interacting with the onesec.to bridge
378 lines (377 loc) • 11.2 kB
TypeScript
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;
}