diff --git a/typescript/src/redemption.ts b/typescript/src/redemption.ts index f8a0665cf..f3b2474a0 100644 --- a/typescript/src/redemption.ts +++ b/typescript/src/redemption.ts @@ -412,11 +412,12 @@ export async function getRedemptionRequest( } /** - * Finds the oldest active wallet that has enough BTC to handle a redemption request. + * Finds the oldest live wallet that has enough BTC to handle a redemption + * request. * @param amount The amount to be redeemed in satoshis. - * @param redeemerOutputScript The redeemer output script the redeemed funds - * are supposed to be locked on. Must be un-prefixed and not prepended - * with length. + * @param redeemerOutputScript The redeemer output script the redeemed funds are + * supposed to be locked on. Must be un-prefixed and not prepended with + * length. * @param bitcoinNetwork Bitcoin network. * @param bridge The handle to the Bridge on-chain contract. * @param bitcoinClient Bitcoin client used to interact with the network. @@ -441,6 +442,7 @@ export async function findWalletForRedemption( } | undefined = undefined let maxAmount = BigNumber.from(0) + let liveWalletsCounter = 0 for (const wallet of wallets) { const { walletPublicKeyHash } = wallet @@ -456,6 +458,7 @@ export async function findWalletForRedemption( ) continue } + liveWalletsCounter++ // Wallet must have a main UTXO that can be determined. const mainUtxo = await determineWalletMainUtxo( @@ -511,6 +514,18 @@ export async function findWalletForRedemption( ) } + if (liveWalletsCounter === 0) { + throw new Error("Currently, there are no live wallets in the network.") + } + + // Cover a corner case when the user requested redemption for all live wallets + // in the network using the same Bitcoin address. + if (!walletData && liveWalletsCounter > 0 && maxAmount.eq(0)) { + throw new Error( + "All live wallets in the network have the pending redemption for a given Bitcoin address. Please use another Bitcoin address." + ) + } + if (!walletData) throw new Error( `Could not find a wallet with enough funds. Maximum redemption amount is ${maxAmount} Satoshi.` diff --git a/typescript/test/redemption.test.ts b/typescript/test/redemption.test.ts index e2efa36d1..58bac10cc 100644 --- a/typescript/test/redemption.test.ts +++ b/typescript/test/redemption.test.ts @@ -1468,7 +1468,7 @@ describe("Redemption", () => { bitcoinClient ) ).to.be.rejectedWith( - "Could not find a wallet with enough funds. Maximum redemption amount is 0 Satoshi." + "Currently, there are no live wallets in the network." ) }) } @@ -1676,6 +1676,61 @@ describe("Redemption", () => { }) } ) + + context( + "when all active wallets has pending redemption for a given Bitcoin address", + () => { + const amount: BigNumber = BigNumber.from("1000000") // 0.01 BTC + const redeemerOutputScript = + findWalletForRedemptionData.pendingRedemption.redeemerOutputScript + + beforeEach(async () => { + const walletPublicKeyHash = + findWalletForRedemptionData.walletWithPendingRedemption.event + .walletPublicKeyHash + + const pendingRedemptions = new Map< + BigNumberish, + RedemptionRequest + >() + + const pendingRedemption1 = MockBridge.buildRedemptionKey( + walletPublicKeyHash.toString(), + redeemerOutputScript + ) + + const pendingRedemption2 = MockBridge.buildRedemptionKey( + findWalletForRedemptionData.liveWallet.event.walletPublicKeyHash.toString(), + redeemerOutputScript + ) + + pendingRedemptions.set( + pendingRedemption1, + findWalletForRedemptionData.pendingRedemption + ) + + pendingRedemptions.set( + pendingRedemption2, + findWalletForRedemptionData.pendingRedemption + ) + bridge.setPendingRedemptions(pendingRedemptions) + }) + + it("should throw an error", async () => { + await expect( + findWalletForRedemption( + amount, + redeemerOutputScript, + BitcoinNetwork.Testnet, + bridge, + bitcoinClient + ) + ).to.be.rejectedWith( + "All live wallets in the network have the pending redemption for a given Bitcoin address. Please use another Bitcoin address." + ) + }) + } + ) }) }) })