Skip to content

Commit

Permalink
Simplify requestRedemption API
Browse files Browse the repository at this point in the history
Get rid of two parms:
- `vault`- `requestRedemption` is exposed on `TBTCToken` and there is no
  other choice than working with `TBTCVault` contract for this
  interaction. We should limit the surface of developer errors- we can
  resolve the `vault` address in the `TBTCToken` handle implementation
  from the `TBTCTokenContract.owner()` call.
- `redeemer`- we can get the address from the contract instance using
  `this._instance.signer.getAddress()`. The `TBTCToken` contract always
  passes `msg.sender` to the `receiveApproval` fn so it's not possible
  to request redemption for someone else using approve and call pattern.
  • Loading branch information
r-czajkowski committed Jul 5, 2023
1 parent b5190e1 commit 06601b9
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 75 deletions.
52 changes: 15 additions & 37 deletions typescript/src/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,40 +381,6 @@ export interface TBTCVault {
getOptimisticMintingFinalizedEvents: GetEvents.Function<OptimisticMintingFinalizedEvent>
}

/**
* Represnts data required to request redemption.
*/
export interface RequestRedemptionData {
/**
* On-chain identifier of the redeemer.
*/
redeemer: Identifier
/**
* The Bitcoin public key of the wallet. Must be in the compressed form (33
* bytes long with 02 or 03 prefix).
*/
walletPublicKey: string
/**
* The main UTXO of the wallet. Must match the main UTXO held by the on-chain
* Bridge contract.
*/
mainUtxo: UnspentTransactionOutput
/**
* The output script that the redeemed funds will be locked to. Must be
* un-prefixed and not prepended with length.
*/
redeemerOutputScript: string
/**
* The amount to be redeemed with the precision of the tBTC on-chain token
* contract.
*/
amount: BigNumber
/**
* The vault address.
*/
vault: Identifier
}

/**
* Interface for communication with the TBTC v2 token on-chain contract.
*/
Expand All @@ -436,9 +402,21 @@ export interface TBTCToken {
* from the tBTC on-chain token contract. Then the tBTC token contract calls
* the `receiveApproval` function from the `TBTCVault` contract which burns
* tBTC tokens and requests redemption.
* @param requestRedemptionData Data required to request redemption @see
* {@link RequestRedemptionData}.
* @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 be un-prefixed and not prepended with
* length.
* @param amount - The amount to be redeemed with the precision of the tBTC
* on-chain token contract.
* @returns Transaction hash of the approve and call transaction.
*/
requestRedemption(requestRedemptionData: RequestRedemptionData): Promise<Hex>
requestRedemption(
walletPublicKey: string,
mainUtxo: UnspentTransactionOutput,
redeemerOutputScript: string,
amount: BigNumber
): Promise<Hex>
}
52 changes: 35 additions & 17 deletions typescript/src/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
TBTCToken as ChainTBTCToken,
Identifier as ChainIdentifier,
GetEvents,
RequestRedemptionData,
} from "./chain"
import {
BigNumber,
Expand Down Expand Up @@ -1106,15 +1105,27 @@ export class TBTCToken
* @see {ChainTBTCToken#requestRedemption}
*/
async requestRedemption(
requestRedemptionData: RequestRedemptionData
walletPublicKey: string,
mainUtxo: UnspentTransactionOutput,
redeemerOutputScript: string,
amount: BigNumber
): Promise<Hex> {
const { vault, amount, ...restData } = requestRedemptionData
const redeemer = await this._instance?.signer?.getAddress()
if (!redeemer) {
throw new Error("Signer not provided.")
}

const extraData = this.buildRequestRedemptionData(restData)
const vault = await this._instance.owner()
const extraData = this.buildRequestRedemptionData(
Address.from(redeemer),
walletPublicKey,
mainUtxo,
redeemerOutputScript
)

const tx = await sendWithRetry<ContractTransaction>(async () => {
return await this._instance.approveAndCall(
vault.identifierHex,
vault,
amount,
extraData.toPrefixedString()
)
Expand All @@ -1124,34 +1135,41 @@ export class TBTCToken
}

private buildRequestRedemptionData(
requestRedemptionData: Omit<RequestRedemptionData, "amount" | "vault">
redeemer: Address,
walletPublicKey: string,
mainUtxo: UnspentTransactionOutput,
redeemerOutputScript: string
): Hex {
const { redeemer, ...restData } = requestRedemptionData
const { walletPublicKeyHash, mainUtxo, prefixedRawRedeemerOutputScript } =
this.buildBridgeRequestRedemptionData(restData)
const {
walletPublicKeyHash,
prefixedRawRedeemerOutputScript,
mainUtxo: _mainUtxo,
} = this.buildBridgeRequestRedemptionData(
walletPublicKey,
mainUtxo,
redeemerOutputScript
)

return Hex.from(
utils.defaultAbiCoder.encode(
["address", "bytes20", "bytes32", "uint32", "uint64", "bytes"],
[
redeemer.identifierHex,
walletPublicKeyHash,
mainUtxo.txHash,
mainUtxo.txOutputIndex,
mainUtxo.txOutputValue,
_mainUtxo.txHash,
_mainUtxo.txOutputIndex,
_mainUtxo.txOutputValue,
prefixedRawRedeemerOutputScript,
]
)
)
}

private buildBridgeRequestRedemptionData(
data: Pick<
RequestRedemptionData,
"mainUtxo" | "walletPublicKey" | "redeemerOutputScript"
>
walletPublicKey: string,
mainUtxo: UnspentTransactionOutput,
redeemerOutputScript: string
) {
const { walletPublicKey, mainUtxo, redeemerOutputScript } = data
const walletPublicKeyHash = `0x${computeHash160(walletPublicKey)}`

const mainUtxoParam = {
Expand Down
14 changes: 4 additions & 10 deletions typescript/src/redemption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ export interface RedemptionRequest {
}

/**
* Requests a redemption from the on-chain Bridge contract.
* @param redeemer - On-chain identifier of the redeemer.
* Requests a redemption of tBTC into BTC.
* @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
Expand All @@ -65,27 +64,22 @@ export interface RedemptionRequest {
* be locked to. Must be un-prefixed and not prepended with length.
* @param amount - The amount to be redeemed with the precision of the tBTC
* on-chain token contract.
* @param vault - The vault address.
* @param tBTCToken - Handle to the TBTCToken on-chain contract.
* @returns Transaction hash of the request redemption transaction.
*/
export async function requestRedemption(
redeemer: Identifier,
walletPublicKey: string,
mainUtxo: UnspentTransactionOutput,
redeemerOutputScript: string,
amount: BigNumber,
vault: Identifier,
tBTCToken: TBTCToken
): Promise<Hex> {
return await tBTCToken.requestRedemption({
redeemer,
return await tBTCToken.requestRedemption(
walletPublicKey,
mainUtxo,
redeemerOutputScript,
amount,
vault,
})
amount
)
}

/**
Expand Down
7 changes: 0 additions & 7 deletions typescript/test/redemption.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import * as chai from "chai"
import chaiAsPromised from "chai-as-promised"
import { expect } from "chai"
import { BigNumberish, BigNumber } from "ethers"
import { Address } from "../src/ethereum"
import { MockTBTCToken } from "./utils/mock-tbtc-token"

chai.use(chaiAsPromised)
Expand All @@ -46,20 +45,16 @@ describe("Redemption", () => {
const redeemerOutputScript =
data.pendingRedemptions[0].pendingRedemption.redeemerOutputScript
const amount = data.pendingRedemptions[0].pendingRedemption.requestedAmount
const vault = Address.from("0xb622eA9D678ddF15135a20d59Ff26D28eC246bfB")
const token: MockTBTCToken = new MockTBTCToken()
const redeemer = Address.from("0x117284D8C50f334a1E2b7712649cB23C7a04Ae74")

beforeEach(async () => {
bcoin.set("testnet")

await requestRedemption(
redeemer,
walletPublicKey,
mainUtxo,
redeemerOutputScript,
amount,
vault,
token
)
})
Expand All @@ -69,12 +64,10 @@ describe("Redemption", () => {

expect(tokenLog.length).to.equal(1)
expect(tokenLog[0]).to.deep.equal({
redeemer,
walletPublicKey,
mainUtxo,
redeemerOutputScript,
amount,
vault,
})
})
})
Expand Down
22 changes: 18 additions & 4 deletions typescript/test/utils/mock-tbtc-token.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { RequestRedemptionData, TBTCToken } from "../../src/chain"
import { TBTCToken } from "../../src/chain"
import { Hex } from "../../src/hex"
import { BigNumber } from "ethers"
import { UnspentTransactionOutput } from "../../src/bitcoin"

interface RequestRedemptionLog extends RequestRedemptionData {}
interface RequestRedemptionLog {
walletPublicKey: string
mainUtxo: UnspentTransactionOutput
redeemerOutputScript: string
amount: BigNumber
}

export class MockTBTCToken implements TBTCToken {
private _requestRedemptionLog: RequestRedemptionLog[] = []
Expand All @@ -16,9 +22,17 @@ export class MockTBTCToken implements TBTCToken {
}

async requestRedemption(
requestRedemptionData: RequestRedemptionData
walletPublicKey: string,
mainUtxo: UnspentTransactionOutput,
redeemerOutputScript: string,
amount: BigNumber
): Promise<Hex> {
this._requestRedemptionLog.push(requestRedemptionData)
this._requestRedemptionLog.push({
walletPublicKey,
mainUtxo,
redeemerOutputScript,
amount,
})

return Hex.from(
"0xf7d0c92c8de4d117d915c2a8a54ee550047f926bc00b91b651c40628751cfe29"
Expand Down

0 comments on commit 06601b9

Please sign in to comment.