@ckboost/client
Version:
TypeScript/JavaScript SDK for dApps to integrate CKBoost - Fast ck-token transactions on Internet Computer through liquidity pools
404 lines (396 loc) • 14.5 kB
JavaScript
;
var agent = require('@dfinity/agent');
var principal = require('@dfinity/principal');
// src/ck-testbtc-client.ts
// src/candid/ck-testbtc/backend.did.js
var idlFactory = ({ IDL }) => {
const BoostId = IDL.Nat;
const BoostStatus2 = IDL.Variant({
"active": IDL.Null,
"cancelled": IDL.Null,
"pending": IDL.Null,
"completed": IDL.Null
});
const Amount = IDL.Nat;
const Timestamp = IDL.Int;
const Subaccount = IDL.Vec(IDL.Nat8);
const BoostRequest2 = IDL.Record({
"id": BoostId,
"status": BoostStatus2,
"receivedBTC": Amount,
"confirmationsRequired": IDL.Nat,
"owner": IDL.Principal,
"maxFeePercentage": IDL.Float64,
"createdAt": Timestamp,
"subaccount": Subaccount,
"booster": IDL.Opt(IDL.Principal),
"updatedAt": Timestamp,
"btcAddress": IDL.Opt(IDL.Text),
"amount": Amount,
"preferredBooster": IDL.Opt(IDL.Principal)
});
const Result = IDL.Variant({ "ok": BoostRequest2, "err": IDL.Text });
const BoosterAccount = IDL.Record({
"availableBalance": Amount,
"owner": IDL.Principal,
"createdAt": Timestamp,
"subaccount": Subaccount,
"updatedAt": Timestamp,
"totalDeposited": Amount
});
const Result_2 = IDL.Variant({ "ok": IDL.Text, "err": IDL.Text });
const Result_1 = IDL.Variant({ "ok": BoosterAccount, "err": IDL.Text });
const Main = IDL.Service({
"acceptBoostRequest": IDL.Func([BoostId], [IDL.Text], []),
"checkBTCDeposit": IDL.Func([BoostId], [Result], []),
"getAllBoostRequests": IDL.Func([], [IDL.Vec(BoostRequest2)], ["query"]),
"getAllBoosterAccounts": IDL.Func(
[],
[IDL.Vec(BoosterAccount)],
["query"]
),
"getBoostRequest": IDL.Func([BoostId], [IDL.Opt(BoostRequest2)], ["query"]),
"getBoostRequestBTCAddress": IDL.Func([BoostId], [Result_2], []),
"getBoosterAccount": IDL.Func(
[IDL.Principal],
[IDL.Opt(BoosterAccount)],
["query"]
),
"getCanisterPrincipal": IDL.Func([], [IDL.Principal], ["query"]),
"getDirectBTCAddress": IDL.Func([], [IDL.Text], []),
"getPendingBoostRequests": IDL.Func(
[],
[IDL.Vec(BoostRequest2)],
["query"]
),
"getUserBoostRequests": IDL.Func(
[IDL.Principal],
[IDL.Vec(BoostRequest2)],
["query"]
),
"registerBoostRequest": IDL.Func(
[Amount, IDL.Float64, IDL.Nat, IDL.Opt(IDL.Principal)],
[Result],
[]
),
"registerBoosterAccount": IDL.Func([], [Result_1], []),
"updateBoosterDeposit": IDL.Func([IDL.Principal, Amount], [Result_1], []),
"updateReceivedBTC": IDL.Func([BoostId, Amount], [Result], []),
"withdrawBoosterFunds": IDL.Func([Amount], [IDL.Text], [])
});
return Main;
};
// src/candid/ck-testbtc/index.js
var canisterId = process.env.CANISTER_ID_BACKEND;
var createActor = (canisterId2, options = {}) => {
const agent$1 = options.agent || new agent.HttpAgent({ ...options.agentOptions });
if (options.agent && options.agentOptions) {
console.warn(
"Detected both agent and agentOptions passed to createActor. Ignoring agentOptions and proceeding with the provided agent."
);
}
if (process.env.DFX_NETWORK !== "ic") {
agent$1.fetchRootKey().catch((err) => {
console.warn(
"Unable to fetch root key. Check to ensure that your local replica is running"
);
console.error(err);
});
}
return agent.Actor.createActor(idlFactory, {
agent: agent$1,
canisterId: canisterId2,
...options.actorOptions
});
};
canisterId ? createActor(canisterId) : void 0;
// src/types.ts
var SupportedToken = /* @__PURE__ */ ((SupportedToken2) => {
SupportedToken2["ckTESTBTC"] = "ckTESTBTC";
return SupportedToken2;
})(SupportedToken || {});
var TOKEN_CONFIGS = {
["ckTESTBTC" /* ckTESTBTC */]: {
token: "ckTESTBTC" /* ckTESTBTC */,
backendCanisterId: "75egi-7qaaa-aaaao-qj6ma-cai",
ledgerCanisterId: "mc6ru-gyaaa-aaaar-qaaaq-cai",
minimumAmount: "0.005",
maximumAmount: "1.0",
standardFee: "0.00001",
confirmationsRequired: 2,
blockExplorerUrl: "https://mempool.space/testnet4",
decimals: 8,
isTestnet: true
}
};
var BoostStatus = /* @__PURE__ */ ((BoostStatus2) => {
BoostStatus2["PENDING"] = "pending";
BoostStatus2["ACTIVE"] = "active";
BoostStatus2["COMPLETED"] = "completed";
BoostStatus2["CANCELLED"] = "cancelled";
return BoostStatus2;
})(BoostStatus || {});
var CKBoostErrorType = /* @__PURE__ */ ((CKBoostErrorType2) => {
CKBoostErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
CKBoostErrorType2["INVALID_AMOUNT"] = "INVALID_AMOUNT";
CKBoostErrorType2["INVALID_TOKEN"] = "INVALID_TOKEN";
CKBoostErrorType2["REQUEST_NOT_FOUND"] = "REQUEST_NOT_FOUND";
CKBoostErrorType2["REQUEST_EXPIRED"] = "REQUEST_EXPIRED";
CKBoostErrorType2["CANISTER_ERROR"] = "CANISTER_ERROR";
CKBoostErrorType2["VALIDATION_ERROR"] = "VALIDATION_ERROR";
return CKBoostErrorType2;
})(CKBoostErrorType || {});
var CKBoostError = class extends Error {
constructor(type, message, details) {
super(message);
this.type = type;
this.details = details;
this.name = "CKBoostError";
}
};
// src/ck-testbtc-client.ts
var ckTESTBTCClient = class {
constructor(config = {}) {
this.tokenConfig = TOKEN_CONFIGS["ckTESTBTC" /* ckTESTBTC */];
this.config = {
host: config.host || "https://icp-api.io",
timeout: config.timeout || 3e4
};
this.agent = new agent.HttpAgent({
host: this.config.host
});
this.backend = createActor(this.tokenConfig.backendCanisterId, {
agent: this.agent
});
if (this.tokenConfig.isTestnet || this.config.host.includes("localhost")) {
this.agent.fetchRootKey().catch((err) => {
console.warn("Failed to fetch root key:", err);
});
}
}
/**
* Generate a deposit address for a ckTESTBTC boost request
*
* This function:
* 1. Validates the input parameters
* 2. Calls registerBoostRequest on the backend
* 3. Gets the deposit address for the request
* 4. Returns formatted deposit information
*/
async generateDepositAddress(params) {
try {
const amount = parseFloat(params.amount);
const minAmount = parseFloat(this.tokenConfig.minimumAmount);
const maxAmount = parseFloat(this.tokenConfig.maximumAmount);
if (amount < minAmount) {
throw new CKBoostError(
"INVALID_AMOUNT" /* INVALID_AMOUNT */,
`Amount ${params.amount} is below minimum ${this.tokenConfig.minimumAmount}`
);
}
if (amount > maxAmount) {
throw new CKBoostError(
"INVALID_AMOUNT" /* INVALID_AMOUNT */,
`Amount ${params.amount} exceeds maximum ${this.tokenConfig.maximumAmount}`
);
}
if (params.maxFeePercentage < 0.1 || params.maxFeePercentage > 2) {
throw new CKBoostError(
"VALIDATION_ERROR" /* VALIDATION_ERROR */,
"Fee percentage must be between 0.1% and 2.0%"
);
}
const amountRaw = BigInt(Math.floor(amount * Math.pow(10, this.tokenConfig.decimals)));
const confirmationsRequired = BigInt(params.confirmationsRequired || this.tokenConfig.confirmationsRequired);
const preferredBooster = params.preferredBooster ? [principal.Principal.fromText(params.preferredBooster)] : [];
const registerResult = await this.backend.registerBoostRequest(
amountRaw,
params.maxFeePercentage,
confirmationsRequired,
preferredBooster
);
if ("err" in registerResult) {
throw new CKBoostError(
"CANISTER_ERROR" /* CANISTER_ERROR */,
`Failed to register boost request: ${registerResult.err}`
);
}
const boostRequest = registerResult.ok;
const requestId = boostRequest.id.toString();
const addressResult = await this.backend.getBoostRequestBTCAddress(boostRequest.id);
if ("err" in addressResult) {
throw new CKBoostError(
"CANISTER_ERROR" /* CANISTER_ERROR */,
`Failed to get deposit address: ${addressResult.err}`
);
}
const depositAddressValue = addressResult.ok;
const depositAddress = {
requestId,
address: depositAddressValue,
amount: params.amount,
amountRaw: amountRaw.toString(),
maxFeePercentage: params.maxFeePercentage,
confirmationsRequired: Number(confirmationsRequired),
explorerUrl: `${this.tokenConfig.blockExplorerUrl}/address/${depositAddressValue}`
};
return {
success: true,
data: depositAddress
};
} catch (error) {
if (error instanceof CKBoostError) {
return {
success: false,
error: {
type: error.type,
message: error.message,
details: error.details
}
};
}
return {
success: false,
error: {
type: "NETWORK_ERROR" /* NETWORK_ERROR */,
message: error instanceof Error ? error.message : "Unknown error occurred",
details: error
}
};
}
}
/**
* Get detailed information about a ckTESTBTC boost request
*
* @param requestId - The boost request ID (as string)
* @returns Detailed boost request information
*/
async getBoostRequest(requestId) {
try {
const boostId = BigInt(requestId);
const result = await this.backend.getBoostRequest(boostId);
if (result.length === 0) {
throw new CKBoostError(
"REQUEST_NOT_FOUND" /* REQUEST_NOT_FOUND */,
`Boost request with ID ${requestId} not found`
);
}
const backendRequest = result[0];
const boostRequest = {
id: backendRequest.id.toString(),
status: this.convertBoostStatus(backendRequest.status),
amount: this.rawToTokenAmount(backendRequest.amount),
amountRaw: backendRequest.amount.toString(),
maxFeePercentage: backendRequest.maxFeePercentage,
owner: backendRequest.owner.toString(),
booster: backendRequest.booster.length > 0 ? backendRequest.booster[0]?.toString() : void 0,
preferredBooster: backendRequest.preferredBooster.length > 0 ? backendRequest.preferredBooster[0]?.toString() : void 0,
depositAddress: backendRequest.btcAddress.length > 0 ? backendRequest.btcAddress[0] : void 0,
receivedAmount: this.rawToTokenAmount(backendRequest.receivedBTC),
confirmationsRequired: Number(backendRequest.confirmationsRequired),
createdAt: Number(backendRequest.createdAt),
updatedAt: Number(backendRequest.updatedAt),
explorerUrl: backendRequest.btcAddress.length > 0 && backendRequest.btcAddress[0] ? `${this.tokenConfig.blockExplorerUrl}/address/${backendRequest.btcAddress[0]}` : this.tokenConfig.blockExplorerUrl
};
return {
success: true,
data: boostRequest
};
} catch (error) {
if (error instanceof CKBoostError) {
return {
success: false,
error: {
type: error.type,
message: error.message,
details: error.details
}
};
}
return {
success: false,
error: {
type: "NETWORK_ERROR" /* NETWORK_ERROR */,
message: error instanceof Error ? error.message : "Unknown error occurred",
details: error
}
};
}
}
/**
* Convert backend BoostStatus variant to our enum
*/
convertBoostStatus(status) {
if ("pending" in status) return "pending" /* PENDING */;
if ("active" in status) return "active" /* ACTIVE */;
if ("completed" in status) return "completed" /* COMPLETED */;
if ("cancelled" in status) return "cancelled" /* CANCELLED */;
return "pending" /* PENDING */;
}
/**
* Convert raw amount (bigint) to token amount string
*/
rawToTokenAmount(rawAmount) {
const amount = Number(rawAmount) / Math.pow(10, this.tokenConfig.decimals);
return amount.toFixed(this.tokenConfig.decimals);
}
/**
* Utility method to get all pending ckTESTBTC boost requests
*/
async getPendingBoostRequests() {
try {
const result = await this.backend.getPendingBoostRequests();
const requests = result.map((backendRequest) => ({
id: backendRequest.id.toString(),
status: this.convertBoostStatus(backendRequest.status),
amount: this.rawToTokenAmount(backendRequest.amount),
amountRaw: backendRequest.amount.toString(),
maxFeePercentage: backendRequest.maxFeePercentage,
owner: backendRequest.owner.toString(),
booster: backendRequest.booster.length > 0 ? backendRequest.booster[0]?.toString() : void 0,
preferredBooster: backendRequest.preferredBooster.length > 0 ? backendRequest.preferredBooster[0]?.toString() : void 0,
depositAddress: backendRequest.btcAddress.length > 0 ? backendRequest.btcAddress[0] : void 0,
receivedAmount: this.rawToTokenAmount(backendRequest.receivedBTC),
confirmationsRequired: Number(backendRequest.confirmationsRequired),
createdAt: Number(backendRequest.createdAt) / 1e6,
// Convert nanoseconds to milliseconds
updatedAt: Number(backendRequest.updatedAt) / 1e6,
// Convert nanoseconds to milliseconds
explorerUrl: backendRequest.btcAddress.length > 0 && backendRequest.btcAddress[0] ? `${this.tokenConfig.blockExplorerUrl}/address/${backendRequest.btcAddress[0]}` : this.tokenConfig.blockExplorerUrl
}));
return {
success: true,
data: requests
};
} catch (error) {
return {
success: false,
error: {
type: "NETWORK_ERROR" /* NETWORK_ERROR */,
message: error instanceof Error ? error.message : "Unknown error occurred",
details: error
}
};
}
}
/**
* Get the ckTESTBTC token configuration
*/
getTokenConfig() {
return this.tokenConfig;
}
};
// src/index.ts
var ckTESTBTC_CANISTER_IDS = {
CKBOOST_BACKEND: "75egi-7qaaa-aaaao-qj6ma-cai",
CKTESTBTC_LEDGER: "mc6ru-gyaaa-aaaar-qaaaq-cai"
};
exports.BoostStatus = BoostStatus;
exports.CKBoostError = CKBoostError;
exports.CKBoostErrorType = CKBoostErrorType;
exports.SupportedToken = SupportedToken;
exports.TOKEN_CONFIGS = TOKEN_CONFIGS;
exports.ckTESTBTCClient = ckTESTBTCClient;
exports.ckTESTBTC_CANISTER_IDS = ckTESTBTC_CANISTER_IDS;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map