Search Apps Documentation Source Content File Folder Download Copy Actions Download

protocol_fee_swap.gno

3.32 Kb ยท 138 lines
  1package v1
  2
  3import (
  4	"chain"
  5	"chain/runtime"
  6
  7	"gno.land/p/nt/ufmt"
  8
  9	prbac "gno.land/p/gnoswap/rbac"
 10	u256 "gno.land/p/gnoswap/uint256"
 11
 12	"gno.land/r/gnoswap/access"
 13	"gno.land/r/gnoswap/common"
 14	"gno.land/r/gnoswap/halt"
 15
 16	pf "gno.land/r/gnoswap/protocol_fee"
 17	_ "gno.land/r/gnoswap/protocol_fee/v1"
 18)
 19
 20// GetSwapFee returns the current swap fee rate in basis points.
 21func (r *routerV1) GetSwapFee() uint64 {
 22	return r.store.GetSwapFee()
 23}
 24
 25// SetSwapFee sets the protocol swap fee rate.
 26//
 27// Fee is deducted from swap output and sent to protocol fee contract.
 28// Only callable by admin or governance.
 29//
 30// Parameters:
 31//   - fee: Fee rate in basis points (0-1000 = 0%-10%)
 32//
 33// Access:
 34//   - Requires: Admin or Governance role
 35//   - Halt check: Router and ProtocolFee must not be halted
 36//
 37// Events:
 38//   - SetSwapFee: Emits previous and new fee values
 39//
 40// Reverts if:
 41//   - Caller is not admin/governance
 42//   - Fee > 1000 bps (10%)
 43//   - Router or ProtocolFee is halted
 44func (r *routerV1) SetSwapFee(fee uint64) {
 45	halt.AssertIsNotHaltedRouter()
 46	halt.AssertIsNotHaltedProtocolFee()
 47
 48	caller := runtime.PreviousRealm().Address()
 49	access.AssertIsAdminOrGovernance(caller)
 50
 51	// max swap fee is 1000 (bps)
 52	if fee > 1000 {
 53		panic(ufmt.Errorf(
 54			"%s: fee must be in range 0 to 1000 (10%). got %d",
 55			errInvalidSwapFee.Error(), fee,
 56		))
 57	}
 58
 59	prevSwapFee := r.store.GetSwapFee()
 60	if err := r.store.SetSwapFee(fee); err != nil {
 61		panic(err)
 62	}
 63
 64	previousRealm := runtime.PreviousRealm()
 65	chain.Emit(
 66		"SetSwapFee",
 67		"prevAddr", previousRealm.Address().String(),
 68		"prevRealm", previousRealm.PkgPath(),
 69		"newFee", formatUint(fee),
 70		"prevFee", formatUint(prevSwapFee),
 71	)
 72}
 73
 74// handleSwapFee deducts the protocol fee from the swap amount and transfers it to the protocol fee contract.
 75func (r *routerV1) handleSwapFee(
 76	outputToken string,
 77	amount int64,
 78) int64 {
 79	swapFee := r.store.GetSwapFee()
 80	if swapFee <= 0 {
 81		return amount
 82	}
 83
 84	currentTokenPath := outputToken
 85
 86	if common.IsGNOTNativePath(outputToken) {
 87		currentTokenPath = wugnotPath
 88	}
 89
 90	feeAmountInt64 := calculateRouterFee(amount, swapFee)
 91
 92	protocolFeeAddr := access.MustGetAddress(prbac.ROLE_PROTOCOL_FEE.String())
 93	common.SafeGRC20Transfer(cross, currentTokenPath, protocolFeeAddr, feeAmountInt64)
 94
 95	pf.AddToProtocolFee(cross, currentTokenPath, feeAmountInt64)
 96
 97	previousRealm := runtime.PreviousRealm()
 98	chain.Emit(
 99		"SwapRouteFee",
100		"prevAddr", previousRealm.Address().String(),
101		"prevRealm", previousRealm.PkgPath(),
102		"tokenPath", currentTokenPath,
103		"amount", formatInt64(feeAmountInt64),
104	)
105
106	return safeSubInt64(amount, feeAmountInt64)
107}
108
109func calculateRouterFee(amount int64, swapFee uint64) int64 {
110	if swapFee <= 0 {
111		return 0
112	}
113
114	feeAmount := u256.MulDiv(u256.NewUintFromInt64(amount), u256.NewUint(swapFee), u256.NewUint(10000))
115	return safeConvertToInt64(feeAmount)
116}
117
118// calculate amount to fetch from pool including router fee
119// poolAmount = userAmount / (1 - feeRate)
120// = userAmount * 10000 / (10000 - swapFeeBPS)
121func calculateExactOutWithRouterFee(amount int64, swapFee uint64) int64 {
122	if amount == 0 {
123		return amount
124	}
125
126	if swapFee > 0 {
127		// Use MulDiv to prevent overflow and maintain precision
128		poolAmount := u256.MulDiv(
129			u256.NewUintFromInt64(amount),
130			u256.NewUint(10000),
131			u256.NewUint(10000-swapFee),
132		)
133
134		return safeConvertToInt64(poolAmount)
135	}
136
137	return amount
138}