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