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/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();