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 10 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.
nkuba marked this conversation as resolved.
Show resolved Hide resolved
*/
depositTxMaxFee: bigint
}

/**
* Represents the Acre network staking fees.
*/
type AcreStakingFees = {
nkuba marked this conversation as resolved.
Show resolved Hide resolved
/**
* The Acre network depositor fee taken from each deposit and transferred to
* the treasury upon stake request finalization.
*/
depositorFee: bigint
nkuba marked this conversation as resolved.
Show resolved Hide resolved
nkuba marked this conversation as resolved.
Show resolved Hide resolved
}

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.
nkuba marked this conversation as resolved.
Show resolved Hide resolved
*/
estimateStakingFees(amountToStake: bigint): Promise<StakingFees>
}
112 changes: 112 additions & 0 deletions 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 @@ -26,6 +28,11 @@ import { EthereumNetwork } from "./network"

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

type TbtcDepositParameters = {
depositTreasuryFeeDivisor: bigint
depositTxMaxFee: bigint
}
nkuba marked this conversation as resolved.
Show resolved Hide resolved

/**
* Ethereum implementation of the BitcoinDepositor.
*/
Expand All @@ -36,6 +43,15 @@ class EthereumBitcoinDepositor
extends EthersContractWrapper<AcreBitcoinDepositorTypechain>
implements BitcoinDepositor
{
/**
* Multiplier to convert satoshi to tBTC token units.
*/
readonly #satoshiMultiplier = 10n ** 10n
nkuba marked this conversation as resolved.
Show resolved Hide resolved

#tbtcBridgeDepositsParameters: TbtcDepositParameters | undefined

#tbtcOptimisticMintingFeeDivisor: bigint | undefined
nkuba marked this conversation as resolved.
Show resolved Hide resolved

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

Expand Down Expand Up @@ -128,6 +144,102 @@ class EthereumBitcoinDepositor

return { staker, referral }
}

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

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 =
(amountToStake - treasuryFee) * this.#satoshiMultiplier

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

const depositorFeeDivisor = await this.instance.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
? (amountToStake * this.#satoshiMultiplier) / depositorFeeDivisor
: 0n

return {
tbtc: {
treasuryFee: treasuryFee * this.#satoshiMultiplier,
optimisticMintingFee,
depositTxMaxFee: depositTxMaxFee * this.#satoshiMultiplier,
},
acre: {
depositorFee,
},
}
}

async #getTbtcMintingFeesParameters(): Promise<
TbtcDepositParameters & { optimisticMintingFeeDivisor: bigint }
> {
const depositParameters = await this.#getTbtcDepositParameters()
const optimisticMintingFeeDivisor =
await this.#getTbtcOptimisticMintingFeeDivisor()

return {
...depositParameters,
optimisticMintingFeeDivisor,
}
}

async #getTbtcDepositParameters(): Promise<TbtcDepositParameters> {
if (this.#tbtcBridgeDepositsParameters) {
return this.#tbtcBridgeDepositsParameters
}

const bridgeAddress = await this.instance.bridge()

const bridge = new Contract(bridgeAddress, [
"function depositsParameters()",
])

const depositsParameters =
(await bridge.depositsParameters()) as TbtcDepositParameters

this.#tbtcBridgeDepositsParameters = depositsParameters

return depositsParameters
}

async #getTbtcOptimisticMintingFeeDivisor(): Promise<bigint> {
if (this.#tbtcOptimisticMintingFeeDivisor) {
return this.#tbtcOptimisticMintingFeeDivisor
}

const vaultAddress = await this.getTbtcVaultChainIdentifier()

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

const optimisticMintingFeeDivisor =
(await vault.optimisticMintingFeeDivisor()) as bigint

this.#tbtcOptimisticMintingFeeDivisor = optimisticMintingFeeDivisor

return optimisticMintingFeeDivisor
}
nkuba marked this conversation as resolved.
Show resolved Hide resolved
}

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"
11 changes: 11 additions & 0 deletions sdk/src/lib/utils/satoshi-converter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const BTC_DECIMALS = 8n

// eslint-disable-next-line import/prefer-default-export
export function toSatoshi(amount: bigint, fromPrecision: bigint = 18n) {
nkuba marked this conversation as resolved.
Show resolved Hide resolved
const SATOSHI_MULTIPLIER = 10n ** (fromPrecision - BTC_DECIMALS)

const remainder = amount % SATOSHI_MULTIPLIER
const satoshis = (amount - remainder) / SATOSHI_MULTIPLIER

return satoshis
}
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 = <
nkuba marked this conversation as resolved.
Show resolved Hide resolved
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