btc-wallet 0.3.11 → 0.3.12

Sign up to get free protection for your applications and to get access to all the features.
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();