Skip to content
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

Implementation of BitcoinRedeemer contract #309

Closed
wants to merge 25 commits into from
Closed

Conversation

nkuba
Copy link
Member

@nkuba nkuba commented Mar 10, 2024

Closes: #159
Closes: #183
Closes: #161

In this PR we implement unstaking process of stBTC shares to Bitcoin via tBTC Bridge in gasless way.

The gasless experience is achieved by EIP-712 signatures over typed data.
The parameters the staker has to sign in RedeemToBitcoin structure are:

  • owner - address of the staker,
  • shares - number of stBTC shares to redeem,
  • bitcoinOutputScript - output script for Bitcoin transaction, where the user want's the funds to be directed to,
  • nonce - nonce for the signature,
  • deadline - deadline for the signature.

Once the signature is available anyone (in our case this will be a relayer bot) can call stBTC.redeemToBitcoinWithPermit function to initiate redemption.
The function validates the signature and allows BitcoinRedeemer contract to use stBTC staker's shares to redeem stBTC to tBTC and bridge tBTC to Bitcoin via tBTC Bridge.

TODO:

  • unit tests

nkuba added 7 commits March 10, 2024 23:27
The contract is used to integrate stBTC unstaking with bridging to
Bitcoin via tBTC Bridge.

The `requestRedemption` function expects the owner of stBTC shares to
approve stBTC token shares to the BticoinRedeemer contract. Then when
`requestRedemption` function is called it redeems stBTC and calls
TBTC token contract to redeem tBTC to Bitcoin.
The extension allows approvals to be made via signatures as per
EIP-2612, which opens possibilities for gasless token transfers.
Here we add redeemToBitcoinWithPermit function that uses EIP-712
signatures to increase allowance from the owner to the BitcoinRedeemer
contract that will request redemption of stBTC to tBTC and tBTC to
Bitcoin via tBTC Bridge.
Copy link

netlify bot commented Mar 10, 2024

Deploy Preview for acre-dapp-testnet failed.

Name Link
🔨 Latest commit 3ea84f8
🔍 Latest deploy log https://app.netlify.com/sites/acre-dapp-testnet/deploys/660dad37c489300008cfa2e6

The event is emitted after stbtc.redeem call, but we control the stBTC
contract, so there is no risk.
@nkuba nkuba mentioned this pull request Mar 10, 2024
nkuba added 2 commits March 11, 2024 00:49
The directory is needed by the deployemnt scripts as it contains
`validations.json` used by open zeppelin hardhat-upgrades plugin.
Without it jobs were failing:
```
 1) AcreBitcoinDepositor
       "before all" hook in "AcreBitcoinDepositor":
     ERROR processing /home/runner/work/acre/acre/core/deploy/04_deploy_bitcoin_redeemer.ts:
Error: Validations cache not found. Recompile with `hardhat compile --force`
    at readValidations (/home/runner/work/acre/acre/node_modules/.pnpm/@OpenZeppelin[email protected]_@[email protected]_@nomicfoundation+h_7qmii226sdyciusj57th5lkecy/node_modules/@openzeppelin/hardhat-upgrades/src/utils/validations.ts:57:13)
    at async getDeployData (/home/runner/work/acre/acre/node_modules/.pnpm/@OpenZeppelin[email protected]_@[email protected]_@nomicfoundation+h_7qmii226sdyciusj57th5lkecy/node_modules/@openzeppelin/hardhat-upgrades/src/utils/deploy-impl.ts:45:23)
    at async deployProxyImpl (/home/runner/work/acre/acre/node_modules/.pnpm/@OpenZeppelin[email protected]
```
@nkuba nkuba requested a review from dimpar March 11, 2024 13:10
@nkuba nkuba self-assigned this Mar 11, 2024
nkuba added 5 commits March 11, 2024 22:45
We use the wrapper in tests instead the snapshots, so we want to be
consistent.
For consistency with BitcoinRedeemerUpdated and DispatcherUpdated we
added old treasury address in the event.
@nkuba nkuba marked this pull request as ready for review March 11, 2024 22:02
/// @dev Hash of the type for redeeming stBTC to Bitcoin with permit.
bytes32 private constant REDEEM_TO_BITCOIN_TYPEHASH =
keccak256(
"RedeemToBitcoin(address owner,uint256 shares,bytes bitcoinOutputScript,uint256 nonce,uint256 deadline)"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't bitcoinOutputScript be bytes32?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, bytes32 redeemerOutputScriptHash what brought my attention. I think bytes can dynamically adjust so yea, should be good


_approve(owner, address(bitcoinRedeemer), shares);

return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is returned?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Result of bitcoinRedeemer.withdrawAssetsAndUnmint. Which is nothing, as this function doesn't return anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why do we need return here? When reading the code I find it confusing.

uint256 shares,
bytes calldata tbtcRedemptionData
) public {
uint256 tbtcAmount = stbtc.redeem(shares, address(this), owner);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to double check.. I take that this call would fail if the requestRedemption is called directly skipping redeemToBitcoinWithPermit because of the approval?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question! I need to revisit the implementation, as I found out that there is a bug for the direct call path.

/// @title Bitcoin Redeemer
/// @notice This contract facilitates redemption of stBTC tokens to Bitcoin through
/// tBTC redemption process.
contract BitcoinRedeemer is Initializable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe AcreBitcoinRedeemer just like AcreBitcoinDepositor?

/// tBTC redemption process.
contract BitcoinRedeemer is Initializable {
/// Interface for tBTC token contract.
ITBTCToken public tbtcToken;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we already import "@keep-network/tbtc-v2" should we maybe import TBTC type instead?

nkuba added 2 commits March 13, 2024 10:40
This function name should better describe what is happening inside.
This path allows the user to sign exact amount of tBTC tokens to
withdraw. It complements RedeemToBitcoin path and is aligned with
withdraw and redeem functions defined by ERC4626.
@nkuba nkuba marked this pull request as draft March 13, 2024 16:15
nkuba added 7 commits March 13, 2024 20:55
This code is copied from OpenZeppelin Upgradable Contracts library:
https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/3cf491630086558f50504d88e76bb4e736c738ab/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol
With the following changes:
- replaced relative import paths with `@openzeppelin/contracts-upgradeable`,
Instead of requiring the user to approve and request redemption in two
transactions we use approveAndCall pattern to cover it within one
transaction.
@nkuba
Copy link
Member Author

nkuba commented Apr 3, 2024

Closing in favor of #335

@nkuba nkuba closed this Apr 3, 2024
dimpar added a commit that referenced this pull request Apr 9, 2024
Closes: #159

In this PR we integrate the withdrawal of tBTC from stBTC vault with
tBTC Bridge redemption, so the user can withdraw directly to Bitcoin in
one transaction.

For the Bitcoin-only experience, it is expected that the owner of the
stake will be Safe with Bitcoin message signatures verification
capability as per
https://github.com/thesis/orangekit/tree/main/solidity.

This implementation is different from the initial implementation in
#309 as we removed ERC20 permits to
handle withdrawals, as dApp users will produce Bitcoin signatures for
the Safe, not the EIP-712 Ethereum signatures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants