btc-wallet 0.4.4-beta → 0.4.6-beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,4 +19,5 @@ export declare function checkBtcTransactionStatus(url: string, sig: string): Pro
19
19
  NearHashList: string[];
20
20
  }>;
21
21
  export declare function getWhitelist(url: string): Promise<string[]>;
22
+ export declare function receiveWithdrawMsg(url: string, txHash: string): Promise<any>;
22
23
  export {};
package/esm/index.js CHANGED
@@ -2354,6 +2354,7 @@ function BtcWalletSelectorContextProvider({
2354
2354
  const [isProcessing, setIsProcessing] = useState8(false);
2355
2355
  const connectors = [
2356
2356
  new UnisatConnector(),
2357
+ new XverseConnector(),
2357
2358
  new OKXConnector(),
2358
2359
  new BitgetConnector()
2359
2360
  ];
@@ -3193,9 +3194,10 @@ Dialog.style = `
3193
3194
  `;
3194
3195
 
3195
3196
  // src/core/btcUtils.ts
3196
- var MINIMUM_DEPOSIT_AMOUNT_BASE = 0;
3197
+ import bitcoin from "bitcoinjs-lib";
3198
+ import coinselect from "coinselect";
3197
3199
  var NEAR_STORAGE_DEPOSIT_AMOUNT = "1250000000000000000000";
3198
- var NBTC_STORAGE_DEPOSIT_AMOUNT = 3e3;
3200
+ var NBTC_STORAGE_DEPOSIT_AMOUNT = "3000";
3199
3201
  var GAS_LIMIT = "50000000000000";
3200
3202
  function getBtcProvider() {
3201
3203
  if (typeof window === "undefined" || !window.btcContext) {
@@ -3253,11 +3255,11 @@ function checkGasTokenBalance(csna, gasToken, minAmount, env) {
3253
3255
  }
3254
3256
  });
3255
3257
  }
3256
- function checkGasTokenArrears(accountInfo, env, autoDeposit) {
3258
+ function checkGasTokenDebt(accountInfo, env, autoDeposit) {
3257
3259
  return __async(this, null, function* () {
3258
3260
  var _a, _b, _c;
3259
3261
  const debtAmount = new Big(((_a = accountInfo == null ? void 0 : accountInfo.debt_info) == null ? void 0 : _a.near_gas_debt_amount) || 0).plus(((_b = accountInfo == null ? void 0 : accountInfo.debt_info) == null ? void 0 : _b.protocol_fee_debt_amount) || 0).toString();
3260
- const relayerFeeAmount = ((_c = accountInfo == null ? void 0 : accountInfo.relayer_fee) == null ? void 0 : _c.amount) || "0";
3262
+ const relayerFeeAmount = !(accountInfo == null ? void 0 : accountInfo.nonce) ? NBTC_STORAGE_DEPOSIT_AMOUNT : ((_c = accountInfo == null ? void 0 : accountInfo.relayer_fee) == null ? void 0 : _c.amount) || 0;
3261
3263
  const hasDebtArrears = new Big(debtAmount).gt(0);
3262
3264
  const hasRelayerFeeArrears = new Big(relayerFeeAmount).gt(0);
3263
3265
  if (!hasDebtArrears && !hasRelayerFeeArrears)
@@ -3267,7 +3269,7 @@ function checkGasTokenArrears(accountInfo, env, autoDeposit) {
3267
3269
  console.log("get_account:", accountInfo);
3268
3270
  const action = {
3269
3271
  receiver_id: config.accountContractId,
3270
- amount: transferAmount,
3272
+ amount: transferAmount.toString(),
3271
3273
  msg: JSON.stringify(hasDebtArrears ? "Repay" : "RelayerFee")
3272
3274
  };
3273
3275
  if (!autoDeposit)
@@ -3287,14 +3289,6 @@ function checkGasTokenArrears(accountInfo, env, autoDeposit) {
3287
3289
  }
3288
3290
  });
3289
3291
  }
3290
- function queryGasTokenArrears(env) {
3291
- return __async(this, null, function* () {
3292
- const config = yield getConfig(env);
3293
- const csna = yield getCsnaAccountId(env);
3294
- const accountInfo = yield getAccountInfo(csna, config.accountContractId);
3295
- return accountInfo == null ? void 0 : accountInfo.debt_info;
3296
- });
3297
- }
3298
3292
  function getBtcGasPrice() {
3299
3293
  return __async(this, null, function* () {
3300
3294
  const network = yield getNetwork();
@@ -3336,6 +3330,18 @@ function getBtcBalance() {
3336
3330
  };
3337
3331
  });
3338
3332
  }
3333
+ function getNBTCBalance(address, env) {
3334
+ return __async(this, null, function* () {
3335
+ const config = yield getConfig(env || "mainnet");
3336
+ const rawBalance = yield nearCall(config.token, "ft_balance_of", {
3337
+ account_id: address
3338
+ });
3339
+ const balance = new Big(rawBalance).div(__pow(10, 8)).round(8, Big.roundDown).toNumber();
3340
+ const rawAvailableBalance = new Big(rawBalance).minus(1e3).toNumber();
3341
+ const availableBalance = new Big(rawAvailableBalance).div(__pow(10, 8)).round(8, Big.roundDown).toNumber();
3342
+ return { balance, availableBalance, rawBalance, rawAvailableBalance };
3343
+ });
3344
+ }
3339
3345
  function sendBitcoin(address, amount, feeRate) {
3340
3346
  return __async(this, null, function* () {
3341
3347
  const { sendBitcoin: sendBitcoin2 } = getBtcProvider();
@@ -3345,28 +3351,29 @@ function sendBitcoin(address, amount, feeRate) {
3345
3351
  }
3346
3352
  function estimateDepositAmount(amount, option) {
3347
3353
  return __async(this, null, function* () {
3348
- const config = yield getConfig((option == null ? void 0 : option.env) || "mainnet");
3349
- const csna = yield getCsnaAccountId((option == null ? void 0 : option.env) || "mainnet");
3350
- const accountInfo = yield getAccountInfo(csna, config.accountContractId);
3351
- const { receiveAmount } = yield getDepositAmount(amount, __spreadProps(__spreadValues({}, option), { isEstimate: true }));
3352
- return (accountInfo == null ? void 0 : accountInfo.nonce) ? receiveAmount : new Big(receiveAmount).minus(NBTC_STORAGE_DEPOSIT_AMOUNT).round(0, Big.roundDown).toNumber();
3354
+ return amount;
3353
3355
  });
3354
3356
  }
3355
3357
  function getDepositAmount(amount, option) {
3356
3358
  return __async(this, null, function* () {
3357
- const config = yield getConfig((option == null ? void 0 : option.env) || "mainnet");
3359
+ const env = (option == null ? void 0 : option.env) || "mainnet";
3360
+ const config = yield getConfig(env);
3361
+ const csna = yield getCsnaAccountId(env);
3362
+ const accountInfo = yield getAccountInfo(csna, config.accountContractId);
3363
+ const debtAction = yield checkGasTokenDebt(accountInfo, env, false);
3364
+ const repayAmount = (debtAction == null ? void 0 : debtAction.amount) || 0;
3358
3365
  const {
3359
3366
  deposit_bridge_fee: { fee_min, fee_rate },
3360
3367
  min_deposit_amount
3361
3368
  } = yield nearCall(config.bridgeContractId, "get_config", {});
3362
- const depositAmount = (option == null ? void 0 : option.isEstimate) ? Number(amount) : Math.max(Number(min_deposit_amount) + MINIMUM_DEPOSIT_AMOUNT_BASE, Number(amount));
3363
- const fee = Math.max(Number(fee_min), Number(depositAmount) * fee_rate);
3364
- const receiveAmount = new Big(depositAmount).minus(fee).minus(MINIMUM_DEPOSIT_AMOUNT_BASE).round(0, Big.roundDown).toNumber();
3365
- console.log("getDepositAmount:", { depositAmount, receiveAmount, fee });
3369
+ const depositAmount = Math.max(Number(min_deposit_amount), Number(amount));
3370
+ const protocolFee = Math.max(Number(fee_min), Number(depositAmount) * fee_rate);
3371
+ const totalDepositAmount = new Big(depositAmount).plus(protocolFee).plus(repayAmount).round(0, Big.roundDown).toNumber();
3366
3372
  return {
3367
3373
  depositAmount,
3368
- receiveAmount: Math.max(receiveAmount, 0),
3369
- fee
3374
+ totalDepositAmount,
3375
+ protocolFee,
3376
+ repayAmount
3370
3377
  };
3371
3378
  });
3372
3379
  }
@@ -3390,7 +3397,6 @@ function executeBTCDepositAndAction(_0) {
3390
3397
  action,
3391
3398
  amount,
3392
3399
  feeRate,
3393
- fixedAmount = true,
3394
3400
  pollResult = true,
3395
3401
  registerDeposit,
3396
3402
  env = "mainnet"
@@ -3407,24 +3413,23 @@ function executeBTCDepositAndAction(_0) {
3407
3413
  throw new Error("amount or action is required");
3408
3414
  }
3409
3415
  const csna = yield getCsnaAccountId(env);
3410
- const rawDepositAmount = (_a = action ? action.amount : amount) != null ? _a : "0";
3411
- if (new Big(rawDepositAmount).lt(0)) {
3416
+ const depositAmount = (_a = action ? action.amount : amount) != null ? _a : "0";
3417
+ if (new Big(depositAmount).lt(0)) {
3412
3418
  throw new Error("amount must be greater than 0");
3413
3419
  }
3414
- const { depositAmount, receiveAmount } = yield getDepositAmount(rawDepositAmount, {
3420
+ const { totalDepositAmount, protocolFee, repayAmount } = yield getDepositAmount(depositAmount, {
3415
3421
  env
3416
3422
  });
3417
3423
  const accountInfo = yield getAccountInfo(csna, config.accountContractId);
3418
3424
  const newActions = [];
3419
- const arrearsAction = yield checkGasTokenArrears(accountInfo, env, false);
3420
- if (arrearsAction) {
3421
- newActions.push(__spreadProps(__spreadValues({}, arrearsAction), {
3425
+ const debtAction = yield checkGasTokenDebt(accountInfo, env, false);
3426
+ if (debtAction) {
3427
+ newActions.push(__spreadProps(__spreadValues({}, debtAction), {
3422
3428
  gas: GAS_LIMIT
3423
3429
  }));
3424
3430
  }
3425
3431
  if (action) {
3426
3432
  newActions.push(__spreadProps(__spreadValues({}, action), {
3427
- amount: (arrearsAction == null ? void 0 : arrearsAction.amount) && !fixedAmount ? new Big(receiveAmount).minus(arrearsAction.amount).toString() : receiveAmount.toString(),
3428
3433
  gas: GAS_LIMIT
3429
3434
  }));
3430
3435
  }
@@ -3441,12 +3446,6 @@ function executeBTCDepositAndAction(_0) {
3441
3446
  }
3442
3447
  if (!(accountInfo == null ? void 0 : accountInfo.nonce)) {
3443
3448
  storageDepositMsg.btc_public_key = btcPublicKey;
3444
- newActions.push({
3445
- receiver_id: config.accountContractId,
3446
- amount: NBTC_STORAGE_DEPOSIT_AMOUNT.toString(),
3447
- msg: JSON.stringify("RelayerFee"),
3448
- gas: GAS_LIMIT
3449
- });
3450
3449
  }
3451
3450
  const depositMsg = {
3452
3451
  recipient_id: csna,
@@ -3460,10 +3459,14 @@ function executeBTCDepositAndAction(_0) {
3460
3459
  { deposit_msg: depositMsg }
3461
3460
  );
3462
3461
  const _feeRate = feeRate || (yield getBtcGasPrice());
3463
- const sendAmount = (arrearsAction == null ? void 0 : arrearsAction.amount) && fixedAmount ? new Big(depositAmount).plus((arrearsAction == null ? void 0 : arrearsAction.amount) || 0).toString() : depositAmount;
3464
- console.log("user deposit address:", userDepositAddress);
3465
- console.log("send amount:", sendAmount);
3466
- console.log("fee rate:", _feeRate);
3462
+ console.table({
3463
+ "User Deposit Address": userDepositAddress,
3464
+ "Deposit Amount": depositAmount,
3465
+ "Protocol Fee": protocolFee,
3466
+ "Repay Amount": repayAmount,
3467
+ "Total Deposit Amount": totalDepositAmount,
3468
+ "Fee Rate": _feeRate
3469
+ });
3467
3470
  const postActionsStr = newActions.length > 0 ? JSON.stringify(newActions) : void 0;
3468
3471
  yield preReceiveDepositMsg(config.base_url, {
3469
3472
  btcPublicKey,
@@ -3471,7 +3474,7 @@ function executeBTCDepositAndAction(_0) {
3471
3474
  postActions: postActionsStr,
3472
3475
  extraMsg: depositMsg.extra_msg
3473
3476
  });
3474
- const txHash = yield sendBitcoin(userDepositAddress, Number(sendAmount), _feeRate);
3477
+ const txHash = yield sendBitcoin(userDepositAddress, totalDepositAmount, _feeRate);
3475
3478
  yield receiveDepositMsg(config.base_url, {
3476
3479
  btcPublicKey,
3477
3480
  txHash,
@@ -3516,6 +3519,148 @@ Sign up now: <a style="color: #ff7a00; text-decoration: underline;" href="https:
3516
3519
  }
3517
3520
  });
3518
3521
  }
3522
+ function getWithdrawTransaction(_0) {
3523
+ return __async(this, arguments, function* ({
3524
+ amount,
3525
+ feeRate,
3526
+ env = "mainnet"
3527
+ }) {
3528
+ const provider = getBtcProvider();
3529
+ const btcAddress = yield provider.account;
3530
+ const config = yield getConfig(env);
3531
+ const brgConfig = yield nearCall(config.bridgeContractId, "get_config", {});
3532
+ if (brgConfig.min_withdraw_amount) {
3533
+ if (Number(amount) < Number(brgConfig.min_withdraw_amount)) {
3534
+ throw new Error("Mini withdraw amount is " + brgConfig.min_withdraw_amount);
3535
+ }
3536
+ }
3537
+ const feePercent = Number(brgConfig.withdraw_bridge_fee.fee_rate) * Number(amount);
3538
+ const withdrawFee = feePercent > Number(brgConfig.withdraw_bridge_fee.fee_min) ? feePercent : Number(brgConfig.withdraw_bridge_fee.fee_min);
3539
+ const allUTXO = yield nearCall(config.bridgeContractId, "get_utxos_paged", {});
3540
+ if (!allUTXO || Object.keys(allUTXO).length === 0) {
3541
+ throw new Error("The network is busy, please try again later.");
3542
+ }
3543
+ const utxos = Object.keys(allUTXO).map((key) => {
3544
+ const txid = key.split("@");
3545
+ return {
3546
+ txid: txid[0],
3547
+ vout: allUTXO[key].vout,
3548
+ value: Number(allUTXO[key].balance),
3549
+ script: allUTXO[key].script
3550
+ };
3551
+ });
3552
+ const _feeRate = feeRate || (yield getBtcGasPrice());
3553
+ const { inputs, outputs, fee } = coinselect(
3554
+ utxos,
3555
+ [{ address: btcAddress, value: Number(amount) }],
3556
+ Math.ceil(_feeRate)
3557
+ );
3558
+ if (!outputs || !inputs) {
3559
+ throw new Error("The network is busy, please try again later.");
3560
+ }
3561
+ const maxBtcFee = Number(brgConfig.max_btc_gas_fee);
3562
+ const newFee = fee;
3563
+ const withdrawChangeAddress = brgConfig.change_address;
3564
+ if (newFee > maxBtcFee) {
3565
+ throw new Error("Gas exceeds maximum value");
3566
+ }
3567
+ let userOutput, noUserOutput;
3568
+ for (let i = 0; i < outputs.length; i++) {
3569
+ const output = outputs[i];
3570
+ if (output.value.toString() === amount.toString()) {
3571
+ userOutput = output;
3572
+ } else {
3573
+ noUserOutput = output;
3574
+ }
3575
+ if (!output.address) {
3576
+ output.address = withdrawChangeAddress;
3577
+ }
3578
+ }
3579
+ userOutput.value = new Big(userOutput.value).minus(newFee).minus(withdrawFee).toNumber();
3580
+ if (noUserOutput) {
3581
+ noUserOutput.value = new Big(noUserOutput.value).plus(newFee).plus(withdrawFee).toNumber();
3582
+ } else {
3583
+ noUserOutput = {
3584
+ address: withdrawChangeAddress,
3585
+ value: new Big(newFee).plus(withdrawFee).toNumber()
3586
+ };
3587
+ outputs.push(noUserOutput);
3588
+ }
3589
+ const insufficientOutput = outputs.some((item) => item.value < 0);
3590
+ if (insufficientOutput) {
3591
+ throw new Error("Not enough gas");
3592
+ }
3593
+ const inputSum = inputs.reduce((sum, cur) => sum + Number(cur.value), 0);
3594
+ const outputSum = outputs.reduce((sum, cur) => sum + Number(cur.value), 0);
3595
+ if (newFee + outputSum !== inputSum) {
3596
+ throw new Error("compute error");
3597
+ }
3598
+ const network = yield getNetwork();
3599
+ const btcNetwork = network === "mainnet" ? bitcoin.networks.bitcoin : bitcoin.networks.testnet;
3600
+ const psbt = new bitcoin.Psbt({ network: btcNetwork });
3601
+ const btcRpcUrl = yield getBtcRpcUrl();
3602
+ for (let i = 0; i < inputs.length; i++) {
3603
+ const input = inputs[i];
3604
+ const txData = yield fetch(`${btcRpcUrl}/tx/${input.txid}`).then((res) => res.json());
3605
+ const inputOptions = {
3606
+ hash: input.txid,
3607
+ index: input.vout,
3608
+ sequence: 4294967293,
3609
+ witnessUtxo: {
3610
+ script: Buffer.from(txData.vout[input.vout].scriptpubkey, "hex"),
3611
+ value: input.value
3612
+ }
3613
+ };
3614
+ psbt.addInput(inputOptions);
3615
+ }
3616
+ outputs.forEach((output) => {
3617
+ psbt.addOutput({
3618
+ address: output.address,
3619
+ value: output.value
3620
+ });
3621
+ });
3622
+ const _inputs = inputs.map((item) => {
3623
+ return `${item.txid}:${item.vout}`;
3624
+ });
3625
+ const txOutputs = psbt.txOutputs.map((item) => {
3626
+ return {
3627
+ script_pubkey: uint8ArrayToHex(item.script),
3628
+ value: item.value
3629
+ };
3630
+ });
3631
+ const msg = {
3632
+ Withdraw: {
3633
+ target_btc_address: btcAddress,
3634
+ input: _inputs,
3635
+ output: txOutputs
3636
+ }
3637
+ };
3638
+ const csna = yield getCsnaAccountId(env);
3639
+ const transaction = {
3640
+ receiverId: config.token,
3641
+ signerId: csna,
3642
+ actions: [
3643
+ {
3644
+ type: "FunctionCall",
3645
+ params: {
3646
+ methodName: "ft_transfer_call",
3647
+ args: {
3648
+ receiver_id: config.bridgeContractId,
3649
+ amount: amount.toString(),
3650
+ msg: JSON.stringify(msg)
3651
+ },
3652
+ gas: "300000000000000",
3653
+ deposit: "1"
3654
+ }
3655
+ }
3656
+ ]
3657
+ };
3658
+ return transaction;
3659
+ });
3660
+ }
3661
+ function uint8ArrayToHex(uint8Array) {
3662
+ return Array.from(uint8Array).map((byte) => byte.toString(16).padStart(2, "0")).join("");
3663
+ }
3519
3664
 
3520
3665
  // src/core/setupBTCWallet.ts
3521
3666
  var { transfer, functionCall } = actionCreators;
@@ -3741,7 +3886,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3741
3886
  const btcContext = window.btcContext;
3742
3887
  const accountId = state.getAccount();
3743
3888
  const accountInfo = yield getAccountInfo(accountId, currentConfig.accountContractId);
3744
- yield checkGasTokenArrears(accountInfo, env, true);
3889
+ yield checkGasTokenDebt(accountInfo, env, true);
3745
3890
  const trans = [...params.transactions];
3746
3891
  console.log("raw trans:", trans);
3747
3892
  const gasTokenBalance = (accountInfo == null ? void 0 : accountInfo.gas_token[currentConfig.token]) || "0";
@@ -3950,7 +4095,7 @@ function setupBTCWallet({
3950
4095
  if (!hasShownNotice) {
3951
4096
  Dialog.alert({
3952
4097
  title: "Notice",
3953
- message: "You are currently using Satoshi Private Mainnet. This is a private version for testing. Please try a small amount of assets in Bridge"
4098
+ message: "You are currently using Satoshi Private Mainnet. This is a private version for testing. Please try a small amount of assets in Ramp"
3954
4099
  });
3955
4100
  localStorage.setItem("satoshi_private_mainnet_notice", "true");
3956
4101
  }
@@ -3979,7 +4124,7 @@ function setupBTCWallet({
3979
4124
 
3980
4125
  // src/index.ts
3981
4126
  var getVersion = () => {
3982
- return "0.4.4-beta";
4127
+ return "0.4.6-beta";
3983
4128
  };
3984
4129
  if (typeof window !== "undefined") {
3985
4130
  window.__BTC_WALLET_VERSION = getVersion();
@@ -3997,8 +4142,8 @@ export {
3997
4142
  UnisatConnector,
3998
4143
  WizzConnector,
3999
4144
  XverseConnector,
4000
- checkGasTokenArrears,
4001
4145
  checkGasTokenBalance,
4146
+ checkGasTokenDebt,
4002
4147
  checkSatoshiWhitelist,
4003
4148
  estimateDepositAmount,
4004
4149
  executeBTCDepositAndAction,
@@ -4007,8 +4152,9 @@ export {
4007
4152
  getBtcGasPrice,
4008
4153
  getCsnaAccountId,
4009
4154
  getDepositAmount,
4155
+ getNBTCBalance,
4010
4156
  getVersion,
4011
- queryGasTokenArrears,
4157
+ getWithdrawTransaction,
4012
4158
  sendBitcoin,
4013
4159
  setupBTCWallet,
4014
4160
  useAccountContract,