From 8a3ee6f843450724cacaf912ae5f8be6cd9e6d42 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Tue, 11 Jun 2024 16:49:42 +0200 Subject: [PATCH 1/6] Expose function to build redemption data In this PR we expose a function to build redemption data in the raw bytes format as expected by the tBTC Bridge. This function can be used by the custom integrator contracts to build the redemption data required to request tBTC redemption. Example of the integrator is Acre's [BitcoinRedeemer contract](https://github.com/thesis/acre/blob/main/solidity/contracts/BitcoinRedeemer.sol) that requires the redemption data to handle stBTC -> tBTC -> Bitcoin conversion in one transaction. --- typescript/src/lib/contracts/tbtc-token.ts | 21 +++++ typescript/src/lib/ethereum/tbtc-token.ts | 6 +- .../redemptions/redemptions-service.ts | 84 ++++++++++++++++--- 3 files changed, 97 insertions(+), 14 deletions(-) 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/redemptions-service.ts b/typescript/src/services/redemptions/redemptions-service.ts index d16cf0330..ac32392d3 100644 --- a/typescript/src/services/redemptions/redemptions-service.ts +++ b/typescript/src/services/redemptions/redemptions-service.ts @@ -1,4 +1,5 @@ import { + ChainIdentifier, RedemptionRequest, TBTCContracts, WalletState, @@ -51,6 +52,73 @@ 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, + } + } + + /** + * 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 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. + * @param chainRedeemerAddress 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 Data needed to request a redemption. + */ + async buildRedemptionData( + bitcoinRedeemerAddress: string, + amount: BigNumber, + chainRedeemerAddress: ChainIdentifier + ): Promise { + const { walletPublicKey, mainUtxo, redeemerOutputScript } = + await this.determineRedemptionData(bitcoinRedeemerAddress, amount) + + return this.tbtcContracts.tbtcToken.buildRequestRedemptionData( + chainRedeemerAddress, + walletPublicKey, + mainUtxo, + redeemerOutputScript + ) + } + + /** + * + * @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 +138,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 +150,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 } } /** @@ -103,7 +161,7 @@ export class RedemptionsService { * @param amount The amount to be redeemed in satoshis. * @returns Promise with the wallet details needed to request a redemption. */ - protected async findWalletForRedemption( + async findWalletForRedemption( redeemerOutputScript: Hex, amount: BigNumber ): Promise<{ From b1b73f82b7405841a8b0859f017202a2ee8464c0 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Fri, 7 Jun 2024 14:46:26 +0200 Subject: [PATCH 2/6] Introduce redemption with proxy This commit introduces a new redemption flow that uses a proxy implementation to request the redemption of tBTC. It gives flexibility to the external integrators to use the redemption data required by the tBTC protocol to request the redemption of tBTC in their contracts. It can also be used to relay the transaction submission with external relayer (e.g. Gelato). --- .../services/redemptions/redeemer-proxy.ts | 22 +++++++ .../redemptions/redemptions-service.ts | 63 ++++++++++++------- 2 files changed, 62 insertions(+), 23 deletions(-) create mode 100644 typescript/src/services/redemptions/redeemer-proxy.ts 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 ac32392d3..dec45355b 100644 --- a/typescript/src/services/redemptions/redemptions-service.ts +++ b/typescript/src/services/redemptions/redemptions-service.ts @@ -1,5 +1,4 @@ import { - ChainIdentifier, RedemptionRequest, TBTCContracts, WalletState, @@ -12,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. @@ -70,33 +70,50 @@ export class RedemptionsService { } /** - * 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 bitcoinRedeemerAddress Bitcoin address redeemed BTC should be - * sent to. Only P2PKH, P2WPKH, P2SH, and P2WSH - * address types are supported. + * 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 chainRedeemerAddress 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 Data needed to request a redemption. + * 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 buildRedemptionData( + async requestRedemptionWithProxy( bitcoinRedeemerAddress: string, - amount: BigNumber, - chainRedeemerAddress: ChainIdentifier - ): Promise { + amount: BigNumberish, + redeemerProxy: RedeemerProxy + ): Promise<{ + targetChainTxHash: Hex + walletPublicKey: Hex + }> { + const chainRedeemerAddress = redeemerProxy.redeemerAddress() + const { walletPublicKey, mainUtxo, redeemerOutputScript } = - await this.determineRedemptionData(bitcoinRedeemerAddress, amount) + await this.determineRedemptionData( + bitcoinRedeemerAddress, + BigNumber.from(amount) + ) - return this.tbtcContracts.tbtcToken.buildRequestRedemptionData( - chainRedeemerAddress, - walletPublicKey, - mainUtxo, - redeemerOutputScript + const redemptionData = + this.tbtcContracts.tbtcToken.buildRequestRedemptionData( + chainRedeemerAddress, + walletPublicKey, + mainUtxo, + redeemerOutputScript + ) + + const targetChainTxHash = await redeemerProxy.requestRedemption( + redemptionData ) + + return { targetChainTxHash, walletPublicKey } } /** From b4f57f168d30d59d90f0c35131e42fb68bc4fcee Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Fri, 7 Jun 2024 14:53:08 +0200 Subject: [PATCH 3/6] Make findWalletForRedemption protected again The function was unintentionally changed to public in the previous commit. Here we revert the change back. --- typescript/src/services/redemptions/redemptions-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/src/services/redemptions/redemptions-service.ts b/typescript/src/services/redemptions/redemptions-service.ts index dec45355b..1480328dc 100644 --- a/typescript/src/services/redemptions/redemptions-service.ts +++ b/typescript/src/services/redemptions/redemptions-service.ts @@ -178,7 +178,7 @@ export class RedemptionsService { * @param amount The amount to be redeemed in satoshis. * @returns Promise with the wallet details needed to request a redemption. */ - async findWalletForRedemption( + protected async findWalletForRedemption( redeemerOutputScript: Hex, amount: BigNumber ): Promise<{ From ebd4939be1a2082f3b0be52b33104afdde5d622e Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Fri, 7 Jun 2024 15:02:55 +0200 Subject: [PATCH 4/6] Export RedeemerProxy interface for external usage --- typescript/src/services/redemptions/index.ts | 1 + 1 file changed, 1 insertion(+) 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" From 1dd5a3070310640e9fea34850e9871c97948a597 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Tue, 11 Jun 2024 15:53:42 +0200 Subject: [PATCH 5/6] Add unit test to cover requestRedemptionWithProxy function Added tests to cover requestRedemptionWithProxy function in redemptions service. Extracted common setup to a function shared by two tests. --- typescript/test/services/redemptions.test.ts | 197 ++++++++++++------- typescript/test/utils/mock-redeemer-proxy.ts | 30 +++ typescript/test/utils/mock-tbtc-token.ts | 30 +++ 3 files changed, 185 insertions(+), 72 deletions(-) create mode 100644 typescript/test/utils/mock-redeemer-proxy.ts 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") } From 548332d5ee2202b7fee6e75910643243552ff02b Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Tue, 11 Jun 2024 16:05:56 +0200 Subject: [PATCH 6/6] Rebuild Typescript API docs --- typescript/api-reference/README.md | 1 + .../classes/EthereumTBTCToken.md | 10 ++- .../classes/RedemptionsService.md | 75 +++++++++++++++++-- .../api-reference/interfaces/RedeemerProxy.md | 53 +++++++++++++ .../api-reference/interfaces/TBTCToken.md | 28 +++++++ 5 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 typescript/api-reference/interfaces/RedeemerProxy.md 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)