From 259022ddac28ce6748770d690971e48b71841b42 Mon Sep 17 00:00:00 2001 From: DenysKarmazynDFINITY Date: Fri, 20 Dec 2024 12:16:19 +0100 Subject: [PATCH] feat(frontend): kong_backend integration --- dfx.json | 20 + scripts/build.kong_backend.sh | 58 ++ scripts/deploy.kong_backend.sh | 5 + scripts/deploy.sh | 2 + scripts/did.delete.types.mjs | 4 +- scripts/generate.sh | 2 + src/declarations/kong_backend/index.d.ts | 45 ++ .../kong_backend/kong_backend.did | 626 ++++++++++++++++++ .../kong_backend/kong_backend.did.d.ts | 452 +++++++++++++ .../kong_backend.factory.certified.did.js | 503 ++++++++++++++ .../kong_backend/kong_backend.factory.did.js | 510 ++++++++++++++ src/frontend/src/lib/api/kong_backend.api.ts | 52 ++ .../lib/canisters/kong_backend.canister.ts | 81 +++ .../src/lib/canisters/kong_backend.errors.ts | 11 + .../src/lib/constants/app.constants.ts | 6 + src/frontend/src/lib/types/api.ts | 21 +- .../canisters/kong_backend.canister.spec.ts | 235 +++++++ 17 files changed, 2631 insertions(+), 2 deletions(-) create mode 100755 scripts/build.kong_backend.sh create mode 100755 scripts/deploy.kong_backend.sh create mode 100644 src/declarations/kong_backend/index.d.ts create mode 100644 src/declarations/kong_backend/kong_backend.did create mode 100644 src/declarations/kong_backend/kong_backend.did.d.ts create mode 100644 src/declarations/kong_backend/kong_backend.factory.certified.did.js create mode 100644 src/declarations/kong_backend/kong_backend.factory.did.js create mode 100644 src/frontend/src/lib/api/kong_backend.api.ts create mode 100644 src/frontend/src/lib/canisters/kong_backend.canister.ts create mode 100644 src/frontend/src/lib/canisters/kong_backend.errors.ts create mode 100644 src/frontend/src/tests/lib/canisters/kong_backend.canister.spec.ts diff --git a/dfx.json b/dfx.json index 68ac79de67..cf88adcc50 100644 --- a/dfx.json +++ b/dfx.json @@ -22,6 +22,26 @@ } } }, + "kong_backend": { + "type": "custom", + "build": "scripts/build.kong_backend.sh", + "candid": "target/kong_backend.did", + "wasm": "target/kong_backend.wasm.gz", + "shrink": false, + "specified_id": "l4lgk-raaaa-aaaar-qahpq-cai", + "remote": { + "id": { + "ic": "2ipq2-uqaaa-aaaar-qailq-cai", + "beta": "2ipq2-uqaaa-aaaar-qailq-cai", + "test_be_1": "l4lgk-raaaa-aaaar-qahpq-cai", + "test_fe_1": "l4lgk-raaaa-aaaar-qahpq-cai", + "test_fe_2": "l4lgk-raaaa-aaaar-qahpq-cai", + "test_fe_3": "l4lgk-raaaa-aaaar-qahpq-cai", + "test_fe_4": "l4lgk-raaaa-aaaar-qahpq-cai", + "staging": "l4lgk-raaaa-aaaar-qahpq-cai" + } + } + }, "backend": { "candid": "src/backend/backend.did", "package": "backend", diff --git a/scripts/build.kong_backend.sh b/scripts/build.kong_backend.sh new file mode 100755 index 0000000000..2c7ea75507 --- /dev/null +++ b/scripts/build.kong_backend.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +print_help() { + cat <<-EOF + Creates the Kong Backend installation files: + + - The Wasm and Candid files are downloaded. + + The files are installed at at the locations defined for 'kong_backend' in 'dfx.json'. + EOF +} + +[[ "${1:-}" != "--help" ]] || { + print_help + exit 0 +} + +KONG_REPO_URL="https://raw.githubusercontent.com/KongSwap/kong/refs/heads/main/canisters" +CANDID_URL="${KONG_REPO_URL}/kong_backend.did" +WASM_URL="${KONG_REPO_URL}/kong_backend.wasm.gz" + +CANDID_FILE="$(jq -r .canisters.kong_backend.candid dfx.json)" +WASM_FILE="$(jq -r .canisters.kong_backend.wasm dfx.json)" + +download() { + : 'Downloads a URL to a given file.' + : '* With argument x, the filename is $X_FILE and the URL is $X_URL' + : '* If the file already exists, the user is prompted whether to overwrite, keeping the existing file by default.' + local asset asset_url asset_file response + asset="$1" + asset_url="${asset^^}_URL" + asset_file="${asset^^}_FILE" + : 'If the asset file already exists, ask the user whether to overwrite it.' + if test -e "${!asset_file}" && read -r -p "Overwrite existing ${!asset_file}? [y/N] " response && [[ "${response,,}" != y* ]]; then + echo "Using existing kong $asset file." + else + echo Downloading ${!asset_url} "-->" ${!asset_file} + mkdir -p "$(dirname "${!asset_file}")" + curl -sSL "${!asset_url}" >"${!asset_file}" + fi +} + +#### +# Downloads the candid file, if it does not exist already. +download candid + +#### +# Downloads the Wasm file, if it does not exist already. +download wasm + +#### +# Success +cat < { }; const promises = Object.keys(canisters) - .filter((canister) => !['backend', 'frontend', 'signer', 'rewards'].includes(canister)) + .filter( + (canister) => !['backend', 'frontend', 'signer', 'rewards', 'kong_backend'].includes(canister) + ) .map(deleteFolder); await Promise.allSettled(promises); diff --git a/scripts/generate.sh b/scripts/generate.sh index d9d8b20de9..621d20415b 100755 --- a/scripts/generate.sh +++ b/scripts/generate.sh @@ -47,6 +47,8 @@ function install_did_files() { ./scripts/did.sh # .. downloads candid for the signer DFX_NETWORK=ic ./scripts/build.signer.sh +# .. downloads candid for the kong_backend +DFX_NETWORK=local ./scripts/build.kong_backend.sh # Download .did files listed in dfx.json install_did_files # Generate Rust bindings diff --git a/src/declarations/kong_backend/index.d.ts b/src/declarations/kong_backend/index.d.ts new file mode 100644 index 0000000000..f8917f5958 --- /dev/null +++ b/src/declarations/kong_backend/index.d.ts @@ -0,0 +1,45 @@ +import type { ActorConfig, ActorSubclass, Agent, HttpAgentOptions } from '@dfinity/agent'; +import type { IDL } from '@dfinity/candid'; +import type { Principal } from '@dfinity/principal'; + +import { _SERVICE } from './kong_backend.did'; + +export declare const idlFactory: IDL.InterfaceFactory; +export declare const canisterId: string; + +export declare interface CreateActorOptions { + /** + * @see {@link Agent} + */ + agent?: Agent; + /** + * @see {@link HttpAgentOptions} + */ + agentOptions?: HttpAgentOptions; + /** + * @see {@link ActorConfig} + */ + actorOptions?: ActorConfig; +} + +/** + * Intializes an {@link ActorSubclass}, configured with the provided SERVICE interface of a canister. + * @constructs {@link ActorSubClass} + * @param {string | Principal} canisterId - ID of the canister the {@link Actor} will talk to + * @param {CreateActorOptions} options - see {@link CreateActorOptions} + * @param {CreateActorOptions["agent"]} options.agent - a pre-configured agent you'd like to use. Supercedes agentOptions + * @param {CreateActorOptions["agentOptions"]} options.agentOptions - options to set up a new agent + * @see {@link HttpAgentOptions} + * @param {CreateActorOptions["actorOptions"]} options.actorOptions - options for the Actor + * @see {@link ActorConfig} + */ +export declare const createActor: ( + canisterId: string | Principal, + options?: CreateActorOptions +) => ActorSubclass<_SERVICE>; + +/** + * Intialized Actor using default settings, ready to talk to a canister using its candid interface + * @constructs {@link ActorSubClass} + */ +export declare const kong_backend: ActorSubclass<_SERVICE>; diff --git a/src/declarations/kong_backend/kong_backend.did b/src/declarations/kong_backend/kong_backend.did new file mode 100644 index 0000000000..d7b8efc1e3 --- /dev/null +++ b/src/declarations/kong_backend/kong_backend.did @@ -0,0 +1,626 @@ +type Icrc10SupportedStandards = record { + url : text; + name : text; +}; + +// Canister interface specification for ICRC-21. +// See https://github.com/dfinity/wg-identity-authentication/blob/main/topics/ICRC-21/icrc_21_consent_msg.md + +type icrc21_consent_message_metadata = record { + // BCP-47 language tag. See https://www.rfc-editor.org/rfc/bcp/bcp47.txt + language: text; + + // The users local timezone offset in minutes from UTC. + // Applicable when converting timestamps to human-readable format. + // + // If absent in the request, the canister should fallback to the UTC timezone when creating the consent message. + // If absent in the response, the canister is indicating that the consent message is not timezone sensitive. + utc_offset_minutes: opt int16; +}; + +type icrc21_consent_message_spec = record { + // Metadata of the consent message. + metadata: icrc21_consent_message_metadata; + + // Information about the device responsible for presenting the consent message to the user. + // If absent in the request, the canister should fallback to one of the values defined in this spec (ICRC-21). + device_spec: opt variant { + // A generic display able to handle large documents and do line wrapping and pagination / scrolling. + // Text must be Markdown formatted, no external resources (e.g. images) are allowed. + GenericDisplay; + // Simple display able to handle lines of text with a maximum number of characters per line. + // Multiple pages can be used if the text does no fit on a single page. + // Text must be plain text without any embedded formatting elements. + LineDisplay: record { + // Maximum number of characters that can be displayed per line. + characters_per_line: nat16; + // Maximum number of lines that can be displayed at once on a single page. + lines_per_page: nat16; + }; + }; +}; + +type icrc21_consent_message_request = record { + // Method name of the canister call. + method: text; + // Argument of the canister call. + arg: blob; + // User preferences with regards to the consent message presented to the end-user. + user_preferences: icrc21_consent_message_spec; +}; + +type icrc21_consent_message = variant { + // Message for a generic display able to handle large documents and do proper line wrapping and pagination / scrolling. + // Uses Markdown formatting, no external resources (e.g. images) are allowed. + GenericDisplayMessage: text; + // Message for a simple display able to handle pages with multiple lines of text with a fixed maximum number of + // characters per line. + // Multiple pages can be used if the text does no fit on a single page. + // Uses plain text, without any embedded formatting elements. + LineDisplayMessage: record { + pages: vec record { + // Lines of text to be displayed on a single page. + // Must not have more entries (lines) than specified in the icrc21_consent_message_spec. + // Lines must not exceed the number of characters per line specified in the icrc21_consent_message_spec. + lines: vec text; + }; + }; +}; + +type icrc21_consent_info = record { + // Consent message describing in a human-readable format what the call will do. + // + // The message should adhere as close as possible to the user_preferences specified in the consent_message_spec + // of the icrc21_consent_message_request. + // If the message is not available for the given user_preferences any fallback message should be used. Providing a + // message should be preferred over sending an icrc21_error. + // The metadata must match the consent_message provided. + // + // The message should be short and concise. + // It should only contain information that is: + // * relevant to the user + // * relevant given the canister call argument + // + // The message must fit the following context shown to + // the user on the signer UI: + // ┌─────────────────────────────────┐ + // │ Approve the following action? │ + // │ ┌───────────────────────────┐ │ + // │ │ │ │ + // │ └───────────────────────────┘ │ + // │ ┌───────────┐ ┌───────────┐ │ + // │ │ Reject │ │ Approve │ │ + // │ └───────────┘ └───────────┘ │ + // └─────────────────────────────────┘ + consent_message: icrc21_consent_message; + // Metadata of the consent_message. + metadata: icrc21_consent_message_metadata; +}; + +type icrc21_error_info = record { + // Human readable technical description of the error intended for developers, not the end-user. + description: text; +}; + +type icrc21_error = variant { + // The canister does not support this call (i.e. it will lead to a rejection or error response). + // Reasons might be (non-exhaustive list): + // * the canister call is malformed (e.g. wrong method name, argument cannot be decoded) + // * the arguments exceed certain bounds + // + // The developer should provide more information about the error using the description in icrc21_error_info. + UnsupportedCanisterCall: icrc21_error_info; + + // The canister cannot produce a consent message for this call. + // Reasons might be (non-exhaustive list): + // * it is an internal call not intended for end-users + // * the canister developer has not yet implemented a consent message for this call + // + // The developer should provide more information about the error using the description in icrc21_error_info. + ConsentMessageUnavailable: icrc21_error_info; + + // The canister did not provide a consent message for because payment was missing or insufficient. + // + // This error is used to account for payment extensions to be added in the future: + // While small consent messages are easy and cheap to provide, this might not generally be the case for all consent + // messages. To avoid future breaking changes, when introducing a payment flow, this error is already introduced + // even though there no standardized payment flow yet. + InsufficientPayment: icrc21_error_info; + + // Any error not covered by the above variants. + GenericError: record { + // Machine parsable error. Can be chosen by the target canister but should indicate the error category. + error_code: nat; + // Human readable technical description of the error intended for developers, not the end-user. + description: text; + }; +}; + +type icrc21_consent_message_response = variant { + // The call is ok, consent message is provided. + Ok: icrc21_consent_info; + // The call is not ok, error is provided. + Err: icrc21_error; +}; + +type Icrc28TrustedOriginsResponse = record { trusted_origins : vec text }; + +type TxId = variant { + BlockIndex : nat; + TransactionId : text; +}; + +type ICTransferReply = record { + chain : text; + symbol : text; + is_send : bool; + amount : nat; + canister_id : text; + block_index : nat; +}; +type TransferReply = variant { + IC : ICTransferReply; +}; +type TransferIdReply = record { + transfer_id : nat64; + transfer : TransferReply +}; + +type UserReply = record { + user_id : nat32; + principal_id : text; + account_id : text; + user_name : text; + my_referral_code : text; + referred_by : opt text; + referred_by_expires_at : opt nat64; + fee_level : nat8; + fee_level_expires_at : opt nat64; + campaign1_flags : vec bool; +}; +type UserResult = variant { Ok : UserReply; Err : text }; + +type UserBalancesReply = variant { + LP : BalancesReply; +}; +type BalancesReply = record { + name : text; + symbol : text; + balance : float64; + usd_balance : float64; + symbol_0 : text; + amount_0 : float64; + usd_amount_0 : float64; + symbol_1 : text; + amount_1 : float64; + usd_amount_1 : float64; + ts : nat64; +}; +type UserBalancesResult = variant { Ok : vec UserBalancesReply; Err : text }; + +type MessagesReply = record { + message_id : nat64; + title : text; + message : text; + ts : nat64; +}; +type MessagesResult = variant { Ok : vec MessagesReply; Err : text }; + +type TokenReply = variant { + LP : LPTokenReply; + IC : ICTokenReply; +}; +type LPTokenReply = record { + token_id : nat32; + name : text; + chain : text; + symbol : text; + token : text; + pool_id_of : nat32; + address : text; + decimals : nat8; + fee : nat; + total_supply : nat; + on_kong : bool; +}; +type ICTokenReply = record { + token_id : nat32; + name : text; + chain : text; + symbol : text; + token : text; + canister_id : text; + decimals : nat8; + fee : nat; + icrc1 : bool; + icrc2 : bool; + icrc3 : bool; + on_kong : bool; +}; +type TokensResult = variant { Ok : vec TokenReply; Err : text }; + +type PoolsReply = record { + pools : vec PoolReply; + total_tvl : nat; + total_24h_volume : nat; + total_24h_lp_fee : nat; + total_24h_num_swaps : nat; +}; +type PoolReply = record { + pool_id : nat32; + name : text; + symbol : text; + chain_0 : text; + symbol_0 : text; + address_0 : text; + balance_0 : nat; + lp_fee_0 : nat; + chain_1 : text; + symbol_1 : text; + address_1 : text; + balance_1 : nat; + lp_fee_1 : nat; + price : float64; + lp_fee_bps : nat8; + tvl : nat; // USD value of TVL + rolling_24h_volume : nat; // USD value of rolling 24h volume + rolling_24h_lp_fee : nat; // USD value of rolling 24h LP fees + rolling_24h_num_swaps : nat; + rolling_24h_apy : float64; + lp_token_symbol : text; + on_kong : bool; // flag indicating if displayed on Kong Swap +}; +type PoolsResult = variant { Ok : PoolsReply; Err : text }; + +type PoolExpectedBalance = record { + pool_symbol : text; + balance : nat; + lp_fee : nat; + kong_fee : nat; +}; +type ExpectedBalance = record { + balance : nat; + pool_balances : vec PoolExpectedBalance; + unclaimed_claims : nat; +}; +type CheckPoolsReply = record { + symbol : text; + actual_balance : nat; + expected_balance : ExpectedBalance; + diff_balance : int; +}; +type CheckPoolsResult = variant { Ok : vec CheckPoolsReply; Err : text }; + +type TxsReply = variant { + AddPool : AddPoolReply; + AddLiquidity : AddLiquidityReply; + RemoveLiquidity : RemoveLiquidityReply; + Swap : SwapReply; +}; +type TxsResult = variant { Ok : vec TxsReply; Err : text }; + +type RequestRequest = variant { + AddPool : AddPoolArgs; + AddLiquidity : AddLiquidityArgs; + RemoveLiquidity : RemoveLiquidityArgs; + Swap : SwapArgs; +}; + +type RequestReply = variant { + Pending; + AddPool : AddPoolReply; + AddLiquidity : AddLiquidityReply; + RemoveLiquidity : RemoveLiquidityReply; + Swap : SwapReply; +}; + +type RequestsReply = record { + request_id : nat64; + statuses : vec text; + request : RequestRequest; + reply : RequestReply; + ts : nat64; +}; +type RequestsResult = variant { Ok : vec RequestsReply; Err : text }; + +type TransfersResult = variant { Ok : vec TransferIdReply; Err : text }; + +type AddTokenArgs = record { + token : text; + on_kong : opt bool; +}; +type AddTokenReply = variant { + IC : ICTokenReply; +}; +type AddTokenResult = variant { Ok : AddTokenReply; Err : text }; + +type AddPoolArgs = record { + token_0 : text; + amount_0 : nat; + tx_id_0 : opt TxId; + token_1 : text; + amount_1 : nat; + tx_id_1 : opt TxId; + lp_fee_bps : opt nat8; + on_kong : opt bool; +}; +type AddPoolReply = record { + tx_id : nat64; + symbol : text; + request_id : nat64; + status : text; + chain_0 : text; + symbol_0 : text; + amount_0 : nat; + chain_1 : text; + symbol_1 : text; + amount_1 : nat; + add_lp_token_amount : nat; + lp_fee_bps : nat8; + lp_token_symbol : text; + transfer_ids : vec TransferIdReply; + claim_ids : vec nat64; + on_kong : bool; + ts : nat64; +}; +type AddPoolResult = variant { Ok : AddPoolReply; Err : text }; + +type AddLiquidityAmountsReply = record { + symbol : text; + chain_0 : text; + symbol_0 : text; + address_0 : text; + amount_0 : nat; + fee_0 : nat; + chain_1 : text; + symbol_1 : text; + address_1 : text; + amount_1 : nat; + fee_1 : nat; + add_lp_token_amount : nat; +}; +type AddLiquiditAmountsResult = variant { Ok : AddLiquidityAmountsReply; Err : text }; + +type AddLiquidityArgs = record { + token_0 : text; + amount_0 : nat; + tx_id_0 : opt TxId; + token_1 : text; + amount_1 : nat; + tx_id_1 : opt TxId; +}; +type AddLiquidityReply = record { + tx_id : nat64; + symbol : text; + request_id : nat64; + status : text; + chain_0 : text; + symbol_0 : text; + amount_0 : nat; + chain_1 : text; + symbol_1 : text; + amount_1 : nat; + add_lp_token_amount : nat; + transfer_ids : vec TransferIdReply; + claim_ids : vec nat64; + ts : nat64; +}; +type AddLiquidityResult = variant { Ok : AddLiquidityReply; Err : text }; +type AddLiquidityAsyncResult = variant { Ok : nat64; Err : text }; +type ValidateAddLiquidityResult = variant { Ok : text; Err : text }; + +type RemoveLiquidityAmountsReply = record { + symbol : text; + chain_0 : text; + symbol_0 : text; + address_0 : text; + amount_0 : nat; + lp_fee_0 : nat; + chain_1 : text; + symbol_1 : text; + address_1 : text; + amount_1 : nat; + lp_fee_1 : nat; + remove_lp_token_amount : nat; +}; +type RemoveLiquidityAmountsResult = variant { Ok : RemoveLiquidityAmountsReply; Err : text }; + +type RemoveLiquidityArgs = record { + token_0 : text; + token_1 : text; + remove_lp_token_amount : nat; +}; +type RemoveLiquidityReply = record { + tx_id : nat64; + symbol : text; + request_id : nat64; + status : text; + chain_0 : text; + symbol_0 : text; + amount_0 : nat; + lp_fee_0 : nat; + chain_1 : text; + symbol_1 : text; + amount_1 : nat; + lp_fee_1 : nat; + remove_lp_token_amount : nat; + transfer_ids : vec TransferIdReply; + claim_ids : vec nat64; + ts : nat64; +}; +type RemoveLiquidityResult = variant { Ok : RemoveLiquidityReply; Err : text }; +type RemoveLiquidityAsyncResult = variant { Ok : nat64; Err : text }; +type ValidateRemoveLiquidityResult = variant { Ok : text; Err : text }; + +type SwapAmountsTxReply = record { + pool_symbol : text; + pay_chain : text; + pay_symbol : text; + pay_address : text; + pay_amount : nat; + receive_chain : text; + receive_symbol : text; + receive_address : text; + receive_amount : nat; + price : float64; + lp_fee : nat; + gas_fee : nat; +}; +type SwapAmountsReply = record { + pay_chain : text; + pay_symbol : text; + pay_address : text; + pay_amount : nat; + receive_chain : text; + receive_symbol : text; + receive_address : text; + receive_amount : nat; + price : float64; + mid_price : float64; + slippage : float64; + txs : vec SwapAmountsTxReply; +}; +type SwapAmountsResult = variant { Ok : SwapAmountsReply; Err : text }; + +type SwapArgs = record { + pay_token : text; + pay_amount : nat; + pay_tx_id : opt TxId; + receive_token : text; + receive_amount : opt nat; + receive_address : opt text; + max_slippage : opt float64; + referred_by : opt text; +}; +type SwapTxReply = record { + pool_symbol : text; + pay_chain : text; + pay_symbol : text; + pay_amount : nat; + receive_chain : text; + receive_symbol : text; + receive_amount : nat; + price : float64; + lp_fee : nat; + gas_fee : nat; + ts : nat64; +}; +type SwapReply = record { + tx_id : nat64; + request_id : nat64; + status : text; + pay_chain : text; + pay_symbol : text; + pay_amount : nat; + receive_chain : text; + receive_symbol : text; + receive_amount : nat; + mid_price : float64; + price : float64; + slippage : float64; + txs : vec SwapTxReply; + transfer_ids : vec TransferIdReply; + claim_ids : vec nat64; + ts : nat64; +}; +type SwapResult = variant { Ok : SwapReply; Err : text }; +type SwapAsyncResult = variant { Ok : nat64; Err : text }; + +type SendArgs = record { + token : text; + amount : nat; + to_address : text; +}; +type SendReply = record { + tx_id : nat64; + request_id : nat64; + status : text; + chain : text; + symbol : text; + amount : nat; + to_address : text; + ts : nat64; +}; +type SendResult = variant { OK : SendReply; Err : text }; + +service : { + // icrc1 standards + icrc1_name : () -> (text) query; + icrc10_supported_standards : () -> (vec Icrc10SupportedStandards) query; + icrc21_canister_call_consent_message: (icrc21_consent_message_request) -> (icrc21_consent_message_response); + icrc28_trusted_origins : () -> (Icrc28TrustedOriginsResponse); + + // tokens(opt wildcard) - returns all tokens or wildcard search + tokens : (opt text) -> (TokensResult) query; + // pools(opt wildcard) - returns all pools or wildcard search + pools : (opt text) -> (PoolsResult) query; + + // user() - returns user information + get_user : () -> (UserResult) query; + // user_balances(principal_id, opt lp_token_wildcard) - returns all user balances or specific LP token balances (current only supports balance of LP tokens) + user_balances : (text, opt text) -> (UserBalancesResult) query; + // messages(opt message_id) - returns specific message or all messages of the user + messages : (opt nat64) -> (MessagesResult) query; + // requests(opt request_id) - return specific request or most recent requests + requests : (opt nat64) -> (RequestsResult) query; + // txs(opt principal_id) - return transactions filtered by principal id + txs : (opt text) -> (TxsResult) query; + + // add a new liquidity pool and token + add_pool : (AddPoolArgs) -> (AddPoolResult); + + // add_liquidity_amounts(token_0, amount_0, token_1) + // token_0, token_1 - format Symbol, Chain.Symbol, CanisterId or Chain.CanisterId ie. ckBTC, IC.ckBTC, or IC.ryjl3-tyaaa-aaaaa-aaaba-cai + // amount_0, amount_1 - Nat numbers with corresponding decimal precision as defined in ledger canister + // - calculates the required amount_1 to add liquidity to pool + // - results of add_liquidity_amounts() are then pass to add_liquidity() for execution + add_liquidity_amounts : (text, nat, text) -> (AddLiquiditAmountsResult) query; + // adds token_0 and token_1 to the liqudity pool in return for LP tokens + // - add_liquidity() has 2 variations: + // 1) 2 x icrc2_approve + icrc2_transfer_from - user must icrc2_approve the amount_0+gas of token_0, amount_1+gas of token_1 and then call add_liquidity() where the canister will then icrc2_transfer_from + // 2) 2 x icrc1_transfer - user must icrc1_transfer the amount_0 of token_0, amount_1 of token_1 and then call add_liquidity() with the block index (tx_id_0 and tx_id_1) + add_liquidity : (AddLiquidityArgs) -> (AddLiquidityResult); + // asnychronous version of add_liquidity() + // request_id will be returned by add_liquidity_async() and poll requests(request_id) to get updated status + add_liquidity_async : (AddLiquidityArgs) -> (AddLiquidityAsyncResult); + // validate add_liquidity for SNS proposals + validate_add_liquidity : () -> (ValidateAddLiquidityResult); + + // remove_liquidity_amounts(token_0, token_1, remove_lp_token_amount) + // calcalates the expected token_0 and token_1 to be received from redeeming remove_lp_token_amount of LP tokens to the pool + remove_liquidity_amounts : (text, text, nat) -> (RemoveLiquidityAmountsResult) query; + // redeems remove_lp_token_amount of LP tokens to the pool and receives token_0 and token_1 in return + remove_liquidity : (RemoveLiquidityArgs) -> (RemoveLiquidityResult); + // asnychronous version of remove_liquidity() + // request_id will be returned by remove_liquidity_async() and poll requests(request_id) to get updated status + remove_liquidity_async : (RemoveLiquidityArgs) -> (RemoveLiquidityAsyncResult); + // validate remove_liquidity for SNS proposals + validate_remove_liquidity : () -> (ValidateRemoveLiquidityResult); + + // swap_amounts(pay_token, pay_amount, receive_token) + // pay_token, receive_token - format Symbol, Chain.Symbol, CanisterId or Chain.CanisterId ie. ckBTC, IC.ckBTC, or IC.ryjl3-tyaaa-aaaaa-aaaba-cai + // pay_amount, receive_amount - Nat numbers with corresponding decimal precision as defined in ledger canister + // - calculates the expected receive_amount and price of the swap + // - results of swap_amounts() are then pass to swap() for execution + swap_amounts : (text, nat, text) -> (SwapAmountsResult) query; + + // swap() + // pay_token, receive_token - format Symbol, Chain.Symbol, CanisterId or Chain.CanisterId ie. ckBTC, IC.ckBTC, or IC.ryjl3-tyaaa-aaaaa-aaaba-cai + // pay_amount, receive_amount - Nat numbers with corresponding decimal precision as defined in ledger canister + // - swaps pay_amount of pay_token into receive_amount of receive_token + // - swap() has 2 variations: + // 1) icrc2_approve + icrc2_transfer_from - user must icrc2_approve the pay_amount+gas of pay_token and then call swap() where the canister will then icrc2_transfer_from + // 2) icrc1_transfer - user must icrc1_transfer the pay_amount of pay_token and then call swap() with the block index + swap : (SwapArgs) -> (SwapResult); + // asnychronous version of swap() + // request_id will be returned by swap_async() and poll requests(request_id) to get updated status + swap_async : (SwapArgs) -> (SwapAsyncResult); + + // send LP tokens to another user + send : (SendArgs) -> (SendResult); + + // admin functions + check_pools : () -> (CheckPoolsResult); + get_requests : (opt nat64, opt nat32, opt nat16) -> (RequestsResult) query; + get_txs : (opt nat64, opt nat64, opt nat32, opt nat16) -> (TxsResult) query; +} diff --git a/src/declarations/kong_backend/kong_backend.did.d.ts b/src/declarations/kong_backend/kong_backend.did.d.ts new file mode 100644 index 0000000000..53864c9f25 --- /dev/null +++ b/src/declarations/kong_backend/kong_backend.did.d.ts @@ -0,0 +1,452 @@ +import type { ActorMethod } from '@dfinity/agent'; +import type { IDL } from '@dfinity/candid'; + +export type AddLiquiditAmountsResult = { Ok: AddLiquidityAmountsReply } | { Err: string }; +export interface AddLiquidityAmountsReply { + add_lp_token_amount: bigint; + amount_0: bigint; + amount_1: bigint; + address_0: string; + address_1: string; + symbol_0: string; + symbol_1: string; + chain_0: string; + chain_1: string; + symbol: string; + fee_0: bigint; + fee_1: bigint; +} +export interface AddLiquidityArgs { + token_0: string; + token_1: string; + amount_0: bigint; + amount_1: bigint; + tx_id_0: [] | [TxId]; + tx_id_1: [] | [TxId]; +} +export type AddLiquidityAsyncResult = { Ok: bigint } | { Err: string }; +export interface AddLiquidityReply { + ts: bigint; + request_id: bigint; + status: string; + tx_id: bigint; + add_lp_token_amount: bigint; + transfer_ids: Array; + amount_0: bigint; + amount_1: bigint; + claim_ids: BigUint64Array | bigint[]; + symbol_0: string; + symbol_1: string; + chain_0: string; + chain_1: string; + symbol: string; +} +export type AddLiquidityResult = { Ok: AddLiquidityReply } | { Err: string }; +export interface AddPoolArgs { + token_0: string; + token_1: string; + amount_0: bigint; + amount_1: bigint; + tx_id_0: [] | [TxId]; + tx_id_1: [] | [TxId]; + lp_fee_bps: [] | [number]; + on_kong: [] | [boolean]; +} +export interface AddPoolReply { + ts: bigint; + request_id: bigint; + status: string; + tx_id: bigint; + lp_token_symbol: string; + add_lp_token_amount: bigint; + transfer_ids: Array; + amount_0: bigint; + amount_1: bigint; + claim_ids: BigUint64Array | bigint[]; + symbol_0: string; + symbol_1: string; + chain_0: string; + chain_1: string; + symbol: string; + lp_fee_bps: number; + on_kong: boolean; +} +export type AddPoolResult = { Ok: AddPoolReply } | { Err: string }; +export interface AddTokenArgs { + token: string; + on_kong: [] | [boolean]; +} +export type AddTokenReply = { IC: ICTokenReply }; +export type AddTokenResult = { Ok: AddTokenReply } | { Err: string }; +export interface BalancesReply { + ts: bigint; + usd_balance: number; + balance: number; + name: string; + amount_0: number; + amount_1: number; + symbol_0: string; + symbol_1: string; + usd_amount_0: number; + usd_amount_1: number; + symbol: string; +} +export interface CheckPoolsReply { + expected_balance: ExpectedBalance; + diff_balance: bigint; + actual_balance: bigint; + symbol: string; +} +export type CheckPoolsResult = { Ok: Array } | { Err: string }; +export interface ExpectedBalance { + balance: bigint; + pool_balances: Array; + unclaimed_claims: bigint; +} +export interface ICTokenReply { + fee: bigint; + decimals: number; + token: string; + token_id: number; + chain: string; + name: string; + canister_id: string; + icrc1: boolean; + icrc2: boolean; + icrc3: boolean; + symbol: string; + on_kong: boolean; +} +export interface ICTransferReply { + is_send: boolean; + block_index: bigint; + chain: string; + canister_id: string; + amount: bigint; + symbol: string; +} +export interface Icrc10SupportedStandards { + url: string; + name: string; +} +export interface Icrc28TrustedOriginsResponse { + trusted_origins: Array; +} +export interface LPTokenReply { + fee: bigint; + decimals: number; + token: string; + token_id: number; + chain: string; + name: string; + address: string; + pool_id_of: number; + total_supply: bigint; + symbol: string; + on_kong: boolean; +} +export interface MessagesReply { + ts: bigint; + title: string; + message: string; + message_id: bigint; +} +export type MessagesResult = { Ok: Array } | { Err: string }; +export interface PoolExpectedBalance { + balance: bigint; + kong_fee: bigint; + pool_symbol: string; + lp_fee: bigint; +} +export interface PoolReply { + tvl: bigint; + lp_token_symbol: string; + name: string; + lp_fee_0: bigint; + lp_fee_1: bigint; + balance_0: bigint; + balance_1: bigint; + rolling_24h_volume: bigint; + rolling_24h_apy: number; + address_0: string; + address_1: string; + rolling_24h_num_swaps: bigint; + symbol_0: string; + symbol_1: string; + pool_id: number; + price: number; + chain_0: string; + chain_1: string; + symbol: string; + rolling_24h_lp_fee: bigint; + lp_fee_bps: number; + on_kong: boolean; +} +export interface PoolsReply { + total_24h_lp_fee: bigint; + total_tvl: bigint; + total_24h_volume: bigint; + pools: Array; + total_24h_num_swaps: bigint; +} +export type PoolsResult = { Ok: PoolsReply } | { Err: string }; +export interface RemoveLiquidityAmountsReply { + lp_fee_0: bigint; + lp_fee_1: bigint; + amount_0: bigint; + amount_1: bigint; + address_0: string; + address_1: string; + symbol_0: string; + symbol_1: string; + chain_0: string; + chain_1: string; + remove_lp_token_amount: bigint; + symbol: string; +} +export type RemoveLiquidityAmountsResult = + | { + Ok: RemoveLiquidityAmountsReply; + } + | { Err: string }; +export interface RemoveLiquidityArgs { + token_0: string; + token_1: string; + remove_lp_token_amount: bigint; +} +export type RemoveLiquidityAsyncResult = { Ok: bigint } | { Err: string }; +export interface RemoveLiquidityReply { + ts: bigint; + request_id: bigint; + status: string; + tx_id: bigint; + transfer_ids: Array; + lp_fee_0: bigint; + lp_fee_1: bigint; + amount_0: bigint; + amount_1: bigint; + claim_ids: BigUint64Array | bigint[]; + symbol_0: string; + symbol_1: string; + chain_0: string; + chain_1: string; + remove_lp_token_amount: bigint; + symbol: string; +} +export type RemoveLiquidityResult = { Ok: RemoveLiquidityReply } | { Err: string }; +export type RequestReply = + | { AddLiquidity: AddLiquidityReply } + | { Swap: SwapReply } + | { AddPool: AddPoolReply } + | { RemoveLiquidity: RemoveLiquidityReply } + | { Pending: null }; +export type RequestRequest = + | { AddLiquidity: AddLiquidityArgs } + | { Swap: SwapArgs } + | { AddPool: AddPoolArgs } + | { RemoveLiquidity: RemoveLiquidityArgs }; +export interface RequestsReply { + ts: bigint; + request_id: bigint; + request: RequestRequest; + statuses: Array; + reply: RequestReply; +} +export type RequestsResult = { Ok: Array } | { Err: string }; +export interface SendArgs { + token: string; + to_address: string; + amount: bigint; +} +export interface SendReply { + ts: bigint; + request_id: bigint; + status: string; + tx_id: bigint; + chain: string; + to_address: string; + amount: bigint; + symbol: string; +} +export type SendResult = { OK: SendReply } | { Err: string }; +export interface SwapAmountsReply { + txs: Array; + receive_chain: string; + mid_price: number; + pay_amount: bigint; + receive_amount: bigint; + pay_symbol: string; + receive_symbol: string; + receive_address: string; + pay_address: string; + price: number; + pay_chain: string; + slippage: number; +} +export type SwapAmountsResult = { Ok: SwapAmountsReply } | { Err: string }; +export interface SwapAmountsTxReply { + receive_chain: string; + pay_amount: bigint; + receive_amount: bigint; + pay_symbol: string; + receive_symbol: string; + receive_address: string; + pool_symbol: string; + pay_address: string; + price: number; + pay_chain: string; + lp_fee: bigint; + gas_fee: bigint; +} +export interface SwapArgs { + receive_token: string; + max_slippage: [] | [number]; + pay_amount: bigint; + referred_by: [] | [string]; + receive_amount: [] | [bigint]; + receive_address: [] | [string]; + pay_token: string; + pay_tx_id: [] | [TxId]; +} +export type SwapAsyncResult = { Ok: bigint } | { Err: string }; +export interface SwapReply { + ts: bigint; + txs: Array; + request_id: bigint; + status: string; + tx_id: bigint; + transfer_ids: Array; + receive_chain: string; + mid_price: number; + pay_amount: bigint; + receive_amount: bigint; + claim_ids: BigUint64Array | bigint[]; + pay_symbol: string; + receive_symbol: string; + price: number; + pay_chain: string; + slippage: number; +} +export type SwapResult = { Ok: SwapReply } | { Err: string }; +export interface SwapTxReply { + ts: bigint; + receive_chain: string; + pay_amount: bigint; + receive_amount: bigint; + pay_symbol: string; + receive_symbol: string; + pool_symbol: string; + price: number; + pay_chain: string; + lp_fee: bigint; + gas_fee: bigint; +} +export type TokenReply = { IC: ICTokenReply } | { LP: LPTokenReply }; +export type TokensResult = { Ok: Array } | { Err: string }; +export interface TransferIdReply { + transfer_id: bigint; + transfer: TransferReply; +} +export type TransferReply = { IC: ICTransferReply }; +export type TransfersResult = { Ok: Array } | { Err: string }; +export type TxId = { TransactionId: string } | { BlockIndex: bigint }; +export type TxsReply = + | { AddLiquidity: AddLiquidityReply } + | { Swap: SwapReply } + | { AddPool: AddPoolReply } + | { RemoveLiquidity: RemoveLiquidityReply }; +export type TxsResult = { Ok: Array } | { Err: string }; +export type UserBalancesReply = { LP: BalancesReply }; +export type UserBalancesResult = { Ok: Array } | { Err: string }; +export interface UserReply { + account_id: string; + user_name: string; + fee_level_expires_at: [] | [bigint]; + referred_by: [] | [string]; + user_id: number; + fee_level: number; + principal_id: string; + referred_by_expires_at: [] | [bigint]; + campaign1_flags: Array; + my_referral_code: string; +} +export type UserResult = { Ok: UserReply } | { Err: string }; +export type ValidateAddLiquidityResult = { Ok: string } | { Err: string }; +export type ValidateRemoveLiquidityResult = { Ok: string } | { Err: string }; +export interface icrc21_consent_info { + metadata: icrc21_consent_message_metadata; + consent_message: icrc21_consent_message; +} +export type icrc21_consent_message = + | { + LineDisplayMessage: { pages: Array<{ lines: Array }> }; + } + | { GenericDisplayMessage: string }; +export interface icrc21_consent_message_metadata { + utc_offset_minutes: [] | [number]; + language: string; +} +export interface icrc21_consent_message_request { + arg: Uint8Array | number[]; + method: string; + user_preferences: icrc21_consent_message_spec; +} +export type icrc21_consent_message_response = { Ok: icrc21_consent_info } | { Err: icrc21_error }; +export interface icrc21_consent_message_spec { + metadata: icrc21_consent_message_metadata; + device_spec: + | [] + | [ + | { GenericDisplay: null } + | { + LineDisplay: { + characters_per_line: number; + lines_per_page: number; + }; + } + ]; +} +export type icrc21_error = + | { + GenericError: { description: string; error_code: bigint }; + } + | { InsufficientPayment: icrc21_error_info } + | { UnsupportedCanisterCall: icrc21_error_info } + | { ConsentMessageUnavailable: icrc21_error_info }; +export interface icrc21_error_info { + description: string; +} +export interface _SERVICE { + add_liquidity: ActorMethod<[AddLiquidityArgs], AddLiquidityResult>; + add_liquidity_amounts: ActorMethod<[string, bigint, string], AddLiquiditAmountsResult>; + add_liquidity_async: ActorMethod<[AddLiquidityArgs], AddLiquidityAsyncResult>; + add_pool: ActorMethod<[AddPoolArgs], AddPoolResult>; + check_pools: ActorMethod<[], CheckPoolsResult>; + get_requests: ActorMethod<[[] | [bigint], [] | [number], [] | [number]], RequestsResult>; + get_txs: ActorMethod<[[] | [bigint], [] | [bigint], [] | [number], [] | [number]], TxsResult>; + get_user: ActorMethod<[], UserResult>; + icrc10_supported_standards: ActorMethod<[], Array>; + icrc1_name: ActorMethod<[], string>; + icrc21_canister_call_consent_message: ActorMethod< + [icrc21_consent_message_request], + icrc21_consent_message_response + >; + icrc28_trusted_origins: ActorMethod<[], Icrc28TrustedOriginsResponse>; + messages: ActorMethod<[[] | [bigint]], MessagesResult>; + pools: ActorMethod<[[] | [string]], PoolsResult>; + remove_liquidity: ActorMethod<[RemoveLiquidityArgs], RemoveLiquidityResult>; + remove_liquidity_amounts: ActorMethod<[string, string, bigint], RemoveLiquidityAmountsResult>; + remove_liquidity_async: ActorMethod<[RemoveLiquidityArgs], RemoveLiquidityAsyncResult>; + requests: ActorMethod<[[] | [bigint]], RequestsResult>; + send: ActorMethod<[SendArgs], SendResult>; + swap: ActorMethod<[SwapArgs], SwapResult>; + swap_amounts: ActorMethod<[string, bigint, string], SwapAmountsResult>; + swap_async: ActorMethod<[SwapArgs], SwapAsyncResult>; + tokens: ActorMethod<[[] | [string]], TokensResult>; + txs: ActorMethod<[[] | [string]], TxsResult>; + user_balances: ActorMethod<[string, [] | [string]], UserBalancesResult>; + validate_add_liquidity: ActorMethod<[], ValidateAddLiquidityResult>; + validate_remove_liquidity: ActorMethod<[], ValidateRemoveLiquidityResult>; +} +export declare const idlFactory: IDL.InterfaceFactory; +export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[]; diff --git a/src/declarations/kong_backend/kong_backend.factory.certified.did.js b/src/declarations/kong_backend/kong_backend.factory.certified.did.js new file mode 100644 index 0000000000..03492df0d0 --- /dev/null +++ b/src/declarations/kong_backend/kong_backend.factory.certified.did.js @@ -0,0 +1,503 @@ +// @ts-ignore +export const idlFactory = ({ IDL }) => { + const TxId = IDL.Variant({ + TransactionId: IDL.Text, + BlockIndex: IDL.Nat + }); + const AddLiquidityArgs = IDL.Record({ + token_0: IDL.Text, + token_1: IDL.Text, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + tx_id_0: IDL.Opt(TxId), + tx_id_1: IDL.Opt(TxId) + }); + const ICTransferReply = IDL.Record({ + is_send: IDL.Bool, + block_index: IDL.Nat, + chain: IDL.Text, + canister_id: IDL.Text, + amount: IDL.Nat, + symbol: IDL.Text + }); + const TransferReply = IDL.Variant({ IC: ICTransferReply }); + const TransferIdReply = IDL.Record({ + transfer_id: IDL.Nat64, + transfer: TransferReply + }); + const AddLiquidityReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + add_lp_token_amount: IDL.Nat, + transfer_ids: IDL.Vec(TransferIdReply), + amount_0: IDL.Nat, + amount_1: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text + }); + const AddLiquidityResult = IDL.Variant({ + Ok: AddLiquidityReply, + Err: IDL.Text + }); + const AddLiquidityAmountsReply = IDL.Record({ + add_lp_token_amount: IDL.Nat, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + address_0: IDL.Text, + address_1: IDL.Text, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text, + fee_0: IDL.Nat, + fee_1: IDL.Nat + }); + const AddLiquiditAmountsResult = IDL.Variant({ + Ok: AddLiquidityAmountsReply, + Err: IDL.Text + }); + const AddLiquidityAsyncResult = IDL.Variant({ + Ok: IDL.Nat64, + Err: IDL.Text + }); + const AddPoolArgs = IDL.Record({ + token_0: IDL.Text, + token_1: IDL.Text, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + tx_id_0: IDL.Opt(TxId), + tx_id_1: IDL.Opt(TxId), + lp_fee_bps: IDL.Opt(IDL.Nat8), + on_kong: IDL.Opt(IDL.Bool) + }); + const AddPoolReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + lp_token_symbol: IDL.Text, + add_lp_token_amount: IDL.Nat, + transfer_ids: IDL.Vec(TransferIdReply), + amount_0: IDL.Nat, + amount_1: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text, + lp_fee_bps: IDL.Nat8, + on_kong: IDL.Bool + }); + const AddPoolResult = IDL.Variant({ Ok: AddPoolReply, Err: IDL.Text }); + const PoolExpectedBalance = IDL.Record({ + balance: IDL.Nat, + kong_fee: IDL.Nat, + pool_symbol: IDL.Text, + lp_fee: IDL.Nat + }); + const ExpectedBalance = IDL.Record({ + balance: IDL.Nat, + pool_balances: IDL.Vec(PoolExpectedBalance), + unclaimed_claims: IDL.Nat + }); + const CheckPoolsReply = IDL.Record({ + expected_balance: ExpectedBalance, + diff_balance: IDL.Int, + actual_balance: IDL.Nat, + symbol: IDL.Text + }); + const CheckPoolsResult = IDL.Variant({ + Ok: IDL.Vec(CheckPoolsReply), + Err: IDL.Text + }); + const SwapArgs = IDL.Record({ + receive_token: IDL.Text, + max_slippage: IDL.Opt(IDL.Float64), + pay_amount: IDL.Nat, + referred_by: IDL.Opt(IDL.Text), + receive_amount: IDL.Opt(IDL.Nat), + receive_address: IDL.Opt(IDL.Text), + pay_token: IDL.Text, + pay_tx_id: IDL.Opt(TxId) + }); + const RemoveLiquidityArgs = IDL.Record({ + token_0: IDL.Text, + token_1: IDL.Text, + remove_lp_token_amount: IDL.Nat + }); + const RequestRequest = IDL.Variant({ + AddLiquidity: AddLiquidityArgs, + Swap: SwapArgs, + AddPool: AddPoolArgs, + RemoveLiquidity: RemoveLiquidityArgs + }); + const SwapTxReply = IDL.Record({ + ts: IDL.Nat64, + receive_chain: IDL.Text, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + pool_symbol: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + lp_fee: IDL.Nat, + gas_fee: IDL.Nat + }); + const SwapReply = IDL.Record({ + ts: IDL.Nat64, + txs: IDL.Vec(SwapTxReply), + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + transfer_ids: IDL.Vec(TransferIdReply), + receive_chain: IDL.Text, + mid_price: IDL.Float64, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + slippage: IDL.Float64 + }); + const RemoveLiquidityReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + transfer_ids: IDL.Vec(TransferIdReply), + lp_fee_0: IDL.Nat, + lp_fee_1: IDL.Nat, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + remove_lp_token_amount: IDL.Nat, + symbol: IDL.Text + }); + const RequestReply = IDL.Variant({ + AddLiquidity: AddLiquidityReply, + Swap: SwapReply, + AddPool: AddPoolReply, + RemoveLiquidity: RemoveLiquidityReply, + Pending: IDL.Null + }); + const RequestsReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + request: RequestRequest, + statuses: IDL.Vec(IDL.Text), + reply: RequestReply + }); + const RequestsResult = IDL.Variant({ + Ok: IDL.Vec(RequestsReply), + Err: IDL.Text + }); + const TxsReply = IDL.Variant({ + AddLiquidity: AddLiquidityReply, + Swap: SwapReply, + AddPool: AddPoolReply, + RemoveLiquidity: RemoveLiquidityReply + }); + const TxsResult = IDL.Variant({ Ok: IDL.Vec(TxsReply), Err: IDL.Text }); + const UserReply = IDL.Record({ + account_id: IDL.Text, + user_name: IDL.Text, + fee_level_expires_at: IDL.Opt(IDL.Nat64), + referred_by: IDL.Opt(IDL.Text), + user_id: IDL.Nat32, + fee_level: IDL.Nat8, + principal_id: IDL.Text, + referred_by_expires_at: IDL.Opt(IDL.Nat64), + campaign1_flags: IDL.Vec(IDL.Bool), + my_referral_code: IDL.Text + }); + const UserResult = IDL.Variant({ Ok: UserReply, Err: IDL.Text }); + const Icrc10SupportedStandards = IDL.Record({ + url: IDL.Text, + name: IDL.Text + }); + const icrc21_consent_message_metadata = IDL.Record({ + utc_offset_minutes: IDL.Opt(IDL.Int16), + language: IDL.Text + }); + const icrc21_consent_message_spec = IDL.Record({ + metadata: icrc21_consent_message_metadata, + device_spec: IDL.Opt( + IDL.Variant({ + GenericDisplay: IDL.Null, + LineDisplay: IDL.Record({ + characters_per_line: IDL.Nat16, + lines_per_page: IDL.Nat16 + }) + }) + ) + }); + const icrc21_consent_message_request = IDL.Record({ + arg: IDL.Vec(IDL.Nat8), + method: IDL.Text, + user_preferences: icrc21_consent_message_spec + }); + const icrc21_consent_message = IDL.Variant({ + LineDisplayMessage: IDL.Record({ + pages: IDL.Vec(IDL.Record({ lines: IDL.Vec(IDL.Text) })) + }), + GenericDisplayMessage: IDL.Text + }); + const icrc21_consent_info = IDL.Record({ + metadata: icrc21_consent_message_metadata, + consent_message: icrc21_consent_message + }); + const icrc21_error_info = IDL.Record({ description: IDL.Text }); + const icrc21_error = IDL.Variant({ + GenericError: IDL.Record({ + description: IDL.Text, + error_code: IDL.Nat + }), + InsufficientPayment: icrc21_error_info, + UnsupportedCanisterCall: icrc21_error_info, + ConsentMessageUnavailable: icrc21_error_info + }); + const icrc21_consent_message_response = IDL.Variant({ + Ok: icrc21_consent_info, + Err: icrc21_error + }); + const Icrc28TrustedOriginsResponse = IDL.Record({ + trusted_origins: IDL.Vec(IDL.Text) + }); + const MessagesReply = IDL.Record({ + ts: IDL.Nat64, + title: IDL.Text, + message: IDL.Text, + message_id: IDL.Nat64 + }); + const MessagesResult = IDL.Variant({ + Ok: IDL.Vec(MessagesReply), + Err: IDL.Text + }); + const PoolReply = IDL.Record({ + tvl: IDL.Nat, + lp_token_symbol: IDL.Text, + name: IDL.Text, + lp_fee_0: IDL.Nat, + lp_fee_1: IDL.Nat, + balance_0: IDL.Nat, + balance_1: IDL.Nat, + rolling_24h_volume: IDL.Nat, + rolling_24h_apy: IDL.Float64, + address_0: IDL.Text, + address_1: IDL.Text, + rolling_24h_num_swaps: IDL.Nat, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + pool_id: IDL.Nat32, + price: IDL.Float64, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text, + rolling_24h_lp_fee: IDL.Nat, + lp_fee_bps: IDL.Nat8, + on_kong: IDL.Bool + }); + const PoolsReply = IDL.Record({ + total_24h_lp_fee: IDL.Nat, + total_tvl: IDL.Nat, + total_24h_volume: IDL.Nat, + pools: IDL.Vec(PoolReply), + total_24h_num_swaps: IDL.Nat + }); + const PoolsResult = IDL.Variant({ Ok: PoolsReply, Err: IDL.Text }); + const RemoveLiquidityResult = IDL.Variant({ + Ok: RemoveLiquidityReply, + Err: IDL.Text + }); + const RemoveLiquidityAmountsReply = IDL.Record({ + lp_fee_0: IDL.Nat, + lp_fee_1: IDL.Nat, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + address_0: IDL.Text, + address_1: IDL.Text, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + remove_lp_token_amount: IDL.Nat, + symbol: IDL.Text + }); + const RemoveLiquidityAmountsResult = IDL.Variant({ + Ok: RemoveLiquidityAmountsReply, + Err: IDL.Text + }); + const RemoveLiquidityAsyncResult = IDL.Variant({ + Ok: IDL.Nat64, + Err: IDL.Text + }); + const SendArgs = IDL.Record({ + token: IDL.Text, + to_address: IDL.Text, + amount: IDL.Nat + }); + const SendReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + chain: IDL.Text, + to_address: IDL.Text, + amount: IDL.Nat, + symbol: IDL.Text + }); + const SendResult = IDL.Variant({ OK: SendReply, Err: IDL.Text }); + const SwapResult = IDL.Variant({ Ok: SwapReply, Err: IDL.Text }); + const SwapAmountsTxReply = IDL.Record({ + receive_chain: IDL.Text, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + receive_address: IDL.Text, + pool_symbol: IDL.Text, + pay_address: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + lp_fee: IDL.Nat, + gas_fee: IDL.Nat + }); + const SwapAmountsReply = IDL.Record({ + txs: IDL.Vec(SwapAmountsTxReply), + receive_chain: IDL.Text, + mid_price: IDL.Float64, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + receive_address: IDL.Text, + pay_address: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + slippage: IDL.Float64 + }); + const SwapAmountsResult = IDL.Variant({ + Ok: SwapAmountsReply, + Err: IDL.Text + }); + const SwapAsyncResult = IDL.Variant({ Ok: IDL.Nat64, Err: IDL.Text }); + const ICTokenReply = IDL.Record({ + fee: IDL.Nat, + decimals: IDL.Nat8, + token: IDL.Text, + token_id: IDL.Nat32, + chain: IDL.Text, + name: IDL.Text, + canister_id: IDL.Text, + icrc1: IDL.Bool, + icrc2: IDL.Bool, + icrc3: IDL.Bool, + symbol: IDL.Text, + on_kong: IDL.Bool + }); + const LPTokenReply = IDL.Record({ + fee: IDL.Nat, + decimals: IDL.Nat8, + token: IDL.Text, + token_id: IDL.Nat32, + chain: IDL.Text, + name: IDL.Text, + address: IDL.Text, + pool_id_of: IDL.Nat32, + total_supply: IDL.Nat, + symbol: IDL.Text, + on_kong: IDL.Bool + }); + const TokenReply = IDL.Variant({ IC: ICTokenReply, LP: LPTokenReply }); + const TokensResult = IDL.Variant({ + Ok: IDL.Vec(TokenReply), + Err: IDL.Text + }); + const BalancesReply = IDL.Record({ + ts: IDL.Nat64, + usd_balance: IDL.Float64, + balance: IDL.Float64, + name: IDL.Text, + amount_0: IDL.Float64, + amount_1: IDL.Float64, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + usd_amount_0: IDL.Float64, + usd_amount_1: IDL.Float64, + symbol: IDL.Text + }); + const UserBalancesReply = IDL.Variant({ LP: BalancesReply }); + const UserBalancesResult = IDL.Variant({ + Ok: IDL.Vec(UserBalancesReply), + Err: IDL.Text + }); + const ValidateAddLiquidityResult = IDL.Variant({ + Ok: IDL.Text, + Err: IDL.Text + }); + const ValidateRemoveLiquidityResult = IDL.Variant({ + Ok: IDL.Text, + Err: IDL.Text + }); + return IDL.Service({ + add_liquidity: IDL.Func([AddLiquidityArgs], [AddLiquidityResult], []), + add_liquidity_amounts: IDL.Func([IDL.Text, IDL.Nat, IDL.Text], [AddLiquiditAmountsResult]), + add_liquidity_async: IDL.Func([AddLiquidityArgs], [AddLiquidityAsyncResult], []), + add_pool: IDL.Func([AddPoolArgs], [AddPoolResult], []), + check_pools: IDL.Func([], [CheckPoolsResult], []), + get_requests: IDL.Func( + [IDL.Opt(IDL.Nat64), IDL.Opt(IDL.Nat32), IDL.Opt(IDL.Nat16)], + [RequestsResult] + ), + get_txs: IDL.Func( + [IDL.Opt(IDL.Nat64), IDL.Opt(IDL.Nat64), IDL.Opt(IDL.Nat32), IDL.Opt(IDL.Nat16)], + [TxsResult] + ), + get_user: IDL.Func([], [UserResult]), + icrc10_supported_standards: IDL.Func([], [IDL.Vec(Icrc10SupportedStandards)]), + icrc1_name: IDL.Func([], [IDL.Text]), + icrc21_canister_call_consent_message: IDL.Func( + [icrc21_consent_message_request], + [icrc21_consent_message_response], + [] + ), + icrc28_trusted_origins: IDL.Func([], [Icrc28TrustedOriginsResponse], []), + messages: IDL.Func([IDL.Opt(IDL.Nat64)], [MessagesResult]), + pools: IDL.Func([IDL.Opt(IDL.Text)], [PoolsResult]), + remove_liquidity: IDL.Func([RemoveLiquidityArgs], [RemoveLiquidityResult], []), + remove_liquidity_amounts: IDL.Func( + [IDL.Text, IDL.Text, IDL.Nat], + [RemoveLiquidityAmountsResult] + ), + remove_liquidity_async: IDL.Func([RemoveLiquidityArgs], [RemoveLiquidityAsyncResult], []), + requests: IDL.Func([IDL.Opt(IDL.Nat64)], [RequestsResult]), + send: IDL.Func([SendArgs], [SendResult], []), + swap: IDL.Func([SwapArgs], [SwapResult], []), + swap_amounts: IDL.Func([IDL.Text, IDL.Nat, IDL.Text], [SwapAmountsResult]), + swap_async: IDL.Func([SwapArgs], [SwapAsyncResult], []), + tokens: IDL.Func([IDL.Opt(IDL.Text)], [TokensResult]), + txs: IDL.Func([IDL.Opt(IDL.Text)], [TxsResult]), + user_balances: IDL.Func([IDL.Text, IDL.Opt(IDL.Text)], [UserBalancesResult]), + validate_add_liquidity: IDL.Func([], [ValidateAddLiquidityResult], []), + validate_remove_liquidity: IDL.Func([], [ValidateRemoveLiquidityResult], []) + }); +}; +// @ts-ignore +export const init = ({ IDL }) => { + return []; +}; diff --git a/src/declarations/kong_backend/kong_backend.factory.did.js b/src/declarations/kong_backend/kong_backend.factory.did.js new file mode 100644 index 0000000000..ce4d17c2aa --- /dev/null +++ b/src/declarations/kong_backend/kong_backend.factory.did.js @@ -0,0 +1,510 @@ +// @ts-ignore +export const idlFactory = ({ IDL }) => { + const TxId = IDL.Variant({ + TransactionId: IDL.Text, + BlockIndex: IDL.Nat + }); + const AddLiquidityArgs = IDL.Record({ + token_0: IDL.Text, + token_1: IDL.Text, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + tx_id_0: IDL.Opt(TxId), + tx_id_1: IDL.Opt(TxId) + }); + const ICTransferReply = IDL.Record({ + is_send: IDL.Bool, + block_index: IDL.Nat, + chain: IDL.Text, + canister_id: IDL.Text, + amount: IDL.Nat, + symbol: IDL.Text + }); + const TransferReply = IDL.Variant({ IC: ICTransferReply }); + const TransferIdReply = IDL.Record({ + transfer_id: IDL.Nat64, + transfer: TransferReply + }); + const AddLiquidityReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + add_lp_token_amount: IDL.Nat, + transfer_ids: IDL.Vec(TransferIdReply), + amount_0: IDL.Nat, + amount_1: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text + }); + const AddLiquidityResult = IDL.Variant({ + Ok: AddLiquidityReply, + Err: IDL.Text + }); + const AddLiquidityAmountsReply = IDL.Record({ + add_lp_token_amount: IDL.Nat, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + address_0: IDL.Text, + address_1: IDL.Text, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text, + fee_0: IDL.Nat, + fee_1: IDL.Nat + }); + const AddLiquiditAmountsResult = IDL.Variant({ + Ok: AddLiquidityAmountsReply, + Err: IDL.Text + }); + const AddLiquidityAsyncResult = IDL.Variant({ + Ok: IDL.Nat64, + Err: IDL.Text + }); + const AddPoolArgs = IDL.Record({ + token_0: IDL.Text, + token_1: IDL.Text, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + tx_id_0: IDL.Opt(TxId), + tx_id_1: IDL.Opt(TxId), + lp_fee_bps: IDL.Opt(IDL.Nat8), + on_kong: IDL.Opt(IDL.Bool) + }); + const AddPoolReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + lp_token_symbol: IDL.Text, + add_lp_token_amount: IDL.Nat, + transfer_ids: IDL.Vec(TransferIdReply), + amount_0: IDL.Nat, + amount_1: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text, + lp_fee_bps: IDL.Nat8, + on_kong: IDL.Bool + }); + const AddPoolResult = IDL.Variant({ Ok: AddPoolReply, Err: IDL.Text }); + const PoolExpectedBalance = IDL.Record({ + balance: IDL.Nat, + kong_fee: IDL.Nat, + pool_symbol: IDL.Text, + lp_fee: IDL.Nat + }); + const ExpectedBalance = IDL.Record({ + balance: IDL.Nat, + pool_balances: IDL.Vec(PoolExpectedBalance), + unclaimed_claims: IDL.Nat + }); + const CheckPoolsReply = IDL.Record({ + expected_balance: ExpectedBalance, + diff_balance: IDL.Int, + actual_balance: IDL.Nat, + symbol: IDL.Text + }); + const CheckPoolsResult = IDL.Variant({ + Ok: IDL.Vec(CheckPoolsReply), + Err: IDL.Text + }); + const SwapArgs = IDL.Record({ + receive_token: IDL.Text, + max_slippage: IDL.Opt(IDL.Float64), + pay_amount: IDL.Nat, + referred_by: IDL.Opt(IDL.Text), + receive_amount: IDL.Opt(IDL.Nat), + receive_address: IDL.Opt(IDL.Text), + pay_token: IDL.Text, + pay_tx_id: IDL.Opt(TxId) + }); + const RemoveLiquidityArgs = IDL.Record({ + token_0: IDL.Text, + token_1: IDL.Text, + remove_lp_token_amount: IDL.Nat + }); + const RequestRequest = IDL.Variant({ + AddLiquidity: AddLiquidityArgs, + Swap: SwapArgs, + AddPool: AddPoolArgs, + RemoveLiquidity: RemoveLiquidityArgs + }); + const SwapTxReply = IDL.Record({ + ts: IDL.Nat64, + receive_chain: IDL.Text, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + pool_symbol: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + lp_fee: IDL.Nat, + gas_fee: IDL.Nat + }); + const SwapReply = IDL.Record({ + ts: IDL.Nat64, + txs: IDL.Vec(SwapTxReply), + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + transfer_ids: IDL.Vec(TransferIdReply), + receive_chain: IDL.Text, + mid_price: IDL.Float64, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + slippage: IDL.Float64 + }); + const RemoveLiquidityReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + transfer_ids: IDL.Vec(TransferIdReply), + lp_fee_0: IDL.Nat, + lp_fee_1: IDL.Nat, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + claim_ids: IDL.Vec(IDL.Nat64), + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + remove_lp_token_amount: IDL.Nat, + symbol: IDL.Text + }); + const RequestReply = IDL.Variant({ + AddLiquidity: AddLiquidityReply, + Swap: SwapReply, + AddPool: AddPoolReply, + RemoveLiquidity: RemoveLiquidityReply, + Pending: IDL.Null + }); + const RequestsReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + request: RequestRequest, + statuses: IDL.Vec(IDL.Text), + reply: RequestReply + }); + const RequestsResult = IDL.Variant({ + Ok: IDL.Vec(RequestsReply), + Err: IDL.Text + }); + const TxsReply = IDL.Variant({ + AddLiquidity: AddLiquidityReply, + Swap: SwapReply, + AddPool: AddPoolReply, + RemoveLiquidity: RemoveLiquidityReply + }); + const TxsResult = IDL.Variant({ Ok: IDL.Vec(TxsReply), Err: IDL.Text }); + const UserReply = IDL.Record({ + account_id: IDL.Text, + user_name: IDL.Text, + fee_level_expires_at: IDL.Opt(IDL.Nat64), + referred_by: IDL.Opt(IDL.Text), + user_id: IDL.Nat32, + fee_level: IDL.Nat8, + principal_id: IDL.Text, + referred_by_expires_at: IDL.Opt(IDL.Nat64), + campaign1_flags: IDL.Vec(IDL.Bool), + my_referral_code: IDL.Text + }); + const UserResult = IDL.Variant({ Ok: UserReply, Err: IDL.Text }); + const Icrc10SupportedStandards = IDL.Record({ + url: IDL.Text, + name: IDL.Text + }); + const icrc21_consent_message_metadata = IDL.Record({ + utc_offset_minutes: IDL.Opt(IDL.Int16), + language: IDL.Text + }); + const icrc21_consent_message_spec = IDL.Record({ + metadata: icrc21_consent_message_metadata, + device_spec: IDL.Opt( + IDL.Variant({ + GenericDisplay: IDL.Null, + LineDisplay: IDL.Record({ + characters_per_line: IDL.Nat16, + lines_per_page: IDL.Nat16 + }) + }) + ) + }); + const icrc21_consent_message_request = IDL.Record({ + arg: IDL.Vec(IDL.Nat8), + method: IDL.Text, + user_preferences: icrc21_consent_message_spec + }); + const icrc21_consent_message = IDL.Variant({ + LineDisplayMessage: IDL.Record({ + pages: IDL.Vec(IDL.Record({ lines: IDL.Vec(IDL.Text) })) + }), + GenericDisplayMessage: IDL.Text + }); + const icrc21_consent_info = IDL.Record({ + metadata: icrc21_consent_message_metadata, + consent_message: icrc21_consent_message + }); + const icrc21_error_info = IDL.Record({ description: IDL.Text }); + const icrc21_error = IDL.Variant({ + GenericError: IDL.Record({ + description: IDL.Text, + error_code: IDL.Nat + }), + InsufficientPayment: icrc21_error_info, + UnsupportedCanisterCall: icrc21_error_info, + ConsentMessageUnavailable: icrc21_error_info + }); + const icrc21_consent_message_response = IDL.Variant({ + Ok: icrc21_consent_info, + Err: icrc21_error + }); + const Icrc28TrustedOriginsResponse = IDL.Record({ + trusted_origins: IDL.Vec(IDL.Text) + }); + const MessagesReply = IDL.Record({ + ts: IDL.Nat64, + title: IDL.Text, + message: IDL.Text, + message_id: IDL.Nat64 + }); + const MessagesResult = IDL.Variant({ + Ok: IDL.Vec(MessagesReply), + Err: IDL.Text + }); + const PoolReply = IDL.Record({ + tvl: IDL.Nat, + lp_token_symbol: IDL.Text, + name: IDL.Text, + lp_fee_0: IDL.Nat, + lp_fee_1: IDL.Nat, + balance_0: IDL.Nat, + balance_1: IDL.Nat, + rolling_24h_volume: IDL.Nat, + rolling_24h_apy: IDL.Float64, + address_0: IDL.Text, + address_1: IDL.Text, + rolling_24h_num_swaps: IDL.Nat, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + pool_id: IDL.Nat32, + price: IDL.Float64, + chain_0: IDL.Text, + chain_1: IDL.Text, + symbol: IDL.Text, + rolling_24h_lp_fee: IDL.Nat, + lp_fee_bps: IDL.Nat8, + on_kong: IDL.Bool + }); + const PoolsReply = IDL.Record({ + total_24h_lp_fee: IDL.Nat, + total_tvl: IDL.Nat, + total_24h_volume: IDL.Nat, + pools: IDL.Vec(PoolReply), + total_24h_num_swaps: IDL.Nat + }); + const PoolsResult = IDL.Variant({ Ok: PoolsReply, Err: IDL.Text }); + const RemoveLiquidityResult = IDL.Variant({ + Ok: RemoveLiquidityReply, + Err: IDL.Text + }); + const RemoveLiquidityAmountsReply = IDL.Record({ + lp_fee_0: IDL.Nat, + lp_fee_1: IDL.Nat, + amount_0: IDL.Nat, + amount_1: IDL.Nat, + address_0: IDL.Text, + address_1: IDL.Text, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + chain_0: IDL.Text, + chain_1: IDL.Text, + remove_lp_token_amount: IDL.Nat, + symbol: IDL.Text + }); + const RemoveLiquidityAmountsResult = IDL.Variant({ + Ok: RemoveLiquidityAmountsReply, + Err: IDL.Text + }); + const RemoveLiquidityAsyncResult = IDL.Variant({ + Ok: IDL.Nat64, + Err: IDL.Text + }); + const SendArgs = IDL.Record({ + token: IDL.Text, + to_address: IDL.Text, + amount: IDL.Nat + }); + const SendReply = IDL.Record({ + ts: IDL.Nat64, + request_id: IDL.Nat64, + status: IDL.Text, + tx_id: IDL.Nat64, + chain: IDL.Text, + to_address: IDL.Text, + amount: IDL.Nat, + symbol: IDL.Text + }); + const SendResult = IDL.Variant({ OK: SendReply, Err: IDL.Text }); + const SwapResult = IDL.Variant({ Ok: SwapReply, Err: IDL.Text }); + const SwapAmountsTxReply = IDL.Record({ + receive_chain: IDL.Text, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + receive_address: IDL.Text, + pool_symbol: IDL.Text, + pay_address: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + lp_fee: IDL.Nat, + gas_fee: IDL.Nat + }); + const SwapAmountsReply = IDL.Record({ + txs: IDL.Vec(SwapAmountsTxReply), + receive_chain: IDL.Text, + mid_price: IDL.Float64, + pay_amount: IDL.Nat, + receive_amount: IDL.Nat, + pay_symbol: IDL.Text, + receive_symbol: IDL.Text, + receive_address: IDL.Text, + pay_address: IDL.Text, + price: IDL.Float64, + pay_chain: IDL.Text, + slippage: IDL.Float64 + }); + const SwapAmountsResult = IDL.Variant({ + Ok: SwapAmountsReply, + Err: IDL.Text + }); + const SwapAsyncResult = IDL.Variant({ Ok: IDL.Nat64, Err: IDL.Text }); + const ICTokenReply = IDL.Record({ + fee: IDL.Nat, + decimals: IDL.Nat8, + token: IDL.Text, + token_id: IDL.Nat32, + chain: IDL.Text, + name: IDL.Text, + canister_id: IDL.Text, + icrc1: IDL.Bool, + icrc2: IDL.Bool, + icrc3: IDL.Bool, + symbol: IDL.Text, + on_kong: IDL.Bool + }); + const LPTokenReply = IDL.Record({ + fee: IDL.Nat, + decimals: IDL.Nat8, + token: IDL.Text, + token_id: IDL.Nat32, + chain: IDL.Text, + name: IDL.Text, + address: IDL.Text, + pool_id_of: IDL.Nat32, + total_supply: IDL.Nat, + symbol: IDL.Text, + on_kong: IDL.Bool + }); + const TokenReply = IDL.Variant({ IC: ICTokenReply, LP: LPTokenReply }); + const TokensResult = IDL.Variant({ + Ok: IDL.Vec(TokenReply), + Err: IDL.Text + }); + const BalancesReply = IDL.Record({ + ts: IDL.Nat64, + usd_balance: IDL.Float64, + balance: IDL.Float64, + name: IDL.Text, + amount_0: IDL.Float64, + amount_1: IDL.Float64, + symbol_0: IDL.Text, + symbol_1: IDL.Text, + usd_amount_0: IDL.Float64, + usd_amount_1: IDL.Float64, + symbol: IDL.Text + }); + const UserBalancesReply = IDL.Variant({ LP: BalancesReply }); + const UserBalancesResult = IDL.Variant({ + Ok: IDL.Vec(UserBalancesReply), + Err: IDL.Text + }); + const ValidateAddLiquidityResult = IDL.Variant({ + Ok: IDL.Text, + Err: IDL.Text + }); + const ValidateRemoveLiquidityResult = IDL.Variant({ + Ok: IDL.Text, + Err: IDL.Text + }); + return IDL.Service({ + add_liquidity: IDL.Func([AddLiquidityArgs], [AddLiquidityResult], []), + add_liquidity_amounts: IDL.Func( + [IDL.Text, IDL.Nat, IDL.Text], + [AddLiquiditAmountsResult], + ['query'] + ), + add_liquidity_async: IDL.Func([AddLiquidityArgs], [AddLiquidityAsyncResult], []), + add_pool: IDL.Func([AddPoolArgs], [AddPoolResult], []), + check_pools: IDL.Func([], [CheckPoolsResult], []), + get_requests: IDL.Func( + [IDL.Opt(IDL.Nat64), IDL.Opt(IDL.Nat32), IDL.Opt(IDL.Nat16)], + [RequestsResult], + ['query'] + ), + get_txs: IDL.Func( + [IDL.Opt(IDL.Nat64), IDL.Opt(IDL.Nat64), IDL.Opt(IDL.Nat32), IDL.Opt(IDL.Nat16)], + [TxsResult], + ['query'] + ), + get_user: IDL.Func([], [UserResult], ['query']), + icrc10_supported_standards: IDL.Func([], [IDL.Vec(Icrc10SupportedStandards)], ['query']), + icrc1_name: IDL.Func([], [IDL.Text], ['query']), + icrc21_canister_call_consent_message: IDL.Func( + [icrc21_consent_message_request], + [icrc21_consent_message_response], + [] + ), + icrc28_trusted_origins: IDL.Func([], [Icrc28TrustedOriginsResponse], []), + messages: IDL.Func([IDL.Opt(IDL.Nat64)], [MessagesResult], ['query']), + pools: IDL.Func([IDL.Opt(IDL.Text)], [PoolsResult], ['query']), + remove_liquidity: IDL.Func([RemoveLiquidityArgs], [RemoveLiquidityResult], []), + remove_liquidity_amounts: IDL.Func( + [IDL.Text, IDL.Text, IDL.Nat], + [RemoveLiquidityAmountsResult], + ['query'] + ), + remove_liquidity_async: IDL.Func([RemoveLiquidityArgs], [RemoveLiquidityAsyncResult], []), + requests: IDL.Func([IDL.Opt(IDL.Nat64)], [RequestsResult], ['query']), + send: IDL.Func([SendArgs], [SendResult], []), + swap: IDL.Func([SwapArgs], [SwapResult], []), + swap_amounts: IDL.Func([IDL.Text, IDL.Nat, IDL.Text], [SwapAmountsResult], ['query']), + swap_async: IDL.Func([SwapArgs], [SwapAsyncResult], []), + tokens: IDL.Func([IDL.Opt(IDL.Text)], [TokensResult], ['query']), + txs: IDL.Func([IDL.Opt(IDL.Text)], [TxsResult], ['query']), + user_balances: IDL.Func([IDL.Text, IDL.Opt(IDL.Text)], [UserBalancesResult], ['query']), + validate_add_liquidity: IDL.Func([], [ValidateAddLiquidityResult], []), + validate_remove_liquidity: IDL.Func([], [ValidateRemoveLiquidityResult], []) + }); +}; +// @ts-ignore +export const init = ({ IDL }) => { + return []; +}; diff --git a/src/frontend/src/lib/api/kong_backend.api.ts b/src/frontend/src/lib/api/kong_backend.api.ts new file mode 100644 index 0000000000..f430c0fec1 --- /dev/null +++ b/src/frontend/src/lib/api/kong_backend.api.ts @@ -0,0 +1,52 @@ +import type { SwapAmountsReply } from '$declarations/kong_backend/kong_backend.did'; +import { KongBackendCanister } from '$lib/canisters/kong_backend.canister'; +import { KONG_BACKEND_CANISTER_ID } from '$lib/constants/app.constants'; +import type { KongSwapAmountsParams, KongSwapParams } from '$lib/types/api'; +import type { CanisterApiFunctionParams } from '$lib/types/canister'; +import { Principal } from '@dfinity/principal'; +import { assertNonNullish, isNullish } from '@dfinity/utils'; + +let canister: KongBackendCanister | undefined = undefined; + +export const kongSwapAmounts = async ({ + identity, + canisterId, + nullishIdentityErrorMessage, + ...restParams +}: CanisterApiFunctionParams): Promise => { + const { swapAmounts } = await kongBackendCanister({ + identity, + canisterId, + nullishIdentityErrorMessage + }); + + return swapAmounts(restParams); +}; + +export const kongSwap = async ({ + identity, + canisterId, + nullishIdentityErrorMessage, + ...restParams +}: CanisterApiFunctionParams): Promise => { + const { swap } = await kongBackendCanister({ identity, canisterId, nullishIdentityErrorMessage }); + + return swap(restParams); +}; + +const kongBackendCanister = async ({ + identity, + nullishIdentityErrorMessage, + canisterId = KONG_BACKEND_CANISTER_ID +}: CanisterApiFunctionParams): Promise => { + assertNonNullish(identity, nullishIdentityErrorMessage); + + if (isNullish(canister)) { + canister = await KongBackendCanister.create({ + identity, + canisterId: Principal.fromText(canisterId) + }); + } + + return canister; +}; diff --git a/src/frontend/src/lib/canisters/kong_backend.canister.ts b/src/frontend/src/lib/canisters/kong_backend.canister.ts new file mode 100644 index 0000000000..4bc6821864 --- /dev/null +++ b/src/frontend/src/lib/canisters/kong_backend.canister.ts @@ -0,0 +1,81 @@ +import type { + _SERVICE as KongBackendService, + SwapAmountsReply +} from '$declarations/kong_backend/kong_backend.did'; +import { idlFactory as idlCertifiedFactoryKongBackend } from '$declarations/kong_backend/kong_backend.factory.certified.did'; +import { idlFactory as idlFactoryKongBackend } from '$declarations/kong_backend/kong_backend.factory.did'; +import { getAgent } from '$lib/actors/agents.ic'; +import { mapKongBackendCanisterError } from '$lib/canisters/kong_backend.errors'; +import type { KongSwapAmountsParams, KongSwapParams } from '$lib/types/api'; +import type { CreateCanisterOptions } from '$lib/types/canister'; +import { Canister, createServices, toNullable } from '@dfinity/utils'; + +export class KongBackendCanister extends Canister { + static async create({ + identity, + ...options + }: CreateCanisterOptions): Promise { + const agent = await getAgent({ identity }); + + const { service, certifiedService, canisterId } = createServices({ + options: { + ...options, + agent + }, + idlFactory: idlFactoryKongBackend, + certifiedIdlFactory: idlCertifiedFactoryKongBackend + }); + + return new KongBackendCanister(canisterId, service, certifiedService); + } + + swapAmounts = async ({ + sourceToken, + destinationToken, + sourceAmount + }: KongSwapAmountsParams): Promise => { + const { swap_amounts } = this.caller({ + certified: true + }); + + const response = await swap_amounts(sourceToken.symbol, sourceAmount, destinationToken.symbol); + + if ('Ok' in response) { + return response.Ok; + } + + throw mapKongBackendCanisterError(response.Err); + }; + + swap = async ({ + destinationToken, + maxSlippage, + sendAmount, + referredBy, + receiveAmount, + destinationAddress, + sourceToken, + payTransactionId + }: KongSwapParams): Promise => { + const { swap_async } = this.caller({ + certified: true + }); + + const response = await swap_async({ + pay_token: sourceToken.symbol, + receive_token: destinationToken.symbol, + pay_amount: sendAmount, + max_slippage: toNullable(maxSlippage), + receive_address: toNullable(destinationAddress), + receive_amount: toNullable(receiveAmount), + pay_tx_id: toNullable(payTransactionId), + referred_by: toNullable(referredBy) + }); + + if ('Ok' in response) { + return response.Ok; + } + + throw mapKongBackendCanisterError(response.Err); + }; +} diff --git a/src/frontend/src/lib/canisters/kong_backend.errors.ts b/src/frontend/src/lib/canisters/kong_backend.errors.ts new file mode 100644 index 0000000000..b5acba13c0 --- /dev/null +++ b/src/frontend/src/lib/canisters/kong_backend.errors.ts @@ -0,0 +1,11 @@ +import { CanisterInternalError } from '$lib/canisters/errors'; +import { jsonReplacer } from '@dfinity/utils'; + +export class KongBackendCanisterError extends CanisterInternalError { + constructor(err: string) { + super(`Kong Backend error: ${JSON.stringify(err, jsonReplacer)}`); + } +} + +export const mapKongBackendCanisterError = (err: string): CanisterInternalError => + new CanisterInternalError(err); diff --git a/src/frontend/src/lib/constants/app.constants.ts b/src/frontend/src/lib/constants/app.constants.ts index 7bbfca587f..fb857b2383 100644 --- a/src/frontend/src/lib/constants/app.constants.ts +++ b/src/frontend/src/lib/constants/app.constants.ts @@ -63,6 +63,12 @@ export const SIGNER_CANISTER_ID = LOCAL ? import.meta.env.VITE_STAGING_SIGNER_CANISTER_ID : import.meta.env.VITE_IC_SIGNER_CANISTER_ID; +export const KONG_BACKEND_CANISTER_ID = LOCAL + ? import.meta.env.VITE_LOCAL_KONG_BACKEND_CANISTER_ID + : STAGING + ? import.meta.env.VITE_STAGING_KONG_BACKEND_CANISTER_ID + : import.meta.env.VITE_IC_KONG_BACKEND_CANISTER_ID; + // How long the delegation identity should remain valid? // e.g. BigInt(60 * 60 * 1000 * 1000 * 1000) = 1 hour in nanoseconds export const AUTH_MAX_TIME_TO_LIVE = BigInt(60 * 60 * 1000 * 1000 * 1000); diff --git a/src/frontend/src/lib/types/api.ts b/src/frontend/src/lib/types/api.ts index f71d252c27..5d01d5df37 100644 --- a/src/frontend/src/lib/types/api.ts +++ b/src/frontend/src/lib/types/api.ts @@ -6,12 +6,14 @@ import type { UserProfile, Utxo } from '$declarations/backend/backend.did'; +import type { TxId } from '$declarations/kong_backend/kong_backend.did'; import type { BtcTxOutput, BitcoinNetwork as SignerBitcoinNetwork, Utxo as SignerUtxo } from '$declarations/signer/signer.did'; -import type { BtcAddress } from '$lib/types/address'; +import type { Address, BtcAddress } from '$lib/types/address'; +import type { Token } from '$lib/types/token'; import { Principal } from '@dfinity/principal'; export interface AddUserCredentialParams { @@ -51,3 +53,20 @@ export interface AddUserHiddenDappIdParams { dappId: string; currentUserVersion?: bigint; } + +export interface KongSwapAmountsParams { + sourceToken: Token; + destinationToken: Token; + sourceAmount: bigint; +} + +export interface KongSwapParams { + destinationToken: Token; + maxSlippage: number; + sendAmount: bigint; + referredBy?: string; + receiveAmount: bigint; + destinationAddress: Address; + sourceToken: Token; + payTransactionId: TxId; +} diff --git a/src/frontend/src/tests/lib/canisters/kong_backend.canister.spec.ts b/src/frontend/src/tests/lib/canisters/kong_backend.canister.spec.ts new file mode 100644 index 0000000000..750ce39da6 --- /dev/null +++ b/src/frontend/src/tests/lib/canisters/kong_backend.canister.spec.ts @@ -0,0 +1,235 @@ +import type { _SERVICE as KongBackendService } from '$declarations/kong_backend/kong_backend.did'; +import { + IC_CKETH_INDEX_CANISTER_ID, + IC_CKETH_LEDGER_CANISTER_ID, + IC_CKETH_MINTER_CANISTER_ID +} from '$env/networks/networks.icrc.env'; +import { ICP_TOKEN } from '$env/tokens/tokens.icp.env'; +import type { IcCkToken } from '$icp/types/ic-token'; +import { CanisterInternalError } from '$lib/canisters/errors'; +import { KongBackendCanister } from '$lib/canisters/kong_backend.canister'; +import type { CreateCanisterOptions } from '$lib/types/canister'; +import { mockIdentity } from '$tests/mocks/identity.mock'; +import { HttpAgent, type ActorSubclass } from '@dfinity/agent'; +import { Principal } from '@dfinity/principal'; +import { toNullable } from '@dfinity/utils'; +import { mock } from 'vitest-mock-extended'; + +vi.mock(import('$lib/constants/app.constants'), async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + LOCAL: false + }; +}); + +vi.mock(import('$lib/actors/agents.ic'), async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + // eslint-disable-next-line require-await + getAgent: async () => mock() + }; +}); + +describe('kong_backend.canister', () => { + const createKongBackendCanister = ({ + serviceOverride + }: Pick< + CreateCanisterOptions, + 'serviceOverride' + >): Promise => + KongBackendCanister.create({ + canisterId: Principal.fromText('l4lgk-raaaa-aaaar-qahpq-cai'), + identity: mockIdentity, + certifiedServiceOverride: serviceOverride, + serviceOverride + }); + const service = mock>(); + const mockResponseError = new Error('Test response error'); + + const sourceAmount = 1000000n; + const sourceToken = ICP_TOKEN; + const destinationToken = { + ...ICP_TOKEN, + standard: 'icrc', + ledgerCanisterId: IC_CKETH_LEDGER_CANISTER_ID, + indexCanisterId: IC_CKETH_INDEX_CANISTER_ID, + minterCanisterId: IC_CKETH_MINTER_CANISTER_ID + } as IcCkToken; + const swapAmountsParams = { + sourceAmount, + sourceToken, + destinationToken + }; + const swapParams = { + destinationToken, + maxSlippage: 1, + sendAmount: sourceAmount, + referredBy: 'referredBy', + receiveAmount: sourceAmount, + destinationAddress: 'destinationAddress', + sourceToken, + payTransactionId: { TransactionId: '1' } + }; + const errorResponse = { Err: 'Test error' }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('kongSwapAmounts', () => { + it('returns correct swap amounts', async () => { + const response = { + Ok: { + txs: [ + { + gas_fee: 10000n, + lp_fee: 112499994n, + pay_address: 'nppha-riaaa-aaaal-ajf2q-cai', + pay_amount: 10000000000000000000n, + pay_chain: 'IC', + pay_symbol: 'ICP', + pool_symbol: 'ICP_ckUSDT', + price: 0.00000373875, + receive_address: 'zdzgz-siaaa-aaaar-qaiba-cai', + receive_amount: 37387488131n, + receive_chain: 'IC', + receive_symbol: 'ckUSDT' + } + ], + mid_price: 7.5, + pay_address: 'nppha-riaaa-aaaal-ajf2q-cai', + pay_amount: 10000000000000000000n, + pay_chain: 'IC', + pay_symbol: 'ICP', + price: 0.000000373875, + receive_address: 'zdzgz-siaaa-aaaar-qaiba-cai', + receive_amount: 37387488131n, + receive_chain: 'IC', + receive_symbol: 'ckUSDT', + slippage: 100 + } + }; + service.swap_amounts.mockResolvedValue(response); + + const { swapAmounts } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = await swapAmounts(swapAmountsParams); + + expect(res).toEqual(response.Ok); + expect(service.swap_amounts).toHaveBeenCalledWith( + sourceToken.symbol, + sourceAmount, + destinationToken.symbol + ); + }); + + it('should throw an error if swap_amounts returns an error', async () => { + service.swap_amounts.mockResolvedValue(errorResponse); + + const { swapAmounts } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = swapAmounts(swapAmountsParams); + + await expect(res).rejects.toThrow(new CanisterInternalError(errorResponse.Err)); + }); + + it('should throw an error if swap_amounts throws', async () => { + service.swap_amounts.mockImplementation(() => { + throw mockResponseError; + }); + + const { swapAmounts } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = swapAmounts(swapAmountsParams); + + await expect(res).rejects.toThrow(mockResponseError); + }); + + it('should throw an error if swap_amounts returns an unexpected response', async () => { + // @ts-expect-error we test this in purposes + service.swap_amounts.mockResolvedValue({ test: 'unexpected' }); + + const { swapAmounts } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = swapAmounts(swapAmountsParams); + + await expect(res).rejects.toThrow(); + }); + }); + + describe('kongSwap', () => { + it('returns correct swap request id', async () => { + const response = { + Ok: 1000n + }; + service.swap_async.mockResolvedValue(response); + + const { swap } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = await swap(swapParams); + + expect(res).toEqual(response.Ok); + expect(service.swap_async).toHaveBeenCalledWith({ + pay_token: swapParams.sourceToken.symbol, + receive_token: swapParams.destinationToken.symbol, + pay_amount: swapParams.sendAmount, + max_slippage: toNullable(swapParams.maxSlippage), + receive_address: toNullable(swapParams.destinationAddress), + receive_amount: toNullable(swapParams.receiveAmount), + pay_tx_id: toNullable(swapParams.payTransactionId), + referred_by: toNullable(swapParams.referredBy) + }); + }); + + it('should throw an error if swap_async returns an error', async () => { + service.swap_async.mockResolvedValue(errorResponse); + + const { swap } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = swap(swapParams); + + await expect(res).rejects.toThrow(new CanisterInternalError(errorResponse.Err)); + }); + + it('should throw an error if swap_async throws', async () => { + service.swap_async.mockImplementation(() => { + throw mockResponseError; + }); + + const { swap } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = swap(swapParams); + + await expect(res).rejects.toThrow(mockResponseError); + }); + + it('should throw an error if swap_async returns an unexpected response', async () => { + // @ts-expect-error we test this in purposes + service.swap_async.mockResolvedValue({ test: 'unexpected' }); + + const { swap } = await createKongBackendCanister({ + serviceOverride: service + }); + + const res = swap(swapParams); + + await expect(res).rejects.toThrow(); + }); + }); +});