diff --git a/typescript/api-reference/README.md b/typescript/api-reference/README.md index a18d50dc4..15f8db8f4 100644 --- a/typescript/api-reference/README.md +++ b/typescript/api-reference/README.md @@ -67,6 +67,7 @@ - [L1BitcoinDepositor](interfaces/L1BitcoinDepositor.md) - [L2BitcoinDepositor](interfaces/L2BitcoinDepositor.md) - [L2TBTCToken](interfaces/L2TBTCToken.md) +- [RedeemerProxy](interfaces/RedeemerProxy.md) - [RedemptionRequest](interfaces/RedemptionRequest.md) - [TBTCToken](interfaces/TBTCToken.md) - [TBTCVault](interfaces/TBTCVault.md) diff --git a/typescript/api-reference/classes/EthereumTBTCToken.md b/typescript/api-reference/classes/EthereumTBTCToken.md index 95a043433..0775dbfd5 100644 --- a/typescript/api-reference/classes/EthereumTBTCToken.md +++ b/typescript/api-reference/classes/EthereumTBTCToken.md @@ -142,7 +142,7 @@ EthersContractHandle.\_totalRetryAttempts #### Defined in -[src/lib/ethereum/tbtc-token.ts:135](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/tbtc-token.ts#L135) +[src/lib/ethereum/tbtc-token.ts:139](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/tbtc-token.ts#L139) ___ @@ -163,9 +163,15 @@ ___ [`Hex`](Hex.md) +**`See`** + +#### Implementation of + +[TBTCToken](../interfaces/TBTCToken.md).[buildRequestRedemptionData](../interfaces/TBTCToken.md#buildrequestredemptiondata) + #### Defined in -[src/lib/ethereum/tbtc-token.ts:104](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/tbtc-token.ts#L104) +[src/lib/ethereum/tbtc-token.ts:108](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/tbtc-token.ts#L108) ___ diff --git a/typescript/api-reference/classes/RedemptionsService.md b/typescript/api-reference/classes/RedemptionsService.md index d83ad0514..8d1c8cf8f 100644 --- a/typescript/api-reference/classes/RedemptionsService.md +++ b/typescript/api-reference/classes/RedemptionsService.md @@ -15,10 +15,12 @@ Service exposing features related to tBTC v2 redemptions. ### Methods +- [determineRedemptionData](RedemptionsService.md#determineredemptiondata) - [determineWalletMainUtxo](RedemptionsService.md#determinewalletmainutxo) - [findWalletForRedemption](RedemptionsService.md#findwalletforredemption) - [getRedemptionRequests](RedemptionsService.md#getredemptionrequests) - [requestRedemption](RedemptionsService.md#requestredemption) +- [requestRedemptionWithProxy](RedemptionsService.md#requestredemptionwithproxy) ## Constructors @@ -39,7 +41,7 @@ Service exposing features related to tBTC v2 redemptions. #### Defined in -[src/services/redemptions/redemptions-service.ts:30](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L30) +[src/services/redemptions/redemptions-service.ts:31](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L31) ## Properties @@ -51,7 +53,7 @@ Bitcoin client handle. #### Defined in -[src/services/redemptions/redemptions-service.ts:28](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L28) +[src/services/redemptions/redemptions-service.ts:29](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L29) ___ @@ -63,10 +65,37 @@ Handle to tBTC contracts. #### Defined in -[src/services/redemptions/redemptions-service.ts:24](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L24) +[src/services/redemptions/redemptions-service.ts:25](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L25) ## Methods +### determineRedemptionData + +▸ **determineRedemptionData**(`bitcoinRedeemerAddress`, `amount`): `Promise`\<\{ `mainUtxo`: [`BitcoinUtxo`](../README.md#bitcoinutxo) ; `redeemerOutputScript`: [`Hex`](Hex.md) ; `walletPublicKey`: [`Hex`](Hex.md) }\> + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `bitcoinRedeemerAddress` | `string` | Bitcoin address redeemed BTC should be sent to. Only P2PKH, P2WPKH, P2SH, and P2WSH address types are supported. | +| `amount` | `BigNumber` | The amount to be redeemed with the precision of the tBTC on-chain token contract. | + +#### Returns + +`Promise`\<\{ `mainUtxo`: [`BitcoinUtxo`](../README.md#bitcoinutxo) ; `redeemerOutputScript`: [`Hex`](Hex.md) ; `walletPublicKey`: [`Hex`](Hex.md) }\> + +Object containing: + - Bitcoin public key of the wallet asked to handle the redemption. + Presented in the compressed form (33 bytes long with 02 or 03 prefix). + - Main UTXO of the wallet. + - Redeemer output script. + +#### Defined in + +[src/services/redemptions/redemptions-service.ts:132](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L132) + +___ + ### determineWalletMainUtxo ▸ **determineWalletMainUtxo**(`walletPublicKeyHash`, `bitcoinNetwork`): `Promise`\<`undefined` \| [`BitcoinUtxo`](../README.md#bitcoinutxo)\> @@ -90,7 +119,7 @@ Promise holding the wallet main UTXO or undefined value. #### Defined in -[src/services/redemptions/redemptions-service.ts:225](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L225) +[src/services/redemptions/redemptions-service.ts:300](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L300) ___ @@ -116,7 +145,7 @@ Promise with the wallet details needed to request a redemption. #### Defined in -[src/services/redemptions/redemptions-service.ts:106](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L106) +[src/services/redemptions/redemptions-service.ts:181](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L181) ___ @@ -147,7 +176,7 @@ Throws an error if no redemption request exists for the given #### Defined in -[src/services/redemptions/redemptions-service.ts:337](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L337) +[src/services/redemptions/redemptions-service.ts:412](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L412) ___ @@ -176,4 +205,36 @@ Object containing: #### Defined in -[src/services/redemptions/redemptions-service.ts:48](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L48) +[src/services/redemptions/redemptions-service.ts:49](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L49) + +___ + +### requestRedemptionWithProxy + +▸ **requestRedemptionWithProxy**(`bitcoinRedeemerAddress`, `amount`, `redeemerProxy`): `Promise`\<\{ `targetChainTxHash`: [`Hex`](Hex.md) ; `walletPublicKey`: [`Hex`](Hex.md) }\> + +Requests a redemption of TBTC v2 token into BTC using a custom integration. +The function builds the redemption data and handles the redemption request +through the provided redeemer proxy. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `bitcoinRedeemerAddress` | `string` | Bitcoin address the redeemed BTC should be sent to. Only P2PKH, P2WPKH, P2SH, and P2WSH address types are supported. | +| `amount` | `BigNumberish` | The amount to be redeemed with the precision of the tBTC on-chain token contract. | +| `redeemerProxy` | [`RedeemerProxy`](../interfaces/RedeemerProxy.md) | Object impleenting functions required to route tBTC redemption requests through the tBTC bridge. | + +#### Returns + +`Promise`\<\{ `targetChainTxHash`: [`Hex`](Hex.md) ; `walletPublicKey`: [`Hex`](Hex.md) }\> + +Object containing: + - Target chain hash of the request redemption transaction + (for example, Ethereum transaction hash) + - Bitcoin public key of the wallet asked to handle the redemption. + Presented in the compressed form (33 bytes long with 02 or 03 prefix). + +#### Defined in + +[src/services/redemptions/redemptions-service.ts:88](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redemptions-service.ts#L88) diff --git a/typescript/api-reference/interfaces/RedeemerProxy.md b/typescript/api-reference/interfaces/RedeemerProxy.md new file mode 100644 index 000000000..b28b86fac --- /dev/null +++ b/typescript/api-reference/interfaces/RedeemerProxy.md @@ -0,0 +1,53 @@ +# Interface: RedeemerProxy + +Interface defining functions required to route tBTC redemption requests through +the tBTC bridge by custom integrators. + +## Table of contents + +### Methods + +- [redeemerAddress](RedeemerProxy.md#redeemeraddress) +- [requestRedemption](RedeemerProxy.md#requestredemption) + +## Methods + +### redeemerAddress + +▸ **redeemerAddress**(): [`ChainIdentifier`](ChainIdentifier.md) + +Chain identifier of the redeemer. This is the address that will be able to +claim the tBTC tokens if anything goes wrong during the redemption process. + +#### Returns + +[`ChainIdentifier`](ChainIdentifier.md) + +#### Defined in + +[src/services/redemptions/redeemer-proxy.ts:13](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redeemer-proxy.ts#L13) + +___ + +### requestRedemption + +▸ **requestRedemption**(`redemptionData`): `Promise`\<[`Hex`](../classes/Hex.md)\> + +Requests redemption of tBTC token with determined redemption data. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `redemptionData` | [`Hex`](../classes/Hex.md) | Data required to redeem the tBTC tokens. | + +#### Returns + +`Promise`\<[`Hex`](../classes/Hex.md)\> + +Target chain hash of the request redemption transaction + (for example, Ethereum transaction hash) + +#### Defined in + +[src/services/redemptions/redeemer-proxy.ts:21](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/redemptions/redeemer-proxy.ts#L21) diff --git a/typescript/api-reference/interfaces/TBTCToken.md b/typescript/api-reference/interfaces/TBTCToken.md index e3e3347f4..49218c879 100644 --- a/typescript/api-reference/interfaces/TBTCToken.md +++ b/typescript/api-reference/interfaces/TBTCToken.md @@ -10,12 +10,40 @@ Interface for communication with the TBTC v2 token on-chain contract. ### Methods +- [buildRequestRedemptionData](TBTCToken.md#buildrequestredemptiondata) - [getChainIdentifier](TBTCToken.md#getchainidentifier) - [requestRedemption](TBTCToken.md#requestredemption) - [totalSupply](TBTCToken.md#totalsupply) ## Methods +### buildRequestRedemptionData + +▸ **buildRequestRedemptionData**(`redeemer`, `walletPublicKey`, `mainUtxo`, `redeemerOutputScript`): [`Hex`](../classes/Hex.md) + +Prepare tBTC Redemption Data in the raw bytes format expected by the tBTC +Bridge contract. The data is used to request a redemption of TBTC v2 token +through custom integration with the tBTC Bridge contract. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `redeemer` | [`ChainIdentifier`](ChainIdentifier.md) | Chain identifier of the redeemer. This is the address that will be able to claim the tBTC tokens if anything goes wrong during the redemption process. | +| `walletPublicKey` | [`Hex`](../classes/Hex.md) | The Bitcoin public key of the wallet. Must be in the compressed form (33 bytes long with 02 or 03 prefix). | +| `mainUtxo` | [`BitcoinUtxo`](../README.md#bitcoinutxo) | The main UTXO of the wallet. Must match the main UTXO held by the on-chain Bridge contract. | +| `redeemerOutputScript` | [`Hex`](../classes/Hex.md) | The output script that the redeemed funds will be locked to. Must not be prepended with length. | + +#### Returns + +[`Hex`](../classes/Hex.md) + +#### Defined in + +[src/lib/contracts/tbtc-token.ts:61](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/tbtc-token.ts#L61) + +___ + ### getChainIdentifier ▸ **getChainIdentifier**(): [`ChainIdentifier`](ChainIdentifier.md) diff --git a/typescript/src/lib/contracts/tbtc-token.ts b/typescript/src/lib/contracts/tbtc-token.ts index 01ea195fe..9682ee884 100644 --- a/typescript/src/lib/contracts/tbtc-token.ts +++ b/typescript/src/lib/contracts/tbtc-token.ts @@ -43,4 +43,25 @@ export interface TBTCToken { redeemerOutputScript: Hex, amount: BigNumber ): Promise + + /** + * Prepare tBTC Redemption Data in the raw bytes format expected by the tBTC + * Bridge contract. The data is used to request a redemption of TBTC v2 token + * through custom integration with the tBTC Bridge contract. + * @param redeemer - Chain identifier of the redeemer. This is the address that + * will be able to claim the tBTC tokens if anything goes wrong during + * the redemption process. + * @param walletPublicKey - The Bitcoin public key of the wallet. Must be in + * the compressed form (33 bytes long with 02 or 03 prefix). + * @param mainUtxo - The main UTXO of the wallet. Must match the main UTXO + * held by the on-chain Bridge contract. + * @param redeemerOutputScript - The output script that the redeemed funds + * will be locked to. Must not be prepended with length. + */ + buildRequestRedemptionData( + redeemer: ChainIdentifier, + walletPublicKey: Hex, + mainUtxo: BitcoinUtxo, + redeemerOutputScript: Hex + ): Hex } diff --git a/typescript/src/lib/ethereum/tbtc-token.ts b/typescript/src/lib/ethereum/tbtc-token.ts index cc6585db2..f52669611 100644 --- a/typescript/src/lib/ethereum/tbtc-token.ts +++ b/typescript/src/lib/ethereum/tbtc-token.ts @@ -101,7 +101,11 @@ export class EthereumTBTCToken return Hex.from(tx.hash) } - private buildRequestRedemptionData( + // eslint-disable-next-line valid-jsdoc + /** + * @see {TBTCToken#buildRequestRedemptionData} + */ + buildRequestRedemptionData( redeemer: EthereumAddress, walletPublicKey: Hex, mainUtxo: BitcoinUtxo, diff --git a/typescript/src/services/redemptions/index.ts b/typescript/src/services/redemptions/index.ts index 112a3ad8c..38a71ee6d 100644 --- a/typescript/src/services/redemptions/index.ts +++ b/typescript/src/services/redemptions/index.ts @@ -1 +1,2 @@ export * from "./redemptions-service" +export * from "./redeemer-proxy" diff --git a/typescript/src/services/redemptions/redeemer-proxy.ts b/typescript/src/services/redemptions/redeemer-proxy.ts new file mode 100644 index 000000000..6adde77c6 --- /dev/null +++ b/typescript/src/services/redemptions/redeemer-proxy.ts @@ -0,0 +1,22 @@ +import { ChainIdentifier } from "../../lib/contracts" +import { Hex } from "../../lib/utils" + +/** + * Interface defining functions required to route tBTC redemption requests through + * the tBTC bridge by custom integrators. + */ +export interface RedeemerProxy { + /** + * Chain identifier of the redeemer. This is the address that will be able to + * claim the tBTC tokens if anything goes wrong during the redemption process. + */ + redeemerAddress(): ChainIdentifier + + /** + * Requests redemption of tBTC token with determined redemption data. + * @param redemptionData Data required to redeem the tBTC tokens. + * @returns Target chain hash of the request redemption transaction + * (for example, Ethereum transaction hash) + */ + requestRedemption(redemptionData: Hex): Promise +} diff --git a/typescript/src/services/redemptions/redemptions-service.ts b/typescript/src/services/redemptions/redemptions-service.ts index d16cf0330..1480328dc 100644 --- a/typescript/src/services/redemptions/redemptions-service.ts +++ b/typescript/src/services/redemptions/redemptions-service.ts @@ -11,8 +11,9 @@ import { BitcoinTxOutput, BitcoinUtxo, } from "../../lib/bitcoin" -import { BigNumber } from "ethers" +import { BigNumber, BigNumberish } from "ethers" import { Hex } from "../../lib/utils" +import { RedeemerProxy } from "./redeemer-proxy" /** * Service exposing features related to tBTC v2 redemptions. @@ -51,6 +52,90 @@ export class RedemptionsService { ): Promise<{ targetChainTxHash: Hex walletPublicKey: Hex + }> { + const { walletPublicKey, mainUtxo, redeemerOutputScript } = + await this.determineRedemptionData(bitcoinRedeemerAddress, amount) + + const txHash = await this.tbtcContracts.tbtcToken.requestRedemption( + walletPublicKey, + mainUtxo, + redeemerOutputScript, + amount + ) + + return { + targetChainTxHash: txHash, + walletPublicKey, + } + } + + /** + * Requests a redemption of TBTC v2 token into BTC using a custom integration. + * The function builds the redemption data and handles the redemption request + * through the provided redeemer proxy. + * @param bitcoinRedeemerAddress Bitcoin address the redeemed BTC should be + * sent to. Only P2PKH, P2WPKH, P2SH, and P2WSH address types are supported. + * @param amount The amount to be redeemed with the precision of the tBTC + * on-chain token contract. + * @param redeemerProxy Object impleenting functions required to route tBTC + * redemption requests through the tBTC bridge. + * @returns Object containing: + * - Target chain hash of the request redemption transaction + * (for example, Ethereum transaction hash) + * - Bitcoin public key of the wallet asked to handle the redemption. + * Presented in the compressed form (33 bytes long with 02 or 03 prefix). + */ + async requestRedemptionWithProxy( + bitcoinRedeemerAddress: string, + amount: BigNumberish, + redeemerProxy: RedeemerProxy + ): Promise<{ + targetChainTxHash: Hex + walletPublicKey: Hex + }> { + const chainRedeemerAddress = redeemerProxy.redeemerAddress() + + const { walletPublicKey, mainUtxo, redeemerOutputScript } = + await this.determineRedemptionData( + bitcoinRedeemerAddress, + BigNumber.from(amount) + ) + + const redemptionData = + this.tbtcContracts.tbtcToken.buildRequestRedemptionData( + chainRedeemerAddress, + walletPublicKey, + mainUtxo, + redeemerOutputScript + ) + + const targetChainTxHash = await redeemerProxy.requestRedemption( + redemptionData + ) + + return { targetChainTxHash, walletPublicKey } + } + + /** + * + * @param bitcoinRedeemerAddress Bitcoin address redeemed BTC should be + * sent to. Only P2PKH, P2WPKH, P2SH, and P2WSH + * address types are supported. + * @param amount The amount to be redeemed with the precision of the tBTC + * on-chain token contract. + * @returns Object containing: + * - Bitcoin public key of the wallet asked to handle the redemption. + * Presented in the compressed form (33 bytes long with 02 or 03 prefix). + * - Main UTXO of the wallet. + * - Redeemer output script. + */ + protected async determineRedemptionData( + bitcoinRedeemerAddress: string, + amount: BigNumber + ): Promise<{ + walletPublicKey: Hex + mainUtxo: BitcoinUtxo + redeemerOutputScript: Hex }> { const bitcoinNetwork = await this.bitcoinClient.getNetwork() @@ -70,7 +155,7 @@ export class RedemptionsService { const amountToSatoshi = (value: BigNumber): BigNumber => { const satoshiMultiplier = BigNumber.from(1e10) const remainder = value.mod(satoshiMultiplier) - const convertibleAmount = amount.sub(remainder) + const convertibleAmount = value.sub(remainder) return convertibleAmount.div(satoshiMultiplier) } @@ -82,17 +167,7 @@ export class RedemptionsService { amountToSatoshi(amount) ) - const txHash = await this.tbtcContracts.tbtcToken.requestRedemption( - walletPublicKey, - mainUtxo, - redeemerOutputScript, - amount - ) - - return { - targetChainTxHash: txHash, - walletPublicKey, - } + return { walletPublicKey, mainUtxo, redeemerOutputScript } } /** diff --git a/typescript/test/services/redemptions.test.ts b/typescript/test/services/redemptions.test.ts index adbee2cda..84204fd29 100644 --- a/typescript/test/services/redemptions.test.ts +++ b/typescript/test/services/redemptions.test.ts @@ -6,6 +6,7 @@ import { BitcoinTx, BitcoinTxHash, BitcoinUtxo, + EthereumAddress, Hex, NewWalletRegisteredEvent, RedemptionRequest, @@ -28,110 +29,99 @@ import { expect } from "chai" import chaiAsPromised from "chai-as-promised" import { BigNumber, BigNumberish } from "ethers" import { MockTBTCContracts } from "../utils/mock-tbtc-contracts" +import { MockRedeemerProxy } from "../utils/mock-redeemer-proxy" chai.use(chaiAsPromised) describe("Redemptions", () => { describe("RedemptionsService", () => { - describe("requestRedemption", () => { - const data: RedemptionTestData = singleP2PKHRedemptionWithWitnessChange - const { transactionHash, value } = data.mainUtxo - const mainUtxo: BitcoinUtxo = { - transactionHash, - outputIndex: 0, - value, - } - const redeemerOutputScript = - data.pendingRedemptions[0].pendingRedemption.redeemerOutputScript - // Use amount in TBTC token precision (1e18) - const amount = - data.pendingRedemptions[0].pendingRedemption.requestedAmount.mul(1e10) + const data: RedemptionTestData = singleP2PKHRedemptionWithWitnessChange + const { transactionHash, value } = data.mainUtxo + const mainUtxo: BitcoinUtxo = { + transactionHash, + outputIndex: 0, + value, + } + const redeemerOutputScript = + data.pendingRedemptions[0].pendingRedemption.redeemerOutputScript + // Use amount in TBTC token precision (1e18) + const amount = + data.pendingRedemptions[0].pendingRedemption.requestedAmount.mul(1e10) + describe("requestRedemption", () => { let tbtcContracts: MockTBTCContracts - let bitcoinClient: MockBitcoinClient beforeEach(async () => { - tbtcContracts = new MockTBTCContracts() - bitcoinClient = new MockBitcoinClient() + let redemptionsService + ;({ redemptionsService, tbtcContracts } = + prepareRedemptionsService(mainUtxo)) - const walletPublicKeyHash = - BitcoinHashUtils.computeHash160(walletPublicKey) + await redemptionsService.requestRedemption( + BitcoinAddressConverter.outputScriptToAddress( + redeemerOutputScript, + BitcoinNetwork.Testnet + ), + amount + ) + }) - // Prepare NewWalletRegisteredEvent history. Set only relevant fields. - tbtcContracts.bridge.newWalletRegisteredEvents = [ - { - walletPublicKeyHash, - } as NewWalletRegisteredEvent, - ] + it("should submit redemption request with correct arguments", () => { + const tokenLog = tbtcContracts.tbtcToken.requestRedemptionLog - // Prepare wallet data in the Bridge. Set only relevant fields. - tbtcContracts.bridge.setWallet(walletPublicKeyHash.toPrefixedString(), { - state: WalletState.Live, + expect(tokenLog.length).to.equal(1) + expect(tokenLog[0]).to.deep.equal({ walletPublicKey, - pendingRedemptionsValue: BigNumber.from(0), - mainUtxoHash: tbtcContracts.bridge.buildUtxoHash(mainUtxo), - } as Wallet) - - const walletAddress = BitcoinAddressConverter.publicKeyHashToAddress( - walletPublicKeyHash, - true, - BitcoinNetwork.Testnet - ) + mainUtxo, + redeemerOutputScript: redeemerOutputScript, + amount: amount.div(1e10), + }) + }) + }) - // Prepare wallet transaction history for main UTXO lookup. - // Set only relevant fields. + describe("requestRedemptionWithProxy", () => { + const expectedRedeemerAddress = EthereumAddress.from( + "0x5dc726ABE471E13757e5d8221ED1d7a0f21a5c20" + ) + const expectedRedemptionData = Hex.from( + "0x00000000000000000000000048cce57c4d2dbb31eaf79575abf482bbb8dc071d8ffb0f52fcc9a9295f93be404c650e518e965f1a000000000000000000000000d644201d17980ce2109d5dce0cf12fa04333f7c2f9b6d1cf1e6dcb818c4e01a100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012d9151100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000017160014165baee6aebf6c14f72c3fc1f46b2369e6eb7c40000000000000000000" + ) - const transaction = { - transactionHash: mainUtxo.transactionHash, - outputs: [ - { - outputIndex: mainUtxo.outputIndex, - value: mainUtxo.value, - scriptPubKey: BitcoinAddressConverter.addressToOutputScript( - walletAddress, - BitcoinNetwork.Testnet - ), - }, - ], - } + let redeemerProxy: MockRedeemerProxy - const walletTransactions = new Map() - walletTransactions.set( - transaction.transactionHash.toString(), - transaction as BitcoinTx - ) - bitcoinClient.transactions = walletTransactions + let tbtcContracts: MockTBTCContracts - const walletTransactionHashes = new Map() - walletTransactionHashes.set(walletPublicKeyHash.toString(), [ - transaction.transactionHash, - ]) - bitcoinClient.transactionHashes = walletTransactionHashes + beforeEach(async () => { + redeemerProxy = new MockRedeemerProxy(expectedRedeemerAddress) - const redemptionsService = new RedemptionsService( - tbtcContracts, - bitcoinClient - ) + let redemptionsService + ;({ redemptionsService, tbtcContracts } = + prepareRedemptionsService(mainUtxo)) - await redemptionsService.requestRedemption( + await redemptionsService.requestRedemptionWithProxy( BitcoinAddressConverter.outputScriptToAddress( redeemerOutputScript, BitcoinNetwork.Testnet ), - amount + amount, + redeemerProxy ) }) - it("should submit redemption request with correct arguments", () => { - const tokenLog = tbtcContracts.tbtcToken.requestRedemptionLog + it("should submit redemption request through the Redeemer Proxy with correct arguments", () => { + const tokenLog = tbtcContracts.tbtcToken.buildRequestRedemptionLog expect(tokenLog.length).to.equal(1) expect(tokenLog[0]).to.deep.equal({ + redeemer: expectedRedeemerAddress, walletPublicKey, mainUtxo, - redeemerOutputScript, - amount: amount.div(1e10), + redeemerOutputScript: redeemerOutputScript, }) + + const proxyLog = redeemerProxy.requestRedemptionLog + + expect(proxyLog.length).to.equal(1) + expect(proxyLog[0]).to.deep.equal(expectedRedemptionData) }) }) @@ -839,6 +829,69 @@ describe("Redemptions", () => { }) }) +function prepareRedemptionsService(mainUtxo: BitcoinUtxo) { + const tbtcContracts = new MockTBTCContracts() + const bitcoinClient = new MockBitcoinClient() + + const walletPublicKeyHash = BitcoinHashUtils.computeHash160(walletPublicKey) + + // Prepare NewWalletRegisteredEvent history. Set only relevant fields. + tbtcContracts.bridge.newWalletRegisteredEvents = [ + { + walletPublicKeyHash, + } as NewWalletRegisteredEvent, + ] + + // Prepare wallet data in the Bridge. Set only relevant fields. + tbtcContracts.bridge.setWallet(walletPublicKeyHash.toPrefixedString(), { + state: WalletState.Live, + walletPublicKey, + pendingRedemptionsValue: BigNumber.from(0), + mainUtxoHash: tbtcContracts.bridge.buildUtxoHash(mainUtxo), + } as Wallet) + + const walletAddress = BitcoinAddressConverter.publicKeyHashToAddress( + walletPublicKeyHash, + true, + BitcoinNetwork.Testnet + ) + + // Prepare wallet transaction history for main UTXO lookup. + // Set only relevant fields. + const transaction = { + transactionHash: mainUtxo.transactionHash, + outputs: [ + { + outputIndex: mainUtxo.outputIndex, + value: mainUtxo.value, + scriptPubKey: BitcoinAddressConverter.addressToOutputScript( + walletAddress, + BitcoinNetwork.Testnet + ), + }, + ], + } + + const walletTransactions = new Map() + walletTransactions.set( + transaction.transactionHash.toString(), + transaction as BitcoinTx + ) + bitcoinClient.transactions = walletTransactions + + const walletTransactionHashes = new Map() + walletTransactionHashes.set(walletPublicKeyHash.toString(), [ + transaction.transactionHash, + ]) + bitcoinClient.transactionHashes = walletTransactionHashes + + const redemptionsService = new RedemptionsService( + tbtcContracts, + bitcoinClient + ) + return { redemptionsService, tbtcContracts, bitcoinClient } +} + export async function runRedemptionScenario( walletPrivKey: string, bitcoinClient: MockBitcoinClient, diff --git a/typescript/test/utils/mock-redeemer-proxy.ts b/typescript/test/utils/mock-redeemer-proxy.ts new file mode 100644 index 000000000..b9850cea7 --- /dev/null +++ b/typescript/test/utils/mock-redeemer-proxy.ts @@ -0,0 +1,30 @@ +import { ChainIdentifier, Hex, RedeemerProxy } from "../../src" + +export class MockRedeemerProxy implements RedeemerProxy { + private _redeemerAddress: ChainIdentifier + private _requestRedemptionLog: Hex[] = [] + + get requestRedemptionLog(): Hex[] { + return this._requestRedemptionLog + } + + constructor(redeemerAddress: ChainIdentifier) { + this._redeemerAddress = redeemerAddress + } + + redeemerAddress(): ChainIdentifier { + return this._redeemerAddress + } + + requestRedemption(redemptionData: Hex): Promise { + this._requestRedemptionLog.push(redemptionData) + return new Promise((resolve, _) => { + // random transaction hash + resolve( + Hex.from( + "13d4d424cacca7468f1df40d04beb141431d65c75b5eee910c0d0a1208ca85b4" + ) + ) + }) + } +} diff --git a/typescript/test/utils/mock-tbtc-token.ts b/typescript/test/utils/mock-tbtc-token.ts index e68a7451c..0a3e21bfb 100644 --- a/typescript/test/utils/mock-tbtc-token.ts +++ b/typescript/test/utils/mock-tbtc-token.ts @@ -11,13 +11,25 @@ interface RequestRedemptionLog { amount: BigNumber } +interface BuildRequestRedemptionLog { + redeemer: ChainIdentifier + walletPublicKey: Hex + mainUtxo: BitcoinUtxo + redeemerOutputScript: Hex +} + export class MockTBTCToken implements TBTCToken { private _requestRedemptionLog: RequestRedemptionLog[] = [] + private _buildRequestRedemptionLog: BuildRequestRedemptionLog[] = [] get requestRedemptionLog() { return this._requestRedemptionLog } + get buildRequestRedemptionLog() { + return this._buildRequestRedemptionLog + } + totalSupply(blockNumber?: number | undefined): Promise { throw new Error("Method not implemented.") } @@ -40,6 +52,24 @@ export class MockTBTCToken implements TBTCToken { ) } + buildRequestRedemptionData( + redeemer: ChainIdentifier, + walletPublicKey: Hex, + mainUtxo: BitcoinUtxo, + redeemerOutputScript: Hex + ): Hex { + this._buildRequestRedemptionLog.push({ + redeemer, + walletPublicKey, + mainUtxo, + redeemerOutputScript, + }) + + return Hex.from( + "0x00000000000000000000000048cce57c4d2dbb31eaf79575abf482bbb8dc071d8ffb0f52fcc9a9295f93be404c650e518e965f1a000000000000000000000000d644201d17980ce2109d5dce0cf12fa04333f7c2f9b6d1cf1e6dcb818c4e01a100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012d9151100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000017160014165baee6aebf6c14f72c3fc1f46b2369e6eb7c40000000000000000000" + ) + } + getChainIdentifier(): ChainIdentifier { return EthereumAddress.from("0x694cfd89700040163727828AE20B52099C58F02C") }