btc-wallet 0.3.11 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.js CHANGED
@@ -2722,6 +2722,73 @@ var btcRpcUrls = {
2722
2722
  testnet: "https://mempool.space/testnet/api"
2723
2723
  };
2724
2724
 
2725
+ // src/utils/nearUtils.ts
2726
+ import { providers } from "near-api-js";
2727
+ function nearCallFunction(contractId, methodName, args, options) {
2728
+ return __async(this, null, function* () {
2729
+ const nearProvider = (options == null ? void 0 : options.provider) || new providers.FailoverRpcProvider(
2730
+ nearRpcUrls[options == null ? void 0 : options.network].map(
2731
+ (url) => new providers.JsonRpcProvider({ url })
2732
+ )
2733
+ );
2734
+ const res = yield nearProvider.query({
2735
+ request_type: "call_function",
2736
+ account_id: contractId,
2737
+ method_name: methodName,
2738
+ args_base64: Buffer.from(JSON.stringify(args)).toString("base64"),
2739
+ finality: "final"
2740
+ });
2741
+ return JSON.parse(Buffer.from(res.result).toString());
2742
+ });
2743
+ }
2744
+ function pollTransactionStatuses(network, hashes) {
2745
+ return __async(this, null, function* () {
2746
+ const provider = new providers.FailoverRpcProvider(
2747
+ Object.values(nearRpcUrls[network]).map(
2748
+ (url) => new providers.JsonRpcProvider({ url })
2749
+ )
2750
+ );
2751
+ const maxAttempts = 30;
2752
+ let currentAttempt = 0;
2753
+ const pendingHashes = new Set(hashes);
2754
+ const results = /* @__PURE__ */ new Map();
2755
+ while (pendingHashes.size > 0 && currentAttempt < maxAttempts) {
2756
+ currentAttempt++;
2757
+ const promises = Array.from(pendingHashes).map((hash) => __async(this, null, function* () {
2758
+ try {
2759
+ const result = yield provider.txStatus(hash, "unused", "FINAL");
2760
+ if (result && result.status) {
2761
+ console.log(`Transaction ${hash} result:`, result);
2762
+ results.set(hash, result);
2763
+ pendingHashes.delete(hash);
2764
+ }
2765
+ } catch (error) {
2766
+ console.error(`Failed to fetch transaction status for ${hash}: ${error.message}`);
2767
+ }
2768
+ }));
2769
+ yield Promise.all(promises);
2770
+ if (pendingHashes.size > 0) {
2771
+ if (currentAttempt === maxAttempts) {
2772
+ throw new Error(
2773
+ `Transactions not found after max attempts: ${Array.from(pendingHashes).join(", ")}`
2774
+ );
2775
+ }
2776
+ console.log(
2777
+ `Waiting for ${pendingHashes.size} transactions, retrying ${maxAttempts - currentAttempt} more times`
2778
+ );
2779
+ yield delay(1e4);
2780
+ }
2781
+ }
2782
+ return hashes.map((hash) => results.get(hash));
2783
+ });
2784
+ }
2785
+
2786
+ // src/core/setupBTCWallet.ts
2787
+ import Big2 from "big.js";
2788
+
2789
+ // src/core/btcUtils.ts
2790
+ import Big from "big.js";
2791
+
2725
2792
  // src/utils/request.ts
2726
2793
  var cache = /* @__PURE__ */ new Map();
2727
2794
  var defaultCacheTimeout = 3e3;
@@ -2762,18 +2829,20 @@ function request(url, options) {
2762
2829
  if (!res.ok)
2763
2830
  throw new Error(res.statusText);
2764
2831
  const data = yield res.json();
2832
+ if (options == null ? void 0 : options.shouldStopPolling) {
2833
+ if (options.shouldStopPolling(data)) {
2834
+ return data;
2835
+ }
2836
+ throw new Error("Polling should continue");
2837
+ }
2765
2838
  if (cacheKey) {
2766
2839
  cache.set(cacheKey, { timestamp: Date.now(), data });
2767
2840
  setTimeout(() => {
2768
2841
  cache.delete(cacheKey);
2769
2842
  }, cacheTimeout);
2770
2843
  }
2771
- if ((options == null ? void 0 : options.shouldStopPolling) && options.shouldStopPolling(data)) {
2772
- return data;
2773
- }
2774
2844
  return data;
2775
2845
  } catch (err) {
2776
- console.error(err);
2777
2846
  if (retryCount > 0) {
2778
2847
  console.log(`Retrying... attempts left: ${retryCount}`);
2779
2848
  return request(url, __spreadProps(__spreadValues({}, options), { retryCount: retryCount - 1 }));
@@ -2787,74 +2856,471 @@ function request(url, options) {
2787
2856
  }));
2788
2857
  }
2789
2858
  }
2790
- return Promise.reject(err);
2859
+ throw err;
2791
2860
  }
2792
2861
  });
2793
2862
  }
2794
2863
 
2795
- // src/utils/nearUtils.ts
2796
- import { providers } from "near-api-js";
2797
- function nearCallFunction(contractId, methodName, args, options) {
2864
+ // src/utils/satoshi.ts
2865
+ function getNonce(url, accountId) {
2798
2866
  return __async(this, null, function* () {
2799
- const nearProvider = (options == null ? void 0 : options.provider) || new providers.FailoverRpcProvider(
2800
- nearRpcUrls[options == null ? void 0 : options.network].map(
2801
- (url) => new providers.JsonRpcProvider({ url })
2802
- )
2867
+ const { result_code, result_message, result_data } = yield request(
2868
+ `${url}/v1/nonce?csna=${accountId}`
2803
2869
  );
2804
- const res = yield nearProvider.query({
2805
- request_type: "call_function",
2806
- account_id: contractId,
2807
- method_name: methodName,
2808
- args_base64: Buffer.from(JSON.stringify(args)).toString("base64"),
2809
- finality: "final"
2870
+ if (result_code !== 0) {
2871
+ throw new Error(result_message);
2872
+ }
2873
+ return result_data;
2874
+ });
2875
+ }
2876
+ function getNearNonce(url, accountId) {
2877
+ return __async(this, null, function* () {
2878
+ const { result_code, result_message, result_data } = yield request(
2879
+ `${url}/v1/nonceNear?csna=${accountId}`
2880
+ );
2881
+ if (result_code !== 0) {
2882
+ throw new Error(result_message);
2883
+ }
2884
+ return result_data;
2885
+ });
2886
+ }
2887
+ function receiveTransaction(url, data) {
2888
+ return __async(this, null, function* () {
2889
+ const { result_code, result_message, result_data } = yield request(
2890
+ `${url}/v1/receiveTransaction`,
2891
+ {
2892
+ method: "POST",
2893
+ body: data
2894
+ }
2895
+ );
2896
+ if (result_code !== 0) {
2897
+ throw new Error(result_message);
2898
+ }
2899
+ return result_data;
2900
+ });
2901
+ }
2902
+ function receiveDepositMsg(_0, _1) {
2903
+ return __async(this, arguments, function* (url, {
2904
+ btcPublicKey,
2905
+ txHash,
2906
+ depositType = 1,
2907
+ postActions,
2908
+ extraMsg
2909
+ }) {
2910
+ const { result_code, result_message, result_data } = yield request(
2911
+ `${url}/v1/receiveDepositMsg`,
2912
+ {
2913
+ method: "POST",
2914
+ body: { btcPublicKey, txHash, depositType, postActions, extraMsg }
2915
+ }
2916
+ );
2917
+ console.log("receiveDepositMsg resp:", { result_code, result_message, result_data });
2918
+ if (result_code !== 0) {
2919
+ throw new Error(result_message);
2920
+ }
2921
+ return result_data;
2922
+ });
2923
+ }
2924
+ function checkBridgeTransactionStatus(url, txHash) {
2925
+ return __async(this, null, function* () {
2926
+ const { result_code, result_message, result_data } = yield request(`${url}/v1/bridgeFromTx?fromTxHash=${txHash}&fromChainId=1`, {
2927
+ timeout: 3e5,
2928
+ pollingInterval: 5e3,
2929
+ maxPollingAttempts: 30,
2930
+ shouldStopPolling: (res) => {
2931
+ var _a;
2932
+ return res.result_code === 0 && [4, 102].includes(((_a = res.result_data) == null ? void 0 : _a.status) || 0);
2933
+ }
2810
2934
  });
2811
- return JSON.parse(Buffer.from(res.result).toString());
2935
+ console.log("checkTransactionStatus resp:", { result_code, result_message, result_data });
2936
+ if ((result_data == null ? void 0 : result_data.status) !== 4) {
2937
+ throw new Error(result_message);
2938
+ }
2939
+ return result_data;
2812
2940
  });
2813
2941
  }
2814
- function pollTransactionStatuses(network, hashes) {
2942
+ function checkBtcTransactionStatus(url, sig) {
2815
2943
  return __async(this, null, function* () {
2816
- const provider = new providers.FailoverRpcProvider(
2817
- Object.values(nearRpcUrls[network]).map(
2818
- (url) => new providers.JsonRpcProvider({ url })
2819
- )
2944
+ const { result_code, result_message, result_data } = yield request(`${url}/v1/btcTx?sig=${sig}`, {
2945
+ timeout: 3e5,
2946
+ pollingInterval: 5e3,
2947
+ maxPollingAttempts: 30,
2948
+ shouldStopPolling: (res) => {
2949
+ var _a;
2950
+ return res.result_code === 0 && [3, 101, 102].includes(((_a = res.result_data) == null ? void 0 : _a.status) || 0);
2951
+ }
2952
+ });
2953
+ console.log("checkBtcTransactionStatus resp:", { result_code, result_message, result_data });
2954
+ if ((result_data == null ? void 0 : result_data.status) !== 3) {
2955
+ throw new Error(result_message);
2956
+ }
2957
+ return result_data;
2958
+ });
2959
+ }
2960
+
2961
+ // src/core/btcUtils.ts
2962
+ function getBtcProvider() {
2963
+ if (typeof window === "undefined" || !window.btcContext) {
2964
+ throw new Error("BTC Provider is not initialized.");
2965
+ }
2966
+ return window.btcContext;
2967
+ }
2968
+ function getNetwork() {
2969
+ return __async(this, null, function* () {
2970
+ const network = yield getBtcProvider().getNetwork();
2971
+ console.log("btc network:", network);
2972
+ return network === "livenet" ? "mainnet" : "testnet";
2973
+ });
2974
+ }
2975
+ function getBtcRpcUrl() {
2976
+ return __async(this, null, function* () {
2977
+ const network = yield getNetwork();
2978
+ return btcRpcUrls[network];
2979
+ });
2980
+ }
2981
+ function getConfig(isDev) {
2982
+ return __async(this, null, function* () {
2983
+ const network = yield getNetwork();
2984
+ return walletConfig[isDev ? "dev" : network];
2985
+ });
2986
+ }
2987
+ function nearCall(contractId, methodName, args) {
2988
+ return __async(this, null, function* () {
2989
+ const network = yield getNetwork();
2990
+ return nearCallFunction(contractId, methodName, args, { network });
2991
+ });
2992
+ }
2993
+ function getBtcGasPrice() {
2994
+ return __async(this, null, function* () {
2995
+ const defaultFeeRate = 100;
2996
+ try {
2997
+ const btcRpcUrl = yield getBtcRpcUrl();
2998
+ const res = yield fetch(`${btcRpcUrl}/v1/fees/recommended`).then((res2) => res2.json());
2999
+ const feeRate = res.fastestFee;
3000
+ return feeRate || defaultFeeRate;
3001
+ } catch (error) {
3002
+ return defaultFeeRate;
3003
+ }
3004
+ });
3005
+ }
3006
+ function getBtcBalance() {
3007
+ return __async(this, null, function* () {
3008
+ const { account } = yield retryOperation(getBtcProvider, (res) => !!res.account);
3009
+ if (!account) {
3010
+ console.error("BTC Account is not available.");
3011
+ return { rawBalance: 0, balance: 0, maxSpendableBalance: 0 };
3012
+ }
3013
+ const btcRpcUrl = yield getBtcRpcUrl();
3014
+ const utxos = yield fetch(`${btcRpcUrl}/address/${account}/utxo`).then((res) => res.json());
3015
+ const rawBalance = (utxos == null ? void 0 : utxos.reduce((acc, cur) => acc + cur.value, 0)) || 0;
3016
+ const balance = rawBalance / __pow(10, 8);
3017
+ const feeRate = yield getBtcGasPrice();
3018
+ const inputSize = ((utxos == null ? void 0 : utxos.length) || 0) * 66;
3019
+ const outputSize = 34;
3020
+ const overheadSize = 10;
3021
+ const estimatedTxSize = inputSize + outputSize + overheadSize;
3022
+ const estimatedFee = estimatedTxSize * feeRate / __pow(10, 8);
3023
+ console.log("estimated fee:", estimatedFee);
3024
+ const availableBalance = Math.max(0, balance - estimatedFee);
3025
+ return {
3026
+ rawBalance,
3027
+ balance,
3028
+ availableBalance
3029
+ };
3030
+ });
3031
+ }
3032
+ function sendBitcoin(address, amount, feeRate) {
3033
+ return __async(this, null, function* () {
3034
+ const { sendBitcoin: sendBitcoin2 } = getBtcProvider();
3035
+ const txHash = yield sendBitcoin2(address, amount, { feeRate });
3036
+ return txHash;
3037
+ });
3038
+ }
3039
+ function estimateDepositAmount(amount, option) {
3040
+ return __async(this, null, function* () {
3041
+ const config = yield getConfig((option == null ? void 0 : option.isDev) || false);
3042
+ const {
3043
+ deposit_bridge_fee: { fee_min, fee_rate }
3044
+ } = yield nearCall(
3045
+ config.bridgeContractId,
3046
+ "get_config",
3047
+ {}
2820
3048
  );
2821
- const maxAttempts = 30;
2822
- let currentAttempt = 0;
2823
- const pendingHashes = new Set(hashes);
2824
- const results = /* @__PURE__ */ new Map();
2825
- while (pendingHashes.size > 0 && currentAttempt < maxAttempts) {
2826
- currentAttempt++;
2827
- const promises = Array.from(pendingHashes).map((hash) => __async(this, null, function* () {
2828
- try {
2829
- const result = yield provider.txStatus(hash, "unused", "FINAL");
2830
- if (result && result.status) {
2831
- console.log(`Transaction ${hash} result:`, result);
2832
- results.set(hash, result);
2833
- pendingHashes.delete(hash);
2834
- }
2835
- } catch (error) {
2836
- console.error(`Failed to fetch transaction status for ${hash}: ${error.message}`);
2837
- }
2838
- }));
2839
- yield Promise.all(promises);
2840
- if (pendingHashes.size > 0) {
2841
- if (currentAttempt === maxAttempts) {
2842
- throw new Error(
2843
- `Transactions not found after max attempts: ${Array.from(pendingHashes).join(", ")}`
2844
- );
3049
+ const fee = Math.max(Number(fee_min), Number(amount) * fee_rate);
3050
+ return new Big(amount).minus(fee).toFixed(0);
3051
+ });
3052
+ }
3053
+ function executeBTCDepositAndAction(_0) {
3054
+ return __async(this, arguments, function* ({
3055
+ action,
3056
+ feeRate,
3057
+ isDev = false
3058
+ }) {
3059
+ try {
3060
+ const { getPublicKey } = getBtcProvider();
3061
+ const config = yield getConfig(isDev);
3062
+ const btcPublicKey = yield getPublicKey();
3063
+ if (!btcPublicKey) {
3064
+ throw new Error("BTC Public Key is not available.");
3065
+ }
3066
+ if (!action.receiver_id) {
3067
+ throw new Error("receiver_id is required");
3068
+ }
3069
+ if (!action.amount) {
3070
+ throw new Error("amount is required");
3071
+ }
3072
+ const csna = yield nearCall(
3073
+ config.accountContractId,
3074
+ "get_chain_signature_near_account_id",
3075
+ {
3076
+ btc_public_key: btcPublicKey
2845
3077
  }
2846
- console.log(
2847
- `Waiting for ${pendingHashes.size} transactions, retrying ${maxAttempts - currentAttempt} more times`
2848
- );
2849
- yield delay(1e4);
3078
+ );
3079
+ const depositMsg = {
3080
+ recipient_id: csna,
3081
+ post_actions: [
3082
+ __spreadProps(__spreadValues({}, action), {
3083
+ gas: new Big(100).mul(__pow(10, 12)).toFixed(0)
3084
+ })
3085
+ ]
3086
+ };
3087
+ const storageDepositMsg = {};
3088
+ const accountInfo = yield nearCall(
3089
+ config.accountContractId,
3090
+ "get_account",
3091
+ {
3092
+ account_id: csna
3093
+ }
3094
+ );
3095
+ if (!(accountInfo == null ? void 0 : accountInfo.nonce)) {
3096
+ storageDepositMsg.btc_public_key = btcPublicKey;
3097
+ }
3098
+ const registerRes = yield nearCall(action.receiver_id, "storage_balance_of", {
3099
+ account_id: csna
3100
+ });
3101
+ if (!(registerRes == null ? void 0 : registerRes.available)) {
3102
+ storageDepositMsg.storage_deposit_msg = {
3103
+ contract_id: action.receiver_id,
3104
+ deposit: new Big(0.25).mul(__pow(10, 24)).toFixed(0),
3105
+ registration_only: true
3106
+ };
3107
+ }
3108
+ if (Object.keys(storageDepositMsg).length > 0) {
3109
+ depositMsg.extra_msg = JSON.stringify(storageDepositMsg);
2850
3110
  }
3111
+ console.log("get_user_deposit_address params:", { deposit_msg: depositMsg });
3112
+ const userDepositAddress = yield nearCall(
3113
+ config.bridgeContractId,
3114
+ "get_user_deposit_address",
3115
+ { deposit_msg: depositMsg }
3116
+ );
3117
+ const _feeRate = feeRate || (yield getBtcGasPrice());
3118
+ const minDepositAmount = 5e3;
3119
+ const sendAmount = Math.max(minDepositAmount, new Big(action.amount).toNumber());
3120
+ console.log("user deposit address:", userDepositAddress);
3121
+ console.log("send amount:", sendAmount);
3122
+ console.log("fee rate:", _feeRate);
3123
+ const txHash = yield sendBitcoin(userDepositAddress, sendAmount, _feeRate);
3124
+ yield receiveDepositMsg(config.base_url, {
3125
+ btcPublicKey,
3126
+ txHash,
3127
+ postActions: JSON.stringify(depositMsg.post_actions),
3128
+ extraMsg: depositMsg.extra_msg
3129
+ });
3130
+ const checkTransactionStatusRes = yield checkBridgeTransactionStatus(config.base_url, txHash);
3131
+ console.log("checkBridgeTransactionStatus resp:", checkTransactionStatusRes);
3132
+ const network = yield getNetwork();
3133
+ const result = yield pollTransactionStatuses(network, [checkTransactionStatusRes.ToTxHash]);
3134
+ return result;
3135
+ } catch (error) {
3136
+ console.error("executeBTCDepositAndAction error:", error);
3137
+ throw error;
2851
3138
  }
2852
- return hashes.map((hash) => results.get(hash));
2853
3139
  });
2854
3140
  }
2855
3141
 
3142
+ // src/utils/Dialog.ts
3143
+ var Dialog = class {
3144
+ static injectStyles() {
3145
+ if (!document.querySelector("#dialog-styles")) {
3146
+ const styleSheet = document.createElement("style");
3147
+ styleSheet.id = "dialog-styles";
3148
+ styleSheet.textContent = this.style;
3149
+ document.head.appendChild(styleSheet);
3150
+ }
3151
+ }
3152
+ static confirm(options) {
3153
+ return new Promise((resolve) => {
3154
+ this.injectStyles();
3155
+ const container = document.createElement("div");
3156
+ container.innerHTML = this.template;
3157
+ document.body.appendChild(container);
3158
+ const titleEl = container.querySelector(".dialog-title");
3159
+ const messageEl = container.querySelector(".dialog-message");
3160
+ const confirmBtn = container.querySelector(".dialog-confirm-btn");
3161
+ const cancelBtn = container.querySelector(".dialog-cancel-btn");
3162
+ if (options.title) {
3163
+ titleEl.textContent = options.title;
3164
+ } else {
3165
+ titleEl.style.display = "none";
3166
+ }
3167
+ messageEl.textContent = options.message;
3168
+ const cleanup = () => {
3169
+ document.body.removeChild(container);
3170
+ };
3171
+ confirmBtn.addEventListener("click", () => {
3172
+ cleanup();
3173
+ resolve(true);
3174
+ });
3175
+ cancelBtn.addEventListener("click", () => {
3176
+ cleanup();
3177
+ resolve(false);
3178
+ });
3179
+ });
3180
+ }
3181
+ static alert(options) {
3182
+ return new Promise((resolve) => {
3183
+ this.injectStyles();
3184
+ const container = document.createElement("div");
3185
+ container.innerHTML = this.template;
3186
+ document.body.appendChild(container);
3187
+ const titleEl = container.querySelector(".dialog-title");
3188
+ const messageEl = container.querySelector(".dialog-message");
3189
+ const confirmBtn = container.querySelector(".dialog-confirm-btn");
3190
+ const cancelBtn = container.querySelector(".dialog-cancel-btn");
3191
+ if (options.title) {
3192
+ titleEl.textContent = options.title;
3193
+ } else {
3194
+ titleEl.style.display = "none";
3195
+ }
3196
+ messageEl.textContent = options.message;
3197
+ cancelBtn.style.display = "none";
3198
+ const cleanup = () => {
3199
+ document.body.removeChild(container);
3200
+ };
3201
+ confirmBtn.addEventListener("click", () => {
3202
+ cleanup();
3203
+ resolve();
3204
+ });
3205
+ });
3206
+ }
3207
+ };
3208
+ Dialog.template = `
3209
+ <div class="dialog-overlay">
3210
+ <div class="dialog-container">
3211
+ <div class="dialog-content">
3212
+ <div class="dialog-title"></div>
3213
+ <div class="dialog-message"></div>
3214
+ <div class="dialog-buttons">
3215
+ <button class="dialog-cancel-btn">Cancel</button>
3216
+ <button class="dialog-confirm-btn">Confirm</button>
3217
+ </div>
3218
+ </div>
3219
+ </div>
3220
+ </div>
3221
+ `;
3222
+ Dialog.style = `
3223
+ .dialog-overlay {
3224
+ position: fixed;
3225
+ top: 0;
3226
+ left: 0;
3227
+ right: 0;
3228
+ bottom: 0;
3229
+ background-color: rgba(0, 0, 0, 0.75);
3230
+ display: flex;
3231
+ align-items: center;
3232
+ justify-content: center;
3233
+ z-index: 999999;
3234
+ backdrop-filter: blur(4px);
3235
+ }
3236
+ .dialog-container {
3237
+ background: #21232f;
3238
+ border-radius: 12px;
3239
+ padding: 24px;
3240
+ width: 350px;
3241
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
3242
+ }
3243
+ .dialog-title {
3244
+ font-size: 18px;
3245
+ font-weight: 600;
3246
+ margin-bottom: 16px;
3247
+ color: #ffffff;
3248
+ }
3249
+ .dialog-message {
3250
+ margin-bottom: 24px;
3251
+ line-height: 1.6;
3252
+ color: rgba(255, 255, 255, 0.8);
3253
+ font-size: 14px;
3254
+ }
3255
+ .dialog-buttons {
3256
+ display: flex;
3257
+ justify-content: flex-end;
3258
+ gap: 12px;
3259
+ }
3260
+ .dialog-confirm-btn {
3261
+ padding: 8px 24px;
3262
+ background-color: #ff7a00;
3263
+ color: white;
3264
+ border: none;
3265
+ border-radius: 6px;
3266
+ cursor: pointer;
3267
+ font-size: 14px;
3268
+ font-weight: 500;
3269
+ transition: all 0.2s ease;
3270
+ }
3271
+ .dialog-confirm-btn:hover {
3272
+ background-color: #ff8f1f;
3273
+ transform: translateY(-1px);
3274
+ }
3275
+ .dialog-confirm-btn:active {
3276
+ transform: translateY(0);
3277
+ }
3278
+ .dialog-cancel-btn {
3279
+ padding: 8px 24px;
3280
+ background-color: rgba(255, 255, 255, 0.1);
3281
+ color: rgba(255, 255, 255, 0.8);
3282
+ border: none;
3283
+ border-radius: 6px;
3284
+ cursor: pointer;
3285
+ font-size: 14px;
3286
+ font-weight: 500;
3287
+ transition: all 0.2s ease;
3288
+ }
3289
+ .dialog-cancel-btn:hover {
3290
+ background-color: rgba(255, 255, 255, 0.15);
3291
+ transform: translateY(-1px);
3292
+ }
3293
+ .dialog-cancel-btn:active {
3294
+ transform: translateY(0);
3295
+ }
3296
+
3297
+ .dialog-overlay {
3298
+ animation: fadeIn 0.2s ease;
3299
+ }
3300
+ .dialog-container {
3301
+ animation: slideIn 0.2s ease;
3302
+ }
3303
+ @keyframes fadeIn {
3304
+ from {
3305
+ opacity: 0;
3306
+ }
3307
+ to {
3308
+ opacity: 1;
3309
+ }
3310
+ }
3311
+ @keyframes slideIn {
3312
+ from {
3313
+ transform: translateY(-20px);
3314
+ opacity: 0;
3315
+ }
3316
+ to {
3317
+ transform: translateY(0);
3318
+ opacity: 1;
3319
+ }
3320
+ }
3321
+ `;
3322
+
2856
3323
  // src/core/setupBTCWallet.ts
2857
- import Big from "big.js";
2858
3324
  var { transfer, functionCall } = actionCreators;
2859
3325
  var state = {
2860
3326
  saveAccount(account) {
@@ -2904,6 +3370,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
2904
3370
  id,
2905
3371
  provider
2906
3372
  }) {
3373
+ var _a;
2907
3374
  const wallet = {
2908
3375
  signIn,
2909
3376
  signOut,
@@ -2913,8 +3380,9 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
2913
3380
  signAndSendTransaction,
2914
3381
  signAndSendTransactions
2915
3382
  };
2916
- const currentConfig = "isDev" in metadata && metadata.isDev ? walletConfig.dev : walletConfig[options.network.networkId];
2917
- const walletNetwork = "isDev" in metadata && metadata.isDev ? "dev" : options.network.networkId;
3383
+ const isDev = (_a = "isDev" in metadata && metadata.isDev) != null ? _a : false;
3384
+ const currentConfig = isDev ? walletConfig.dev : walletConfig[options.network.networkId];
3385
+ const walletNetwork = isDev ? "dev" : options.network.networkId;
2918
3386
  initWalletButton(walletNetwork, wallet);
2919
3387
  if (!inter) {
2920
3388
  inter = setInterval(() => __async(void 0, null, function* () {
@@ -3055,9 +3523,15 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3055
3523
  return __async(this, null, function* () {
3056
3524
  const btcContext = window.btcContext;
3057
3525
  const accountId = state.getAccount();
3526
+ const accountInfo = yield getAccountInfo();
3527
+ yield checkGasTokenArrears(accountInfo.debt_info);
3058
3528
  const trans = [...params.transactions];
3059
3529
  console.log("raw trans:", trans);
3060
- const { transferGasTransaction, useNearPayGas, gasLimit } = yield calculateGasStrategy(trans);
3530
+ const gasTokenBalance = accountInfo.gas_token[currentConfig.token] || "0";
3531
+ const { transferGasTransaction, useNearPayGas, gasLimit } = yield calculateGasStrategy(
3532
+ gasTokenBalance,
3533
+ trans
3534
+ );
3061
3535
  console.log("transferGasTransaction:", transferGasTransaction);
3062
3536
  console.log("useNearPayGas:", useNearPayGas);
3063
3537
  console.log("gasLimit:", gasLimit);
@@ -3068,11 +3542,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3068
3542
  const newTrans = yield Promise.all(
3069
3543
  trans.map((transaction, index) => convertTransactionToTxHex(transaction, index))
3070
3544
  );
3071
- const { result_data: nonceFromApi } = yield getNonceFromApi(
3072
- currentConfig.base_url,
3073
- accountId
3074
- );
3075
- const accountInfo = yield getAccountInfo();
3545
+ const nonceFromApi = yield getNonce(currentConfig.base_url, accountId);
3076
3546
  const nonce = Number(nonceFromApi) > Number(accountInfo.nonce) ? String(nonceFromApi) : String(accountInfo.nonce);
3077
3547
  const intention = {
3078
3548
  chain_id: "397",
@@ -3085,29 +3555,48 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3085
3555
  };
3086
3556
  const strIntention = JSON.stringify(intention);
3087
3557
  const signature = yield btcContext.signMessage(strIntention);
3088
- const result = yield uploadBTCTx(currentConfig.base_url, {
3558
+ yield receiveTransaction(currentConfig.base_url, {
3089
3559
  sig: signature,
3090
3560
  btcPubKey: state.getBtcPublicKey(),
3091
3561
  data: toHex(strIntention)
3092
3562
  });
3093
- if (result.result_code === 0) {
3094
- const hash = newTrans.map((t) => t.hash);
3095
- console.log("txHash:", hash);
3096
- const result2 = yield pollTransactionStatuses(options.network.networkId, hash);
3097
- return result2;
3563
+ yield checkBtcTransactionStatus(currentConfig.base_url, signature);
3564
+ const hash = newTrans.map((t) => t.hash);
3565
+ console.log("txHash:", hash);
3566
+ const result = yield pollTransactionStatuses(options.network.networkId, hash);
3567
+ return result;
3568
+ });
3569
+ }
3570
+ function checkGasTokenArrears(debtInfo) {
3571
+ return __async(this, null, function* () {
3572
+ const transferAmount = (debtInfo == null ? void 0 : debtInfo.transfer_amount) || "0";
3573
+ console.log("get_account debtInfo:", debtInfo);
3574
+ if (transferAmount === "0")
3575
+ return;
3576
+ const confirmed = yield Dialog.confirm({
3577
+ title: "Has gas token arrears",
3578
+ message: "You have gas token arrears, please deposit gas token to continue."
3579
+ });
3580
+ if (confirmed) {
3581
+ const action = {
3582
+ receiver_id: currentConfig.token,
3583
+ amount: transferAmount,
3584
+ msg: JSON.stringify("Deposit")
3585
+ };
3586
+ yield executeBTCDepositAndAction({ action, isDev });
3587
+ yield Dialog.alert({
3588
+ title: "Deposit success",
3589
+ message: "Deposit success, will continue to execute transaction."
3590
+ });
3098
3591
  } else {
3099
- return null;
3592
+ throw new Error("Deposit failed, please deposit gas token first.");
3100
3593
  }
3101
3594
  });
3102
3595
  }
3103
3596
  function getAccountInfo() {
3104
3597
  return __async(this, null, function* () {
3105
3598
  const accountId = state.getAccount();
3106
- const accountInfo = yield nearCall2(
3107
- currentConfig.accountContractId,
3108
- "get_account",
3109
- { account_id: accountId }
3110
- );
3599
+ const accountInfo = yield nearCall2(currentConfig.accountContractId, "get_account", { account_id: accountId });
3111
3600
  return accountInfo;
3112
3601
  });
3113
3602
  }
@@ -3126,7 +3615,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3126
3615
  amount,
3127
3616
  msg: JSON.stringify("Deposit")
3128
3617
  },
3129
- gas: new Big(50).mul(__pow(10, 12)).toFixed(0),
3618
+ gas: new Big2(50).mul(__pow(10, 12)).toFixed(0),
3130
3619
  deposit: "1"
3131
3620
  }
3132
3621
  }
@@ -3139,7 +3628,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3139
3628
  const { txHex: transferTxHex } = yield convertTransactionToTxHex(transferTx);
3140
3629
  let newGasLimit;
3141
3630
  if (useNearPayGas && perTxFee) {
3142
- newGasLimit = new Big(perTxFee).mul(transactions2.length + 1).toFixed(0);
3631
+ newGasLimit = new Big2(perTxFee).mul(transactions2.length + 1).toFixed(0);
3143
3632
  } else {
3144
3633
  newGasLimit = yield getPredictedGasAmount(
3145
3634
  currentConfig.accountContractId,
@@ -3157,14 +3646,14 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3157
3646
  gas_token_id: tokenId,
3158
3647
  near_transactions: transactions2
3159
3648
  });
3160
- const predictedGasAmount = new Big(predictedGas).mul(1.2).toFixed(0);
3649
+ const predictedGasAmount = new Big2(predictedGas).mul(1.2).toFixed(0);
3161
3650
  console.log("predictedGas:", predictedGasAmount);
3162
3651
  return predictedGasAmount;
3163
3652
  });
3164
3653
  }
3165
- function calculateGasStrategy(transactions2) {
3654
+ function calculateGasStrategy(gasTokenBalance, transactions2) {
3166
3655
  return __async(this, null, function* () {
3167
- var _a;
3656
+ var _a2;
3168
3657
  const accountId = state.getAccount();
3169
3658
  const nearAccount = yield provider.query({
3170
3659
  request_type: "view_account",
@@ -3173,8 +3662,6 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3173
3662
  });
3174
3663
  const availableBalance = parseFloat(nearAccount.amount) / __pow(10, 24);
3175
3664
  console.log("available near balance:", availableBalance);
3176
- const accountInfo = yield getAccountInfo();
3177
- const gasTokenBalance = accountInfo.gas_token[currentConfig.token] || "0";
3178
3665
  console.log("available gas token balance:", gasTokenBalance);
3179
3666
  const convertTx = yield Promise.all(
3180
3667
  transactions2.map((transaction, index) => convertTransactionToTxHex(transaction, index))
@@ -3187,13 +3674,13 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3187
3674
  );
3188
3675
  console.log("list_gas_token gas tokens:", gasTokens);
3189
3676
  const perTxFee = Math.max(
3190
- Number(((_a = gasTokens[currentConfig.token]) == null ? void 0 : _a.per_tx_protocol_fee) || 0),
3677
+ Number(((_a2 = gasTokens[currentConfig.token]) == null ? void 0 : _a2.per_tx_protocol_fee) || 0),
3191
3678
  100
3192
3679
  );
3193
3680
  console.log("perTxFee:", perTxFee);
3194
- const protocolFee = new Big(perTxFee || "0").mul(convertTx.length).toFixed(0);
3681
+ const protocolFee = new Big2(perTxFee || "0").mul(convertTx.length).toFixed(0);
3195
3682
  console.log("protocolFee:", protocolFee);
3196
- if (new Big(gasTokenBalance).gte(protocolFee)) {
3683
+ if (new Big2(gasTokenBalance).gte(protocolFee)) {
3197
3684
  console.log("use near pay gas and enough gas token balance");
3198
3685
  return { useNearPayGas: true, gasLimit: protocolFee };
3199
3686
  } else {
@@ -3208,7 +3695,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3208
3695
  currentConfig.token,
3209
3696
  convertTx.map((t) => t.txHex)
3210
3697
  );
3211
- if (new Big(gasTokenBalance).gte(adjustedGas)) {
3698
+ if (new Big2(gasTokenBalance).gte(adjustedGas)) {
3212
3699
  console.log("use gas token and gas token balance is enough");
3213
3700
  return { useNearPayGas: false, gasLimit: adjustedGas };
3214
3701
  } else {
@@ -3236,10 +3723,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3236
3723
  const accessKey = __spreadProps(__spreadValues({}, rawAccessKey), {
3237
3724
  nonce: BigInt(rawAccessKey.nonce || 0)
3238
3725
  });
3239
- const { result_data: nearNonceFromApi } = yield getNearNonceFromApi(
3240
- currentConfig.base_url,
3241
- accountId
3242
- );
3726
+ const nearNonceFromApi = yield getNearNonce(currentConfig.base_url, accountId);
3243
3727
  let nearNonceNumber = accessKey.nonce + BigInt(1);
3244
3728
  if (nearNonceFromApi) {
3245
3729
  nearNonceNumber = BigInt(nearNonceFromApi) > nearNonceNumber ? BigInt(nearNonceFromApi) : nearNonceNumber;
@@ -3293,18 +3777,6 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3293
3777
  }
3294
3778
  return wallet;
3295
3779
  });
3296
- function getNonceFromApi(url, accountId) {
3297
- return request(`${url}/v1/nonce?csna=${accountId}`);
3298
- }
3299
- function getNearNonceFromApi(url, accountId) {
3300
- return request(`${url}/v1/nonceNear?csna=${accountId}`);
3301
- }
3302
- function uploadBTCTx(url, data) {
3303
- return request(`${url}/v1/receiveTransaction`, {
3304
- method: "POST",
3305
- body: data
3306
- });
3307
- }
3308
3780
  function toHex(originalString) {
3309
3781
  const charArray = originalString.split("");
3310
3782
  const asciiArray = charArray.map((char) => char.charCodeAt(0));
@@ -3341,228 +3813,9 @@ function setupBTCWallet({
3341
3813
  return btcWallet;
3342
3814
  }
3343
3815
 
3344
- // src/core/btcUtils.ts
3345
- import Big2 from "big.js";
3346
- function getBtcProvider() {
3347
- if (typeof window === "undefined" || !window.btcContext) {
3348
- throw new Error("BTC Provider is not initialized.");
3349
- }
3350
- return window.btcContext;
3351
- }
3352
- function getNetwork() {
3353
- return __async(this, null, function* () {
3354
- const network = yield getBtcProvider().getNetwork();
3355
- console.log("btc network:", network);
3356
- return network === "livenet" ? "mainnet" : "testnet";
3357
- });
3358
- }
3359
- function getBtcRpcUrl() {
3360
- return __async(this, null, function* () {
3361
- const network = yield getNetwork();
3362
- return btcRpcUrls[network];
3363
- });
3364
- }
3365
- function getConfig(isDev) {
3366
- return __async(this, null, function* () {
3367
- const network = yield getNetwork();
3368
- return walletConfig[isDev ? "dev" : network];
3369
- });
3370
- }
3371
- function nearCall(contractId, methodName, args) {
3372
- return __async(this, null, function* () {
3373
- const network = yield getNetwork();
3374
- return nearCallFunction(contractId, methodName, args, { network });
3375
- });
3376
- }
3377
- function receiveDepositMsg(_0, _1) {
3378
- return __async(this, arguments, function* (baseUrl, {
3379
- btcPublicKey,
3380
- txHash,
3381
- depositType = 1,
3382
- postActions,
3383
- extraMsg
3384
- }) {
3385
- const res = yield request(`${baseUrl}/v1/receiveDepositMsg`, {
3386
- method: "POST",
3387
- body: { btcPublicKey, txHash, depositType, postActions, extraMsg }
3388
- });
3389
- console.log("receiveDepositMsg resp:", res);
3390
- return res;
3391
- });
3392
- }
3393
- function checkTransactionStatus(baseUrl, txHash) {
3394
- return __async(this, null, function* () {
3395
- const res = yield request(
3396
- `${baseUrl}/v1/bridgeFromTx?fromTxHash=${txHash}&fromChainId=1`,
3397
- {
3398
- timeout: 6e4,
3399
- pollingInterval: 5e3,
3400
- maxPollingAttempts: 10,
3401
- shouldStopPolling: (res2) => res2.result_code === 0
3402
- }
3403
- );
3404
- return res;
3405
- });
3406
- }
3407
- function getBtcGasPrice() {
3408
- return __async(this, null, function* () {
3409
- const defaultFeeRate = 100;
3410
- try {
3411
- const btcRpcUrl = yield getBtcRpcUrl();
3412
- const res = yield fetch(`${btcRpcUrl}/v1/fees/recommended`).then((res2) => res2.json());
3413
- const feeRate = res.fastestFee;
3414
- return feeRate || defaultFeeRate;
3415
- } catch (error) {
3416
- return defaultFeeRate;
3417
- }
3418
- });
3419
- }
3420
- function getBtcBalance() {
3421
- return __async(this, null, function* () {
3422
- const { account } = yield retryOperation(getBtcProvider, (res) => !!res.account);
3423
- if (!account) {
3424
- console.error("BTC Account is not available.");
3425
- return { rawBalance: 0, balance: 0, maxSpendableBalance: 0 };
3426
- }
3427
- const btcRpcUrl = yield getBtcRpcUrl();
3428
- const utxos = yield fetch(`${btcRpcUrl}/address/${account}/utxo`).then((res) => res.json());
3429
- const rawBalance = (utxos == null ? void 0 : utxos.reduce((acc, cur) => acc + cur.value, 0)) || 0;
3430
- const balance = rawBalance / __pow(10, 8);
3431
- const feeRate = yield getBtcGasPrice();
3432
- const inputSize = ((utxos == null ? void 0 : utxos.length) || 0) * 66;
3433
- const outputSize = 34;
3434
- const overheadSize = 10;
3435
- const estimatedTxSize = inputSize + outputSize + overheadSize;
3436
- const estimatedFee = estimatedTxSize * feeRate / __pow(10, 8);
3437
- console.log("estimated fee:", estimatedFee);
3438
- const availableBalance = Math.max(0, balance - estimatedFee);
3439
- return {
3440
- rawBalance,
3441
- balance,
3442
- availableBalance
3443
- };
3444
- });
3445
- }
3446
- function sendBitcoin(address, amount, feeRate) {
3447
- return __async(this, null, function* () {
3448
- const { sendBitcoin: sendBitcoin2 } = getBtcProvider();
3449
- const txHash = yield sendBitcoin2(address, amount, { feeRate });
3450
- return txHash;
3451
- });
3452
- }
3453
- function estimateDepositAmount(amount, option) {
3454
- return __async(this, null, function* () {
3455
- const config = yield getConfig((option == null ? void 0 : option.isDev) || false);
3456
- const {
3457
- deposit_bridge_fee: { fee_min, fee_rate }
3458
- } = yield nearCall(
3459
- config.bridgeContractId,
3460
- "get_config",
3461
- {}
3462
- );
3463
- const fee = Math.max(Number(fee_min), Number(amount) * fee_rate);
3464
- return new Big2(amount).minus(fee).toFixed(0);
3465
- });
3466
- }
3467
- function executeBTCDepositAndAction(_0) {
3468
- return __async(this, arguments, function* ({
3469
- action,
3470
- feeRate,
3471
- isDev = false
3472
- }) {
3473
- try {
3474
- const { getPublicKey } = getBtcProvider();
3475
- const config = yield getConfig(isDev);
3476
- const btcPublicKey = yield getPublicKey();
3477
- const _action = Object.assign(
3478
- {},
3479
- __spreadProps(__spreadValues({}, action), {
3480
- gas: new Big2(100).mul(__pow(10, 12)).toFixed(0)
3481
- })
3482
- );
3483
- if (!btcPublicKey) {
3484
- throw new Error("BTC Public Key is not available.");
3485
- }
3486
- if (!_action.receiver_id) {
3487
- throw new Error("action.receiver_id is required");
3488
- }
3489
- const amountWithFee = yield estimateDepositAmount(_action.amount, {
3490
- isDev
3491
- });
3492
- _action.amount = amountWithFee;
3493
- if (!_action.amount || !new Big2(_action.amount || 0).gt(0)) {
3494
- throw new Error("action.amount is required or deposit amount is not enough");
3495
- }
3496
- const csna = yield nearCall(
3497
- config.accountContractId,
3498
- "get_chain_signature_near_account_id",
3499
- {
3500
- btc_public_key: btcPublicKey
3501
- }
3502
- );
3503
- const depositMsg = {
3504
- recipient_id: csna,
3505
- post_actions: [_action]
3506
- };
3507
- const storageDepositMsg = {};
3508
- const accountInfo = yield nearCall(
3509
- config.accountContractId,
3510
- "get_account",
3511
- {
3512
- account_id: csna
3513
- }
3514
- );
3515
- if (!(accountInfo == null ? void 0 : accountInfo.nonce)) {
3516
- storageDepositMsg.btc_public_key = btcPublicKey;
3517
- }
3518
- const registerRes = yield nearCall(action.receiver_id, "storage_balance_of", {
3519
- account_id: csna
3520
- });
3521
- if (!(registerRes == null ? void 0 : registerRes.available)) {
3522
- storageDepositMsg.storage_deposit_msg = {
3523
- contract_id: action.receiver_id,
3524
- deposit: new Big2(0.25).mul(__pow(10, 24)).toFixed(0),
3525
- registration_only: true
3526
- };
3527
- }
3528
- if (Object.keys(storageDepositMsg).length > 0) {
3529
- depositMsg.extra_msg = JSON.stringify(storageDepositMsg);
3530
- }
3531
- console.log("get_user_deposit_address params:", { deposit_msg: depositMsg });
3532
- const userDepositAddress = yield nearCall(
3533
- config.bridgeContractId,
3534
- "get_user_deposit_address",
3535
- { deposit_msg: depositMsg }
3536
- );
3537
- const _feeRate = feeRate || (yield getBtcGasPrice());
3538
- console.log("user deposit address:", userDepositAddress);
3539
- console.log("deposit amount:", new Big2(action.amount).toNumber());
3540
- console.log("receive amount:", new Big2(_action.amount).toNumber());
3541
- console.log("fee rate:", _feeRate);
3542
- const txHash = yield sendBitcoin(
3543
- userDepositAddress,
3544
- new Big2(action.amount).toNumber(),
3545
- _feeRate
3546
- );
3547
- yield receiveDepositMsg(config.base_url, {
3548
- btcPublicKey,
3549
- txHash,
3550
- postActions: JSON.stringify(depositMsg.post_actions),
3551
- extraMsg: depositMsg.extra_msg
3552
- });
3553
- const checkTransactionStatusRes = yield checkTransactionStatus(config.base_url, txHash);
3554
- console.log("checkTransactionStatus resp:", checkTransactionStatusRes);
3555
- return checkTransactionStatusRes.result_code === 0 ? { result: "success" } : { result: "failed", error: checkTransactionStatusRes.result_message };
3556
- } catch (error) {
3557
- console.error("Error executing Bridge+BurrowSupply:", error);
3558
- return { result: "failed", error: error.message };
3559
- }
3560
- });
3561
- }
3562
-
3563
3816
  // src/index.ts
3564
3817
  var getVersion = () => {
3565
- return "0.3.11";
3818
+ return "0.3.12";
3566
3819
  };
3567
3820
  if (typeof window !== "undefined") {
3568
3821
  window.__PARTICLE_BTC_CONNECT_VERSION = getVersion();