Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
6c495ae
Add oscillating min price test
StephenButtolph Apr 14, 2026
0d1b7c0
refactor: move gaspriceconfig to saetypes
alarso16 Mar 30, 2026
b25165b
refactor: remove hooks import from gastime
alarso16 Mar 30, 2026
b73259a
chore: comments
alarso16 Apr 3, 2026
a92de59
Update gastime handling to intrinsically support minPrice
StephenButtolph Apr 14, 2026
d173c0d
Merge branch 'master' into sae-price-manipulation
StephenButtolph Apr 14, 2026
dcb0690
nit
StephenButtolph Apr 14, 2026
078a84a
Merge branch 'sae-price-manipulation' of github.com:ava-labs/avalanch…
StephenButtolph Apr 14, 2026
6f5743f
lint
StephenButtolph Apr 14, 2026
77d9df8
wip
StephenButtolph Apr 15, 2026
b48c47b
refactor: delete one of the configs
alarso16 Apr 16, 2026
5465b55
update tests
StephenButtolph Apr 16, 2026
0fc7871
expand tests
StephenButtolph Apr 16, 2026
ac6ad4b
nit
StephenButtolph Apr 16, 2026
b08c178
Update TODO
StephenButtolph Apr 16, 2026
3a172a8
Merge branch 'master' into sae-price-manipulation
StephenButtolph Apr 16, 2026
c8f92ba
go.mod
StephenButtolph Apr 16, 2026
13b7ea3
Merge branch 'sae-price-manipulation' of github.com:ava-labs/avalanch…
StephenButtolph Apr 16, 2026
c11e6b1
nit
StephenButtolph Apr 16, 2026
09eba0f
chore(gastime): fix error message
alarso16 Apr 16, 2026
c08767b
Add fuzz test
StephenButtolph Apr 16, 2026
1aa2709
nit
StephenButtolph Apr 16, 2026
194bd97
remove bad testdata
StephenButtolph Apr 16, 2026
b164f17
improve fuzzing
StephenButtolph Apr 16, 2026
fd719d8
nit
StephenButtolph Apr 16, 2026
78d3298
nit
StephenButtolph Apr 16, 2026
60f93dc
nit
StephenButtolph Apr 16, 2026
9bcdd8e
refactor(hooks): Return a time.Time
alarso16 Apr 16, 2026
6436992
chore: cleanups
alarso16 Apr 16, 2026
d991e10
merged
StephenButtolph Apr 16, 2026
50ebce9
reference godoc rather than other function
StephenButtolph Apr 16, 2026
107730a
update test assertions
StephenButtolph Apr 16, 2026
ac57791
Merge master into feature
alarso16 Apr 16, 2026
a443a14
Merge branch 'alarso16/gastime-hook-imports' into sae-price-manipulation
StephenButtolph Apr 16, 2026
3302a34
Merge master
alarso16 Apr 16, 2026
bb4b5a2
Merge branch 'alarso16/gastime-hook-imports' into sae-price-manipulation
StephenButtolph Apr 16, 2026
19a2734
remove helper
StephenButtolph Apr 17, 2026
d1d0d0e
address comments
StephenButtolph Apr 17, 2026
1c0ea31
nit
StephenButtolph Apr 17, 2026
1d2de17
nit
StephenButtolph Apr 17, 2026
850a11c
nit
StephenButtolph Apr 17, 2026
38f6929
merged
StephenButtolph Apr 17, 2026
1ff132d
nits
StephenButtolph Apr 17, 2026
5825fec
nit
StephenButtolph Apr 17, 2026
414d588
typo
StephenButtolph Apr 17, 2026
8ab5061
typo
StephenButtolph Apr 17, 2026
afcad6f
nit
StephenButtolph Apr 17, 2026
2a54e32
optimize
StephenButtolph Apr 17, 2026
0920b20
First pass of comments
StephenButtolph Apr 27, 2026
bccfdeb
Remove SetTarget and SetRate
StephenButtolph Apr 27, 2026
8953fc6
Reuse existing state struct
StephenButtolph Apr 27, 2026
c114a02
Merge branch 'master' into sae-price-manipulation
StephenButtolph Apr 27, 2026
069cd5b
nit
StephenButtolph Apr 27, 2026
441dd4e
kinda weak comment, but ok
StephenButtolph Apr 27, 2026
615d180
whats another test amongst friends
StephenButtolph Apr 27, 2026
3fa86b7
nit
StephenButtolph Apr 28, 2026
0369966
fix bazel
StephenButtolph Apr 28, 2026
85ae854
ci: free runner disk before publishing Antithesis images (#5310)
JonathanOppenheimer Apr 28, 2026
9d780e3
Merge branch 'master' into sae-price-manipulation
StephenButtolph Apr 28, 2026
96f8744
Merge branch 'master' into sae-price-manipulation
StephenButtolph Apr 28, 2026
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
3 changes: 0 additions & 3 deletions vms/saevm/gastime/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,9 @@ go_test(
"//vms/saevm/proxytime",
"@com_github_arr4n_shed//testerr",
"@com_github_ava_labs_libevm//core/types",
"@com_github_ava_labs_libevm//params",
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_golang_x_text//language",
"@org_golang_x_text//message",
],
)
84 changes: 50 additions & 34 deletions vms/saevm/gastime/acp176.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/ava-labs/avalanchego/vms/components/gas"
"github.com/ava-labs/avalanchego/vms/saevm/hook"
"github.com/ava-labs/avalanchego/vms/saevm/intmath"
)

// BeforeBlock is intended to be called before processing a block, with the
Expand All @@ -26,56 +27,71 @@ func (tm *Time) BeforeBlock(hooks hook.Points, h *types.Header) {
// target and gas configuration sourced from [hook.Points] and [types.Header].
func (tm *Time) AfterBlock(used gas.Gas, hooks hook.Points, h *types.Header) error {
tm.Tick(used)
target, hookCfg := hooks.GasConfigAfter(h)
// Although [Time.SetTarget] scales the excess by the same factor as the
// change in target, it rounds when necessary, which might alter the price
// by a negligible amount. We therefore take a price snapshot beforehand
// otherwise we'd call [Time.findExcessForPrice] with a different value,
// which makes it extremely hard to test.
p := tm.Price()
tm.SetTarget(target)
Comment thread
StephenButtolph marked this conversation as resolved.

cfg, err := newConfig(hookCfg)
target, hookCfg := hooks.GasConfigAfter(h)
c, err := newConfig(hookCfg)
if err != nil {
return fmt.Errorf("%T.newConfig() after block: %w", tm, err)
}
Comment thread
StephenButtolph marked this conversation as resolved.
if cfg.equals(tm.config) {
return nil
}
tm.config = cfg
tm.excess = tm.findExcessForPrice(p)

tm.setGasPriceConfig(target, c)
return nil
}

// findExcessForPrice uses binary search over uint64 to find the smallest excess
// value that produces targetPrice with the current [config]. This maintains
// price continuity under a change in [config], with the following scenarios:
//
// - K changes (via TargetToExcessScaling): Scale excess to maintain current price
// - StaticPricing is true: Set excess to 0, enabling fixed price mode
// - M decreases: Scale excess to maintain current price
// - M increases AND current price >= new M: Scale excess to maintain current price
// - M increases AND current price < new M: Price bumps to new M (excess becomes 0)
func (tm *Time) findExcessForPrice(targetPrice gas.Price) gas.Gas {
// We return 0 in case targetPrice < minPrice because we should at least maintain the minimum price
// by setting the excess to 0. ( P = M * e^(0 / K) = M )
// Note: Even though we return 0 for excess it won't avoid accumulating excess in the long run.
if targetPrice <= tm.config.minPrice || tm.config.staticPricing {
return 0
func (tm *Time) setGasPriceConfig(target gas.Gas, c config) {
// x := x * (K' * T') / (K * T)
oldK := tm.excessScalingFactor() // K * T

Comment thread
StephenButtolph marked this conversation as resolved.
Outdated
tm.target = clampTarget(target)
tm.Time.SetRate(rateOf(tm.target))
tm.config = c

newK := tm.excessScalingFactor() // K' * T'
scaled, _, err := intmath.MulDivCeil(tm.excess, newK, oldK)
if err != nil {
scaled = math.MaxUint64
}
tm.excess = scaled

k := tm.excessScalingFactor()
if c.staticPricing {
tm.excess = 0
}

// The price function is monotonic non-decreasing so binary search is appropriate.
lo, hi := gas.Gas(0), gas.Gas(math.MaxUint64)
// x := max(x, ln(minPrice) * K' * T')
minExcess := priceExcess(c.minPrice, newK)
tm.excess = max(tm.excess, minExcess)
}

// priceExcess returns the lowest excess that produces p, if one exists. If the
// integer approximation in [calculatePrice] skips over p, it returns the
// maximum excess where the price is less than p.
//
// Mathematically, it returns ln(p) * k.
func priceExcess(p gas.Price, k gas.Gas) gas.Gas {
Comment thread
StephenButtolph marked this conversation as resolved.
Outdated
if p <= 1 {
return 0
}
// Binary search for the minimum x where calculatePrice(x, k) >= minPrice.
Comment thread
StephenButtolph marked this conversation as resolved.
Outdated
//
// calculatePrice(0, k) == 1, and minPrice > 1, so lo > 0.
lo, hi := gas.Gas(1), gas.Gas(math.MaxUint64)
for lo < hi {
mid := lo + (hi-lo)/2
if gas.CalculatePrice(tm.config.minPrice, mid, k) >= targetPrice {
if calculatePrice(mid, k) >= p {
hi = mid
} else {
lo = mid + 1
}
}
return lo
// Ensure [Time.Price] can return minPrice even if the approximation can't
// represent it.
if calculatePrice(lo, k) == p {
return lo
}
return lo - 1
}

// calculatePrice returns an integer approximation of e^(x/k).
func calculatePrice(x, k gas.Gas) gas.Price {
return gas.CalculatePrice(1, x, k)
}
Loading
Loading