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

SOM: transaction cost #669

Merged
merged 9 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/pelletier/go-toml/v2 v2.1.1
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.17.0
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240419161010-71a18c1ab9a2
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240422215914-3b758c48e596
github.com/smartcontractkit/libocr v0.0.0-20240326191951-2bbe9382d052
github.com/stretchr/testify v1.9.0
go.uber.org/multierr v1.11.0
Expand Down Expand Up @@ -50,6 +50,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240419161010-71a18c1ab9a2 h1:1gkiOu6GfzfeDu6XyYB0G9A6HYsz1lDWx8gM1jReSqI=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240419161010-71a18c1ab9a2/go.mod h1:GTDBbovHUSAUk+fuGIySF2A/whhdtHGaWmU61BoERks=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240422215914-3b758c48e596 h1:Wo+i2cPSO8eBQ3wgJFi1rBuh01n4yHcVZTkb1hYXti4=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240422215914-3b758c48e596/go.mod h1:GTDBbovHUSAUk+fuGIySF2A/whhdtHGaWmU61BoERks=
github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss=
github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU=
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/lib/pq v1.10.9
github.com/onsi/gomega v1.30.0
github.com/rs/zerolog v1.30.0
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240419205832-845fa69af8d9
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240422215914-3b758c48e596
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240422131138-ec3bcf1a0821
github.com/smartcontractkit/chainlink-testing-framework v1.28.3
github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240422131834-9efe7cab4c4d
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1412,8 +1412,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq
github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE=
github.com/smartcontractkit/chainlink-automation v1.0.3 h1:h/ijT0NiyV06VxYVgcNfsE3+8OEzT3Q0Z9au0z1BPWs=
github.com/smartcontractkit/chainlink-automation v1.0.3/go.mod h1:RjboV0Qd7YP+To+OrzHGXaxUxoSONveCoAK2TQ1INLU=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240419205832-845fa69af8d9 h1:elDIBChe7ByPNvCyrSjMLTPKrgY+sKgzzlWe2p3wokY=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240419205832-845fa69af8d9/go.mod h1:GTDBbovHUSAUk+fuGIySF2A/whhdtHGaWmU61BoERks=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240422215914-3b758c48e596 h1:Wo+i2cPSO8eBQ3wgJFi1rBuh01n4yHcVZTkb1hYXti4=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240422215914-3b758c48e596/go.mod h1:GTDBbovHUSAUk+fuGIySF2A/whhdtHGaWmU61BoERks=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240419131812-73d148593d92 h1:MvaNzuaQh1vX4CAYLM8qFd99cf0ZF1JNwtDZtLU7WvU=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240419131812-73d148593d92/go.mod h1:uATrrJ8IsuBkOBJ46USuf73gz9gZy5k5bzGE5/ji/rc=
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo=
Expand Down
97 changes: 97 additions & 0 deletions pkg/monitoring/exporter/fees.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package exporter

import (
"context"

commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"
"github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/fees"
)

func NewFeesFactory(
log commonMonitoring.Logger,
metrics metrics.Fees,
) commonMonitoring.ExporterFactory {
return &feesFactory{
log,
metrics,
}
}

type feesFactory struct {
log commonMonitoring.Logger
metrics metrics.Fees
}

func (p *feesFactory) NewExporter(
params commonMonitoring.ExporterParams,
) (commonMonitoring.Exporter, error) {
return &feesExporter{
metrics.FeedInput{
AccountAddress: params.FeedConfig.GetContractAddress(),
FeedID: params.FeedConfig.GetContractAddress(),
ChainID: params.ChainConfig.GetChainID(),
ContractStatus: params.FeedConfig.GetContractStatus(),
ContractType: params.FeedConfig.GetContractType(),
FeedName: params.FeedConfig.GetName(),
FeedPath: params.FeedConfig.GetPath(),
NetworkID: params.ChainConfig.GetNetworkID(),
NetworkName: params.ChainConfig.GetNetworkName(),
},
p.log,
p.metrics,
}, nil
}

type feesExporter struct {
label metrics.FeedInput // static for each feed
log commonMonitoring.Logger
metrics metrics.Fees
}

func (f *feesExporter) Export(ctx context.Context, data interface{}) {
details, err := types.MakeTxDetails(data)
if err != nil {
return // skip if input could not be parsed
}

// skip on no updates
if len(details) == 0 {
return
}

// calculate average of non empty TxDetails
var feeArr []uint64
var computeUnitsArr []fees.ComputeUnitPrice
for _, d := range details {
if d.Empty() {
continue
}
feeArr = append(feeArr, d.Fee)
computeUnitsArr = append(computeUnitsArr, d.ComputeUnitPrice)
}
if len(feeArr) == 0 || len(computeUnitsArr) == 0 {
f.log.Errorf("exporter could not find non-empty TxDetails")
return
}

fee, err := mathutil.Avg(feeArr...)
if err != nil {
f.log.Errorf("fee average: %w", err)
return
}
computeUnits, err := mathutil.Avg(computeUnitsArr...)
if err != nil {
f.log.Errorf("computeUnits average: %w", err)
return
}

f.metrics.Set(fee, computeUnits, f.label)
}

func (f *feesExporter) Cleanup(_ context.Context) {
f.metrics.Cleanup(f.label)
}
53 changes: 53 additions & 0 deletions pkg/monitoring/exporter/fees_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package exporter

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/smartcontractkit/chainlink-common/pkg/logger"
commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"
"github.com/smartcontractkit/chainlink-common/pkg/utils"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/metrics/mocks"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/testutils"
"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/fees"
)

func TestFees(t *testing.T) {
ctx := utils.Context(t)
lgr, logs := logger.TestObserved(t, zapcore.ErrorLevel)
m := mocks.NewFees(t)
m.On("Set", mock.Anything, mock.Anything, mock.Anything).Once()
m.On("Cleanup", mock.Anything).Once()

factory := NewFeesFactory(lgr, m)

chainConfig := testutils.GenerateChainConfig()
feedConfig := testutils.GenerateFeedConfig()
exporter, err := factory.NewExporter(commonMonitoring.ExporterParams{ChainConfig: chainConfig, FeedConfig: feedConfig, Nodes: []commonMonitoring.NodeConfig{}})
require.NoError(t, err)

// happy path
exporter.Export(ctx, []types.TxDetails{{Fee: 1, ComputeUnitPrice: 1}})
exporter.Cleanup(ctx)

// not txdetails type - no calls to mock
assert.NotPanics(t, func() { exporter.Export(ctx, 1) })

// zero txdetails - no calls to mock
exporter.Export(ctx, []types.TxDetails{})

// empty txdetails
exporter.Export(ctx, []types.TxDetails{{}})
assert.Equal(t, 1, logs.FilterMessage("exporter could not find non-empty TxDetails").Len())

// multiple TxDetails should return average
// skip empty
m.On("Set", uint64(1), fees.ComputeUnitPrice(10), mock.Anything).Once()
exporter.Export(ctx, []types.TxDetails{{}, {Fee: 2}, {ComputeUnitPrice: 20}})
}
39 changes: 39 additions & 0 deletions pkg/monitoring/metrics/fees.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package metrics

import (
commonMonitoring "github.com/smartcontractkit/chainlink-common/pkg/monitoring"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/fees"
)

//go:generate mockery --name Fees --output ./mocks/

type Fees interface {
Set(txFee uint64, computeUnitPrice fees.ComputeUnitPrice, feedInput FeedInput)
Cleanup(feedInput FeedInput)
}

var _ Fees = (*feeMetrics)(nil)

type feeMetrics struct {
txFee simpleGauge
computeUnit simpleGauge
}

func NewFees(log commonMonitoring.Logger) *feeMetrics {
return &feeMetrics{
txFee: newSimpleGauge(log, types.TxFeeMetric),
computeUnit: newSimpleGauge(log, types.ComputeUnitPriceMetric),
}
}

func (sh *feeMetrics) Set(txFee uint64, computeUnitPrice fees.ComputeUnitPrice, feedInput FeedInput) {
sh.txFee.set(float64(txFee), feedInput.ToPromLabels())
sh.computeUnit.set(float64(computeUnitPrice), feedInput.ToPromLabels())
}

func (sh *feeMetrics) Cleanup(feedInput FeedInput) {
sh.txFee.delete(feedInput.ToPromLabels())
sh.computeUnit.delete(feedInput.ToPromLabels())
}
45 changes: 45 additions & 0 deletions pkg/monitoring/metrics/fees_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package metrics

import (
"testing"

"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-common/pkg/logger"

"github.com/smartcontractkit/chainlink-solana/pkg/monitoring/types"
"github.com/smartcontractkit/chainlink-solana/pkg/solana/fees"
)

func TestFees(t *testing.T) {
lgr := logger.Test(t)
m := NewFees(lgr)

// fetching gauges
gFees, ok := gauges[types.TxFeeMetric]
require.True(t, ok)
gComputeUnits, ok := gauges[types.ComputeUnitPriceMetric]
require.True(t, ok)

v0 := 1
v1 := 10
l := FeedInput{NetworkID: t.Name()}

// set gauge
assert.NotPanics(t, func() {
m.Set(uint64(v0), fees.ComputeUnitPrice(v1), l)
})
num := testutil.ToFloat64(gFees.With(l.ToPromLabels()))
assert.Equal(t, float64(v0), num)
num = testutil.ToFloat64(gComputeUnits.With(l.ToPromLabels()))
assert.Equal(t, float64(v1), num)

// cleanup gauges
assert.Equal(t, 1, testutil.CollectAndCount(gFees))
assert.Equal(t, 1, testutil.CollectAndCount(gComputeUnits))
assert.NotPanics(t, func() { m.Cleanup(l) })
assert.Equal(t, 0, testutil.CollectAndCount(gFees))
assert.Equal(t, 0, testutil.CollectAndCount(gComputeUnits))
}
16 changes: 9 additions & 7 deletions pkg/monitoring/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,15 @@ func init() {
nodeLabels,
)

// init gauge for observation count tracking
gauges[types.ReportObservationMetric] = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: types.ReportObservationMetric,
},
feedLabels,
)
// init gauges for tx details tracking
for _, txDetailMetric := range types.TxDetailsMetrics {
gauges[txDetailMetric] = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: txDetailMetric,
},
feedLabels,
)
}

// init gauge for slot height
gauges[types.SlotHeightMetric] = promauto.NewGaugeVec(
Expand Down
40 changes: 40 additions & 0 deletions pkg/monitoring/metrics/mocks/Fees.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/monitoring/types/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import "github.com/gagliardetto/solana-go"

var (
sampleTxResultSigner = solana.MustPublicKeyFromBase58("9YR7YttJFfptQJSo5xrnYoAw1fJyVonC1vxUSqzAgyjY")
SampleTxResultProgram = solana.MustPublicKeyFromBase58("cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ")
SampleTxResultJSON = `{"blockTime":1712887149,"meta":{"computeUnitsConsumed":64949,"err":null,"fee":5000,"innerInstructions":[{"index":1,"instructions":[{"accounts":[2,4],"data":"6y43XFem5gk9n8ESJ4pGFboagJiimTtvvy2VCjAUur3y","programIdIndex":3,"stackHeight":2}]}],"loadedAddresses":{"readonly":[],"writable":[]},"logMessages":["Program ComputeBudget111111111111111111111111111111 invoke [1]","Program ComputeBudget111111111111111111111111111111 success","Program cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ invoke [1]","Program data: gjbLTR5rT6hW4eUAAAN30/iLBm0GRKxe6y9hGtvvKCPLmscA16aVgw6AKe17ouFpAAAAAAAAAAAAAAAAA2uVGGYEAwECAAAAAAAAAAAAAAAAAAAAAKom6kICAAAAsr0AAAAAAAA=","Program HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny invoke [2]","Program log: Instruction: Submit","Program HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny consumed 4427 of 140121 compute units","Program HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny success","Program cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ consumed 64799 of 199850 compute units","Program cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ success"],"postBalances":[1019067874127,49054080,2616960,1141440,0,0,1141440,1],"postTokenBalances":[],"preBalances":[1019067879127,49054080,2616960,1141440,0,0,1141440,1],"preTokenBalances":[],"rewards":[],"status":{"Ok":null}},"slot":291748793,"transaction":{"message":{"accountKeys":["9YR7YttJFfptQJSo5xrnYoAw1fJyVonC1vxUSqzAgyjY","Ghm1a2c2NGPg6pKGG3PP1GLAuJkHm1RKMPqqJwPM7JpJ","HXoZZBWv25N4fm2vfSKnHXTeDJ31qaAcWZe3ZKeM6dQv","HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny","3u6T92C2x18s39a7WNM8NGaQK1YEtstTtqamZGsLvNZN","Sysvar1nstructions1111111111111111111111111","cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ","ComputeBudget111111111111111111111111111111"],"header":{"numReadonlySignedAccounts":0,"numReadonlyUnsignedAccounts":5,"numRequiredSignatures":1},"instructions":[{"accounts":[],"data":"3DTZbgwsozUF","programIdIndex":7,"stackHeight":null},{"accounts":[1,0,2,3,4,5],"data":"4W4pS7SH6dugDLwXWijhmW3dGTP7WENQa9vbUjvati1j95ghou2jUJHxvPUoowhZk2bHk21uKk4uFRQrpVF5e54NejQLtAT4DeZPC8n3QudjXhAHgBvFjYvDZDhCKRBK4nvdysDh7aKSE4nb3RiampwUo4u5WsKFfXYZnzbn8edC6jwuJVju1DczQPiLuzuCUps99C8rxwE9XkonGMrjc3Pj4cArMggk5fitRkfdaUn4mGRXDHzPFSg63YTZEn7tnnJd8pWEu9v9H8wBKcN1ptLiY5QmKSnayRcfYvd8MZ9wWf8bD7iVGSNUnwJToyFBVyBNabibozthXSDNmxr3yz1uR9vE3HFq6C2i1LX32a2aqZWzJjmvgdVNfNZZxqDxR6GvWYMw35","programIdIndex":6,"stackHeight":null}],"recentBlockhash":"BKUsMxK39LcgXKm8j5LuYyhig2kgQtRBkxR89szEzaSU"},"signatures":["2eEb8FeJyhczELJ3XKc6yvNLi3jYoC9vdpaR6WUN5vJ3f15ZV1d7LGZZqrqseQFEedgE4cxwcd3S3jYLmvJWBrNg"]}}`
)
Loading
Loading