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

Estimate staking fees #281

Merged
merged 24 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2b3a1a3
Add `depositorFeeDivisor` to Depositor contract
r-czajkowski Feb 29, 2024
61dc761
Estimate staking fees
r-czajkowski Feb 29, 2024
7bbdcf8
Expose fee breakdown for staking operation
r-czajkowski Feb 29, 2024
57fdf57
Merge branch 'main' into sdk-estimate-staking-fees
r-czajkowski Mar 14, 2024
166dac9
Group the staking fees by networks
r-czajkowski Mar 14, 2024
b567014
Expose the `estimateStakingFees` in staking module
r-czajkowski Mar 14, 2024
ac1c50b
Update `estimateStakingFees`
r-czajkowski Mar 14, 2024
6574179
Update `estimateStakingFees` Ethereum function
r-czajkowski Mar 14, 2024
de56282
Update `estimateStakingFees` in staking module
r-czajkowski Mar 18, 2024
554c75e
Merge branch 'main' into sdk-estimate-staking-fees
r-czajkowski Mar 18, 2024
59c15b8
Rename field in `AcreStakingFees` type
r-czajkowski Mar 18, 2024
c706f88
Combine all tBTC Bridge params in one obj and fn
r-czajkowski Mar 18, 2024
a743b88
Add `fromSatoshi` utils function
r-czajkowski Mar 18, 2024
fb089a5
Leave `TODO`
r-czajkowski Mar 18, 2024
a127322
Cache `depositorFeeDivisor`
r-czajkowski Mar 18, 2024
013880d
Merge branch 'main' into sdk-estimate-staking-fees
r-czajkowski Apr 16, 2024
26a1c63
Replace `staking` term with `deposit`
r-czajkowski Apr 16, 2024
4a454b8
Update deposit fees calculation
r-czajkowski Apr 16, 2024
b26a8f9
Update `estimateDepositFees` fn
r-czajkowski Apr 16, 2024
0128a15
Fix typos in comments
r-czajkowski Apr 16, 2024
cd2da4b
Simplify statement in `depositorFeeDivisor` fn
r-czajkowski Apr 16, 2024
1614c5a
Make function names consistent
r-czajkowski Apr 16, 2024
211bba7
Cache the `entryFeeBasisPoints` value
r-czajkowski Apr 16, 2024
1a21936
Rename variables/types/functions in staking module
r-czajkowski Apr 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions sdk/src/lib/contracts/bitcoin-depositor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,45 @@ export type DecodedExtraData = {
referral: number
}

/**
* Represents the tBTC network minting fees.
*/
type TBTCMintingFees = {
/**
* The tBTC treasury fee taken from each deposit and transferred to the
* treasury upon sweep proof submission. Is calculated based on the initial
* funding transaction amount.
*/
treasuryFee: bigint
/**
* The tBTC optimistic minting fee, Is calculated AFTER the treasury fee is
* cut.
*/
optimisticMintingFee: bigint
/**
* Maximum amount of BTC transaction fee that can
* be incurred by each swept deposit being part of the given sweep
* transaction.
*/
depositTxMaxFee: bigint
}

/**
* Represents the Acre network staking fees.
*/
type AcreStakingFees = {
/**
* The Acre network depositor fee taken from each Bitcoin deposit and
* transferred to the treasury upon stake request finalization.
*/
bitcoinDepositorFee: bigint
}

export type StakingFees = {
tbtc: TBTCMintingFees
acre: AcreStakingFees
}

/**
* Interface for communication with the AcreBitcoinDepositor on-chain contract.
*/
Expand All @@ -35,4 +74,12 @@ export interface BitcoinDepositor extends DepositorProxy {
* @param extraData Encoded extra data.
*/
decodeExtraData(extraData: string): DecodedExtraData

/**
* Estimates the staking fees based on the provided amount.
* @param amountToStake Amount to stake in 1e8 satoshi precision.
* @returns Staking fees grouped by tBTC and Acre networks in 1e18 tBTC token
* precision.
*/
estimateStakingFees(amountToStake: bigint): Promise<StakingFees>
}
107 changes: 106 additions & 1 deletion sdk/src/lib/ethereum/bitcoin-depositor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import {
isAddress,
solidityPacked,
zeroPadBytes,
Contract,
} from "ethers"
import {
ChainIdentifier,
DecodedExtraData,
BitcoinDepositor,
DepositReceipt,
StakingFees,
} from "../contracts"
import { BitcoinRawTxVectors } from "../bitcoin"
import { EthereumAddress } from "./address"
Expand All @@ -21,11 +23,25 @@ import {
EthersContractDeployment,
EthersContractWrapper,
} from "./contract"
import { Hex } from "../utils"
import { Hex, fromSatoshi } from "../utils"
import { EthereumNetwork } from "./network"

import SepoliaBitcoinDepositor from "./artifacts/sepolia/AcreBitcoinDepositor.json"

type TbtcDepositParameters = {
depositTreasuryFeeDivisor: bigint
depositTxMaxFee: bigint
}

type TbtcBridgeMintingParameters = TbtcDepositParameters & {
optimisticMintingFeeDivisor: bigint
}

type BitcoinDepositorCache = {
tbtcBridgeMintingParameters: TbtcBridgeMintingParameters | undefined
depositorFeeDivisor: bigint | undefined
}

/**
* Ethereum implementation of the BitcoinDepositor.
*/
Expand All @@ -36,6 +52,8 @@ class EthereumBitcoinDepositor
extends EthersContractWrapper<AcreBitcoinDepositorTypechain>
implements BitcoinDepositor
{
#cache: BitcoinDepositorCache

constructor(config: EthersContractConfig, network: EthereumNetwork) {
let artifact: EthersContractDeployment

Expand All @@ -49,6 +67,10 @@ class EthereumBitcoinDepositor
}

super(config, artifact)
this.#cache = {
tbtcBridgeMintingParameters: undefined,
depositorFeeDivisor: undefined,
}
}

/**
Expand Down Expand Up @@ -128,6 +150,89 @@ class EthereumBitcoinDepositor

return { staker, referral }
}

/**
* @see {BitcoinDepositor#estimateStakingFees}
*/
async estimateStakingFees(amountToStake: bigint): Promise<StakingFees> {
const {
depositTreasuryFeeDivisor,
depositTxMaxFee,
optimisticMintingFeeDivisor,
} = await this.#getTbtcBridgeMintingParameters()

const treasuryFee =
depositTreasuryFeeDivisor > 0
? amountToStake / depositTreasuryFeeDivisor
: 0n

// Both deposit amount and treasury fee are in the 1e8 satoshi precision.
// We need to convert them to the 1e18 TBTC precision.
const amountSubTreasury = fromSatoshi(amountToStake - treasuryFee)

const optimisticMintingFee =
optimisticMintingFeeDivisor > 0
? amountSubTreasury / optimisticMintingFeeDivisor
: 0n

const depositorFeeDivisor = await this.#depositorFeeDivisor()
// Compute depositor fee. The fee is calculated based on the initial funding
// transaction amount, before the tBTC protocol network fees were taken.
const depositorFee =
depositorFeeDivisor > 0n
? fromSatoshi(amountToStake) / depositorFeeDivisor
: 0n

return {
tbtc: {
treasuryFee: fromSatoshi(treasuryFee),
optimisticMintingFee,
depositTxMaxFee: fromSatoshi(depositTxMaxFee),
},
acre: {
bitcoinDepositorFee: depositorFee,
},
}
}

// TODO: Consider exposing it from tBTC SDK.
async #getTbtcBridgeMintingParameters(): Promise<TbtcBridgeMintingParameters> {
if (this.#cache.tbtcBridgeMintingParameters) {
return this.#cache.tbtcBridgeMintingParameters
}

const bridgeAddress = await this.instance.bridge()
const bridge = new Contract(bridgeAddress, [
"function depositsParameters()",
])
const depositsParameters =
(await bridge.depositsParameters()) as TbtcDepositParameters

const vaultAddress = await this.getTbtcVaultChainIdentifier()
const vault = new Contract(`0x${vaultAddress.identifierHex}`, [
"function optimisticMintingFeeDivisor()",
])
const optimisticMintingFeeDivisor =
(await vault.optimisticMintingFeeDivisor()) as bigint

this.#cache.tbtcBridgeMintingParameters = {
...depositsParameters,
optimisticMintingFeeDivisor,
}
return this.#cache.tbtcBridgeMintingParameters
}

async #depositorFeeDivisor(): Promise<bigint> {
if (this.#cache.depositorFeeDivisor) {
return this.#cache.depositorFeeDivisor
}

const depositorFeeDivisor = await this.instance.depositorFeeDivisor()

this.#cache.depositorFeeDivisor = depositorFeeDivisor

return this.#cache.depositorFeeDivisor
}
}

export { EthereumBitcoinDepositor, packRevealDepositParameters }
1 change: 1 addition & 0 deletions sdk/src/lib/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./hex"
export * from "./ethereum-signer"
export * from "./backoff"
export * from "./satoshi-converter"
15 changes: 15 additions & 0 deletions sdk/src/lib/utils/satoshi-converter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Multiplier to convert satoshi to 1e18 precision.
*/
const SATOSHI_MULTIPLIER = 10n ** 10n

export function toSatoshi(amount: bigint) {
const remainder = amount % SATOSHI_MULTIPLIER
const satoshis = (amount - remainder) / SATOSHI_MULTIPLIER

return satoshis
}

export function fromSatoshi(amount: bigint) {
return amount * SATOSHI_MULTIPLIER
}
43 changes: 42 additions & 1 deletion sdk/src/modules/staking/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { ChainIdentifier, TBTC } from "@keep-network/tbtc-v2.ts"
import { AcreContracts, DepositorProxy } from "../../lib/contracts"
import {
AcreContracts,
DepositorProxy,
StakingFees as StakingFeesByNetwork,
} from "../../lib/contracts"
import { ChainEIP712Signer } from "../../lib/eip712-signer"
import { StakeInitialization } from "./stake-initialization"
import { toSatoshi } from "../../lib/utils"

/**
* Represents all total staking fees grouped by network.
*/
export type TotalStakingFees = {
tbtc: bigint
acre: bigint
total: bigint
}

/**
* Module exposing features related to the staking.
Expand Down Expand Up @@ -78,6 +92,33 @@ class StakingModule {
estimatedBitcoinBalance(identifier: ChainIdentifier) {
return this.#contracts.stBTC.assetsBalanceOf(identifier)
}

/**
* Estimates the staking fees based on the provided amount.
* @param amountToStake Amount to stake in satoshi.
* @returns Staking fees grouped by tBTC and Acre networks in 1e8 satoshi
* precision and total staking fees value.
*/
async estimateStakingFees(amount: bigint): Promise<TotalStakingFees> {
const { acre: acreFees, tbtc: tbtcFees } =
await this.#contracts.bitcoinDepositor.estimateStakingFees(amount)

const sumFeesByNetwork = <
T extends StakingFeesByNetwork["tbtc"] | StakingFeesByNetwork["acre"],
>(
fees: T,
) => Object.values(fees).reduce((reducer, fee) => reducer + fee, 0n)

const tbtc = toSatoshi(sumFeesByNetwork(tbtcFees))

const acre = toSatoshi(sumFeesByNetwork(acreFees))

return {
tbtc,
acre,
total: tbtc + acre,
}
}
}

export { StakingModule, StakeInitialization }
Loading
Loading