btc-wallet 0.4.4-beta → 0.4.5-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,149 @@ 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
+ const _amount = Number(new Big(amount).mul(__pow(10, 8)).toFixed(0));
3533
+ if (brgConfig.min_withdraw_amount) {
3534
+ if (_amount < Number(brgConfig.min_withdraw_amount)) {
3535
+ throw new Error("Mini withdraw amount is " + brgConfig.min_withdraw_amount);
3536
+ }
3537
+ }
3538
+ const feePercent = Number(brgConfig.withdraw_bridge_fee.fee_rate) * _amount;
3539
+ const withdrawFee = feePercent > Number(brgConfig.withdraw_bridge_fee.fee_min) ? feePercent : Number(brgConfig.withdraw_bridge_fee.fee_min);
3540
+ const allUTXO = yield nearCall(config.bridgeContractId, "get_utxos_paged", {});
3541
+ if (!allUTXO || Object.keys(allUTXO).length === 0) {
3542
+ throw new Error("The network is busy, please try again later.");
3543
+ }
3544
+ const utxos = Object.keys(allUTXO).map((key) => {
3545
+ const txid = key.split("@");
3546
+ return {
3547
+ txid: txid[0],
3548
+ vout: allUTXO[key].vout,
3549
+ value: Number(allUTXO[key].balance),
3550
+ script: allUTXO[key].script
3551
+ };
3552
+ });
3553
+ const _feeRate = feeRate || (yield getBtcGasPrice());
3554
+ const { inputs, outputs, fee } = coinselect(
3555
+ utxos,
3556
+ [{ address: btcAddress, value: _amount }],
3557
+ Math.ceil(_feeRate)
3558
+ );
3559
+ if (!outputs || !inputs) {
3560
+ throw new Error("The network is busy, please try again later.");
3561
+ }
3562
+ const maxBtcFee = Number(brgConfig.max_btc_gas_fee);
3563
+ const newFee = fee;
3564
+ const withdrawChangeAddress = brgConfig.change_address;
3565
+ if (newFee > maxBtcFee) {
3566
+ throw new Error("Gas exceeds maximum value");
3567
+ }
3568
+ let userOutput, noUserOutput;
3569
+ for (let i = 0; i < outputs.length; i++) {
3570
+ const output = outputs[i];
3571
+ if (output.value.toString() === _amount.toString()) {
3572
+ userOutput = output;
3573
+ } else {
3574
+ noUserOutput = output;
3575
+ }
3576
+ if (!output.address) {
3577
+ output.address = withdrawChangeAddress;
3578
+ }
3579
+ }
3580
+ userOutput.value = new Big(userOutput.value).minus(newFee).minus(withdrawFee).toNumber();
3581
+ if (noUserOutput) {
3582
+ noUserOutput.value = new Big(noUserOutput.value).plus(newFee).plus(withdrawFee).toNumber();
3583
+ } else {
3584
+ noUserOutput = {
3585
+ address: withdrawChangeAddress,
3586
+ value: new Big(newFee).plus(withdrawFee).toNumber()
3587
+ };
3588
+ outputs.push(noUserOutput);
3589
+ }
3590
+ const insufficientOutput = outputs.some((item) => item.value < 0);
3591
+ if (insufficientOutput) {
3592
+ throw new Error("Not enough gas");
3593
+ }
3594
+ const inputSum = inputs.reduce((sum, cur) => sum + Number(cur.value), 0);
3595
+ const outputSum = outputs.reduce((sum, cur) => sum + Number(cur.value), 0);
3596
+ if (newFee + outputSum !== inputSum) {
3597
+ throw new Error("compute error");
3598
+ }
3599
+ const network = yield getNetwork();
3600
+ const btcNetwork = network === "mainnet" ? bitcoin.networks.bitcoin : bitcoin.networks.testnet;
3601
+ const psbt = new bitcoin.Psbt({ network: btcNetwork });
3602
+ const btcRpcUrl = yield getBtcRpcUrl();
3603
+ for (let i = 0; i < inputs.length; i++) {
3604
+ const input = inputs[i];
3605
+ const txData = yield fetch(`${btcRpcUrl}/tx/${input.txid}`).then((res) => res.json());
3606
+ const inputOptions = {
3607
+ hash: input.txid,
3608
+ index: input.vout,
3609
+ sequence: 4294967293,
3610
+ witnessUtxo: {
3611
+ script: Buffer.from(txData.vout[input.vout].scriptpubkey, "hex"),
3612
+ value: input.value
3613
+ }
3614
+ };
3615
+ psbt.addInput(inputOptions);
3616
+ }
3617
+ outputs.forEach((output) => {
3618
+ psbt.addOutput({
3619
+ address: output.address,
3620
+ value: output.value
3621
+ });
3622
+ });
3623
+ const _inputs = inputs.map((item) => {
3624
+ return `${item.txid}:${item.vout}`;
3625
+ });
3626
+ const txOutputs = psbt.txOutputs.map((item) => {
3627
+ return {
3628
+ script_pubkey: uint8ArrayToHex(item.script),
3629
+ value: item.value
3630
+ };
3631
+ });
3632
+ const msg = {
3633
+ Withdraw: {
3634
+ target_btc_address: btcAddress,
3635
+ input: _inputs,
3636
+ output: txOutputs
3637
+ }
3638
+ };
3639
+ const csna = yield getCsnaAccountId(env);
3640
+ const transaction = {
3641
+ receiverId: config.token,
3642
+ signerId: csna,
3643
+ actions: [
3644
+ {
3645
+ type: "FunctionCall",
3646
+ params: {
3647
+ methodName: "ft_transfer_call",
3648
+ args: {
3649
+ receiver_id: config.bridgeContractId,
3650
+ amount: _amount.toString(),
3651
+ msg: JSON.stringify(msg)
3652
+ },
3653
+ gas: "300000000000000",
3654
+ deposit: "1"
3655
+ }
3656
+ }
3657
+ ]
3658
+ };
3659
+ return transaction;
3660
+ });
3661
+ }
3662
+ function uint8ArrayToHex(uint8Array) {
3663
+ return Array.from(uint8Array).map((byte) => byte.toString(16).padStart(2, "0")).join("");
3664
+ }
3519
3665
 
3520
3666
  // src/core/setupBTCWallet.ts
3521
3667
  var { transfer, functionCall } = actionCreators;
@@ -3741,7 +3887,7 @@ var BTCWallet = (_0) => __async(void 0, [_0], function* ({
3741
3887
  const btcContext = window.btcContext;
3742
3888
  const accountId = state.getAccount();
3743
3889
  const accountInfo = yield getAccountInfo(accountId, currentConfig.accountContractId);
3744
- yield checkGasTokenArrears(accountInfo, env, true);
3890
+ yield checkGasTokenDebt(accountInfo, env, true);
3745
3891
  const trans = [...params.transactions];
3746
3892
  console.log("raw trans:", trans);
3747
3893
  const gasTokenBalance = (accountInfo == null ? void 0 : accountInfo.gas_token[currentConfig.token]) || "0";
@@ -3950,7 +4096,7 @@ function setupBTCWallet({
3950
4096
  if (!hasShownNotice) {
3951
4097
  Dialog.alert({
3952
4098
  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"
4099
+ 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
4100
  });
3955
4101
  localStorage.setItem("satoshi_private_mainnet_notice", "true");
3956
4102
  }
@@ -3979,7 +4125,7 @@ function setupBTCWallet({
3979
4125
 
3980
4126
  // src/index.ts
3981
4127
  var getVersion = () => {
3982
- return "0.4.4-beta";
4128
+ return "0.4.5-beta";
3983
4129
  };
3984
4130
  if (typeof window !== "undefined") {
3985
4131
  window.__BTC_WALLET_VERSION = getVersion();
@@ -3997,8 +4143,8 @@ export {
3997
4143
  UnisatConnector,
3998
4144
  WizzConnector,
3999
4145
  XverseConnector,
4000
- checkGasTokenArrears,
4001
4146
  checkGasTokenBalance,
4147
+ checkGasTokenDebt,
4002
4148
  checkSatoshiWhitelist,
4003
4149
  estimateDepositAmount,
4004
4150
  executeBTCDepositAndAction,
@@ -4007,8 +4153,9 @@ export {
4007
4153
  getBtcGasPrice,
4008
4154
  getCsnaAccountId,
4009
4155
  getDepositAmount,
4156
+ getNBTCBalance,
4010
4157
  getVersion,
4011
- queryGasTokenArrears,
4158
+ getWithdrawTransaction,
4012
4159
  sendBitcoin,
4013
4160
  setupBTCWallet,
4014
4161
  useAccountContract,