-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for deposits with extra data #749
Labels
Milestone
Comments
lukasz-zimnoch
added
⛓️ solidity
Solidity contracts
🔌 typescript
TypeScript library
labels
Nov 16, 2023
This was referenced Nov 30, 2023
nkuba
added a commit
that referenced
this issue
Jan 9, 2024
Refs: #749 Here we expose the `revealDepositWithExtraData` function in the `Bridge` contract. This function allows revealing a deposit whose funding transaction embeds arbitrary 32-byte extra data. The new function opens a variety of use cases. An EOA depositor can craft a Bitcoin deposit transaction that allows a third-party smart contract to manage the deposit and provide additional services on top. In this case, 32-byte extra data can be used to securely attribute the deposit to the original EOA depositor. ### The `revealDepositWithExtraData` function The `revealDepositWithExtraData` was added next to the existing `revealDeposit` function to provide backward compatibility of the `Bridge` contract API. The `Deposit` library that does the heavy lifting was refactored to handle both regular deposits without extra data and the new deposits with extra data. The common logic was extracted to the `_revealDeposit` internal function to avoid duplication. Worth noting, the extra data passed in the new flow has to be externalized so tBTC wallets can reconstruct the deposit script and sweep those deposits, just as the regular ones. To do so, we decided to store the extra data in the `Bridge` storage (as part of the `DepositRequest` structure). This approach is not ideal but has some advantages: - Negligible gas overhead for regular deposits without extra data (~2000 of gas) - Easier integration as extra data can be easily fetched from storage by tBTC wallets and third-party smart contracts - Making the `Bridge` aware of the extra data may open some use cases in the future Alternative approaches rely on emitting the extra data using contract events. However: - Extending the existing `DepositReveal` event is not backward compatible. The event signature would change and clients trying to fetch past events that don't contain the extra data field would fail. This is especially problematic for tBTC wallets, SPV maintainers, and block explorers - Using a new event just for emitting the extra data is clunky and makes the integration experience significantly worse ### Changes in the `WalletProposalValidator` contract The `WalletProposalValidator` contract is used by tBTC off-chain wallet clients to validate incoming deposit sweep proposals. This contract needs to reconstruct deposit scripts so must be aware of the new alternative format with 32-byte extra data. As part of this pull request, we are adjusting this contract to properly validate deposit sweep proposals that include deposits with extra data. ### Impact on gas costs and contract size Before the presented change, the gas costs of functions and size of affected contract/libraries was as follows: ``` ·------------------------------|---------------------------|--------------|-----------------------------· | Solc version: 0.8.17 · Optimizer enabled: true · Runs: 1000 · Block limit: 30000000 gas │ ·······························|···························|··············|······························ | Methods │ ·············|·················|·············|·············|··············|···············|·············· | Contract · Method · Min · Max · Avg · # calls · eur (avg) │ ·············|·················|·············|·············|··············|···············|·············· | Bridge · revealDeposit · 82767 · 105463 · 102057 · 170 · - │ ·············|·················|·············|·············|··············|···············|·············· ``` ``` ·-------------------------------|-------------|---------------· | Contract Name · Size (KB) · Change (KB) │ ································|·············|················ | Bridge · 21.264 · │ ································|·············|················ | Deposit · 6.327 · │ ································|·············|················ | WalletProposalValidator · 11.266 · │ ································|·············|················ ``` After introducing `revealDepositWithExtraData`, those are as follows: ``` ·------------------------------·············|---------------------------|--------------|-----------------------------· | Solc version: 0.8.17 · Optimizer enabled: true · Runs: 1000 · Block limit: 30000000 gas │ ············································|···························|··············|······························ | Methods │ ·············|······························|·············|·············|··············|···············|·············· | Contract · Method · Min · Max · Avg · # calls · eur (avg) │ ·············|······························|·············|·············|··············|···············|·············· | Bridge · revealDeposit · 85270 · 107966 · 104562 · 170 · - │ ·············|······························|·············|·············|··············|···············|·············· | Bridge · revealDepositWithExtraData · 124176 · 126773 · 125872 · 12 · - │ ·············|······························|·············|·············|··············|···············|·············· ``` ``` ·-------------------------------|-------------|---------------· | Contract Name · Size (KB) · Change (KB) │ ································|·············|················ | Bridge · 21.563 · │ ································|·············|················ | Deposit · 6.894 · │ ································|·············|················ | WalletProposalValidator · 11.520 · │ ································|·············|················ ``` ### Next steps This change is the most important one towards full support of deposits with extra data. However, comprehensive support requires some additional steps: - Add support in the tBTC off-chain client - Add support in the tBTC SDK
nkuba
added a commit
that referenced
this issue
Jan 19, 2024
Refs: #749 Pull request #760 introduced a possibility to embed 32-byte extra data within the deposit script. This simple change opens multiple possibilities. Notably, third-party smart contracts can now act as depositor proxies and reveal deposits on depositors' behalf. This way, proxy contracts receive minted TBTC and can provide extra services without requiring additional actions from the depositor (e.g., deposit it to a yield protocol or bridge it to an L2 chain). This, in turn, empowers third-party protocols to use tBTC as a foundation and propose additional value on top of it. The goal of this pull request is to facilitate the integration of such third-party protocols through tBTC SDK. ### The `DepositorProxy` interface First of all, we are introducing the `DepositorProxy` interface that represents a third-party depositor proxy contract in a chain-agnostic way. A third-party integrator willing to relay deposits is expected to provide an implementation of this interface and inject it into the tBTC SDK. The SDK uses the instance of the `DepositorProxy` to prepare the right deposit script (thus deposit BTC address) and notifies that instance once the deposit is funded and ready for minting. How minting is initialized depends on the proxy implementation thus this logic is abstracted as the `revealDeposit` function exposed by the `DepositorProxy` interface. This way, the SDK is responsible for the heavy lifting around deposit construction while the depositor proxy must only finalize the process by doing their own logic and, reveal the deposit to the `Bridge` contract. To facilitate the job for Ethereum-based proxies, we are also exposing the `EthereumDepositorProxy` abstract class. This component can serve as a base for classes interacting with Ethereum depositor proxy contracts that relay deposit data to the Ethereum `Bridge`. The `EthereumDepositorProxy` aims to make that part easier. ### The `initiateDepositWithProxy` function To provide a single point of entrance to the depositor proxy flow, we are exposing the `initiateDepositWithProxy` function. This function is available from the top-level SDK interface, alongside the regular `initiateDeposit` function triggering the non-proxy deposit flow. The signature of the `initiateDepositWithProxy` function is similar to `initiateDeposit`. The difference is that it expects an instance of the `DepositProxy` interface. It also accepts optional 32-byte `extraData` that can be used to embed additional data within the deposit script (e.g. data allowing to attribute the deposit to the original depositor). The `initiateDepositWithProxy` returns a `Deposit` object that represents the initiated deposit process. ### Usage Here is a brief example illustrating what a third-party integrator should do to use their contract as a deposit intermediary. Let's suppose the integrator implemented an `ExampleDepositor` contract that exposes a `revealDepositOnBehalf` function which takes a deposit and reveals it to the `Bridge` on behalf of the original depositor: ```typescript contract ExampleDepositor { Bridge public bridge; function revealDepositOnBehalf( BitcoinTx.Info calldata fundingTx, DepositRevealInfo calldata reveal, address originalDepositor, ) external { // Do some additional logic, e.g. attribute the deposit to the original depositor. bytes32 extraData = bytes32(abi.encodePacked(originalDepositor)); bridge.revealDepositWithExtraData(fundingTx, reveal, extraData); } } ``` In that case, the off-chain part leveraging tBTC SDK can be as follows: ```typescript import { BitcoinRawTxVectors, ChainIdentifier, DepositReceipt, EthereumDepositorProxy, Hex, TBTC, } from "@keep-network/tbtc-v2.ts" import { Contract as EthersContract } from "@ethersproject/contracts" import { JsonRpcProvider, Provider } from "@ethersproject/providers" import { Signer, Wallet } from "ethers" // Address of the ExampleDepositor contract. const contractAddress = "..." // ABI of the ExampleDepositor contract. const contractABI = "..." class ExampleDepositor extends EthereumDepositorProxy { // Ethers handle pointing to the ExampleDepositor contract. private contractHandle: EthersContract constructor(signer: Signer) { super(contractAddress) this.contractHandle = new EthersContract( contractAddress, contractABI, signer ) } revealDeposit( depositTx: BitcoinRawTxVectors, depositOutputIndex: number, deposit: DepositReceipt, vault?: ChainIdentifier ): Promise<Hex> { // Additional logic, if necessary. // Prepare parameters for the contract call. const { fundingTx, reveal, extraData } = this.packRevealDepositParameters( depositTx, depositOutputIndex, deposit, vault ) // Call the depositor contract function that reveals the deposit to the // Bridge. return this.contractHandle.revealDepositOnBehalf( fundingTx, reveal, this.decodeExtraData(extraData) // Contract function expects originalDepositor as third parameter ) } private decodeExtraData(extraData: string): string { // Extract the originalDepositor address from extraData. // This should be a reverse operation to extra data encoding // implemented in the revealDepositOnBehalf function of // the ExampleDepositor contract. return "..." } } async function main() { // Create a readonly Ethers provider. const provider: Provider = new JsonRpcProvider("...") // Create an instance of the ExampleDepositor class. Pass an Ethers // signer as constructor parameter. This is needed because the // ExampleDepositor issues transactions using an Ethers contract handle. const exampleDepositor: ExampleDepositor = new ExampleDepositor( new Wallet("...", provider) ) // Create an instance of the tBTC SDK. It is enough to pass a readonly // Ethers provider as parameter. In this example, the SDK does not issue // transactions directly but relies on the ExampleDepositor // instead. const sdk: TBTC = await TBTC.initializeMainnet(provider) // Get BTC recovery address for the deposit. const bitcoinRecoveryAddress: string = "..." // Determine the address of the original depositor who will actually // own the deposit. const originalDepositor: string = "..." // Encode the original depositor as 32-byte deposit extra data. This // must be done in the same way as in the ExampleDepositor solidity contract // (see revealDepositOnBehalf function). const extraData: Hex = encodeExtraData(originalDepositor) // Initiate the deposit with the proxy. const deposit = await sdk.deposits.initiateDepositWithProxy( bitcoinRecoveryAddress, exampleDepositor, extraData ) // Get BTC deposit address and send funds to it. const depositAddress: string = await deposit.getBitcoinAddress() // Initiate minting once BTC transaction is made. This will call // revealDepositOnBehalf function of the ExampleDepositor contract // under the hood. const ethTxHash: Hex = await deposit.initiateMinting() console.log( `Minting initiated. ETH transaction hash: ${ethTxHash.toPrefixedString()}` ) } ```
tomaszslabon
added a commit
to keep-network/keep-core
that referenced
this issue
Jan 22, 2024
Refs: keep-network/tbtc-v2#749 The `Bridge` contract supports a new shape of the deposit script that allows embedding arbitrary 32-byte extra data. Here we introduce necessary adjustments in the wallet client to make it able to sweep those deposits.
tomaszslabon
added a commit
that referenced
this issue
Jan 23, 2024
Refs: #749 The `Bridge` contract was upgraded on Sepolia. A new function `revealDepositWithExtraData` has been added. Here we update the deployment artifact embedded in the SDK to make this new function accessible from there.
Closing as implementation and testing are done. Leaving the Deployment checklist as a cheat sheet to be used during deployment. |
tomaszslabon
added a commit
to keep-network/keep-core
that referenced
this issue
Jan 25, 2024
Refs: keep-network/tbtc-v2#749 Bitcoin scripts of deposits embedding 32-byte extra data are longer (126 bytes) than the scripts of regular deposits not holding extra data (92 bytes). That means the actual virtual size of the resulting deposit sweep transaction is greater than the size assumed by the sweep fee estimator. Here we adjust the fee estimator to always take the longer script for calculations to avoid underestimation of the transaction fee. This approach is not ideal but is safe as it makes sure transactions are not rejected due to too low network fees.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This task is about implementing a feature allowing to reveal deposits with extra data, as specified in RFC 11.
This feature opens multiple possibilities. Notably, it allows creating intermediary smart contracts that can reveal deposits on behalf of the depositor and provide additional services regarding minted TBTC (e.g., deposit it to a yield protocol or bridge it to an L2 chain). This, in turn, empowers third-party protocols to use tBTC as a foundation and propose additional value on top of it.
The scope of this task includes:
Bridge
Development work
revealDepositWithExtraData
in theBridge
contract #760Bridge
deployment artifact for Sepolia #777Deployment checklist
The text was updated successfully, but these errors were encountered: