diff --git a/go.mod b/go.mod index 5858a3c5..dae17b8c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/uuid v1.6.0 github.com/prometheus/client_golang v1.23.2 github.com/rs/zerolog v1.34.0 - github.com/smartcontractkit/chain-selectors v1.0.98 + github.com/smartcontractkit/chain-selectors v1.0.100 github.com/smartcontractkit/chainlink-ccip/chains/evm v0.0.0-20260429214841-fb2e77efc1aa github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260429214841-fb2e77efc1aa github.com/smartcontractkit/chainlink-ccv v0.0.2-0.20260505141947-8206a97a7d0b @@ -178,7 +178,7 @@ require ( go.opentelemetry.io/collector/processor v1.30.0 // indirect go.opentelemetry.io/collector/semconv v0.124.0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect - go.opentelemetry.io/contrib/bridges/prometheus v0.61.0 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.68.0 // indirect go.opentelemetry.io/contrib/exporters/autoexport v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect @@ -187,10 +187,10 @@ require ( go.opentelemetry.io/otel/exporters/prometheus v0.58.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect - golang.org/x/mod v0.33.0 // indirect + golang.org/x/mod v0.36.0 // indirect google.golang.org/api v0.241.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) @@ -399,7 +399,7 @@ require ( github.com/pressly/goose/v3 v3.26.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v1.20.99 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/procfs v0.20.1 // indirect github.com/redis/go-redis/v9 v9.14.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect @@ -417,13 +417,13 @@ require ( github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm v0.0.0-20260409110811-3490f129337a // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 // indirect - github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect + github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260518142424-bacfb6ba4146 // indirect github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/committee-verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/heartbeat v0.0.0-20260115142640-f6b99095c12e // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-discovery v0.0.0-20251211142334-5c3421fe2c8d // indirect github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260420204255-a3f3bdd56877 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-stellar/bindings v0.0.0-20260210152908-4554e474e484 @@ -470,7 +470,7 @@ require ( go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect @@ -482,26 +482,26 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect go.opentelemetry.io/otel/log v0.19.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 go.opentelemetry.io/otel/sdk v1.43.0 // indirect go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect - go.uber.org/multierr v1.11.0 // indirect + go.uber.org/multierr v1.11.0 go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/zap v1.27.1 golang.org/x/arch v0.21.0 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect - golang.org/x/net v0.52.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/term v0.41.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/tools v0.45.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/grpc v1.80.0 // indirect diff --git a/go.sum b/go.sum index 22c41c6e..d18e7976 100644 --- a/go.sum +++ b/go.sum @@ -1069,8 +1069,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/prometheus/prometheus v0.304.2 h1:HhjbaAwet87x8Be19PFI/5W96UMubGy3zt24kayEuh4= github.com/prometheus/prometheus v0.304.2/go.mod h1:ioGx2SGKTY+fLnJSQCdTHqARVldGNS8OlIe3kvp98so= github.com/prometheus/sigv4 v0.2.0 h1:qDFKnHYFswJxdzGeRP63c4HlH3Vbn1Yf/Ao2zabtVXk= @@ -1148,8 +1148,8 @@ github.com/smartcontractkit/ccip-contract-examples/chains/evm v0.0.0-20250826190 github.com/smartcontractkit/ccip-contract-examples/chains/evm v0.0.0-20250826190403-aed7f5f33cde/go.mod h1:SYc+BvAALmwsx2zMJIAczIyVNwsiXRIBXmejcTORxGE= github.com/smartcontractkit/ccip-owner-contracts v0.1.0 h1:GiBDtlx7539o7AKlDV+9LsA7vTMPv+0n7ClhSFnZFAk= github.com/smartcontractkit/ccip-owner-contracts v0.1.0/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= -github.com/smartcontractkit/chain-selectors v1.0.98 h1:fuI7CQ1o5cX64eO4/LvwtfhdpGFH5vnsM/bFHRwEiww= -github.com/smartcontractkit/chain-selectors v1.0.98/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= +github.com/smartcontractkit/chain-selectors v1.0.100 h1:wpiSpmI/eFjY+wx/nPr5VuNF4hki0prIBMKEaQWn3g4= +github.com/smartcontractkit/chain-selectors v1.0.100/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260306142855-8d629e752265 h1:Q/sYLdOefZUKc/Bxssq1mg8ptQE/AOot2WI+QcLoiVA= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260306142855-8d629e752265/go.mod h1:CQGkKp3YDsUuxixxmmngmRKfh6yIcftGEZsQrsSIIM8= github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm v0.0.0-20260409110811-3490f129337a h1:WmySB3m3ObwkeiFb9Ud7/2GP47JvDie/edy3tQvLkm0= @@ -1168,12 +1168,10 @@ github.com/smartcontractkit/chainlink-ccv/build/devenv v0.0.2-0.20260505141947-8 github.com/smartcontractkit/chainlink-ccv/build/devenv v0.0.2-0.20260505141947-8206a97a7d0b/go.mod h1:DRH23Vz/5yxZ3rAADzp74kFS0HYhIiCMfKpBB9/vNbQ= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260505141947-8206a97a7d0b h1:3s4dXyB82uFkfZ4ItKYRUStiYx4FoYdNZoLuNoYmzTg= github.com/smartcontractkit/chainlink-ccv/deployment v0.0.2-0.20260505141947-8206a97a7d0b/go.mod h1:V+wrhuNve+JiFwoBr25d6y0lL1rYSCSJhTFyloL3ueo= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260505173221-fcd288aaf3b1 h1:UwCydT0s182k2EMNJQX0pSXNvgvujVEvU0wZj9EC+HE= -github.com/smartcontractkit/chainlink-common v0.11.2-0.20260505173221-fcd288aaf3b1/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= github.com/smartcontractkit/chainlink-common/keystore v1.0.2 h1:AWisx4JT3QV8tcgh6J5NCrex+wAgTYpWyHsyNPSXzsQ= github.com/smartcontractkit/chainlink-common/keystore v1.0.2/go.mod h1:rSkIHdomyak3YnUtXLenl6poIq8q0V3UZPiiyYqPdGA= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260518142424-bacfb6ba4146 h1:PlkA7NGpBm5sc2P//crDFgMIQ0qsQhKcpjWV7Qzwqz8= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.11-0.20260518142424-bacfb6ba4146/go.mod h1:HmUyH2oD9m+GRpKq7q3vuRnm1F2Uczf/Nd1v3ipMSK8= github.com/smartcontractkit/chainlink-deployments-framework v0.100.0 h1:M8+wVsfqgcxzH5WP9NEK5FFlEgU1r+6kD3ow0kURx7E= github.com/smartcontractkit/chainlink-deployments-framework v0.100.0/go.mod h1:yWSgE8ZqcY9vERwBkDRARBbUfFFEyzICOg4Gqi9Yrng= github.com/smartcontractkit/chainlink-evm v0.3.4-0.20260427132147-1ef18876ae9b h1:X9vZpG8A0CUlOZJQXgnjd3vtkN98dFOSPbt8T6HpW9g= @@ -1196,8 +1194,8 @@ github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-rules v0.0.0- github.com/smartcontractkit/chainlink-protos/chainlink-ccv/message-rules v0.0.0-20260505131349-78e491b80735/go.mod h1:zAJq6Tpkx5AdFUwW67dIYnW+Bdf50drCCpMR81Qxb4E= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d h1:AJy55QJ/pBhXkZjc7N+ATnWfxrcjq9BI9DmdtdjwDUQ= github.com/smartcontractkit/chainlink-protos/chainlink-ccv/verifier v0.0.0-20251211142334-5c3421fe2c8d/go.mod h1:5JdppgngCOUS76p61zCinSCgOhPeYQ+OcDUuome5THQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260420204255-a3f3bdd56877 h1:6UueUIbck1Ogarm9rm/9TS6b09mKgMmx+YE8XFg63AQ= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260420204255-a3f3bdd56877/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7 h1:iljEJss3WOwcsMkWy72Yn2zvjw7Gyxc+RXL7r8YKM6g= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260526195338-adcf8013a1b7/go.mod h1:vTFHTCbLui4Vn8fTmAadfE3rdnvfrDwOmMujmW857D0= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= @@ -1423,8 +1421,8 @@ go.opentelemetry.io/collector/semconv v0.124.0 h1:YTdo3UFwNyDQCh9DiSm2rbzAgBuwn/ go.opentelemetry.io/collector/semconv v0.124.0/go.mod h1:te6VQ4zZJO5Lp8dM2XIhDxDiL45mwX0YAQQWRQ0Qr9U= go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 h1:ojdSRDvjrnm30beHOmwsSvLpoRF40MlwNCA+Oo93kXU= go.opentelemetry.io/contrib/bridges/otelzap v0.10.0/go.mod h1:oTTm4g7NEtHSV2i/0FeVdPaPgUIZPfQkFbq0vbzqnv0= -go.opentelemetry.io/contrib/bridges/prometheus v0.61.0 h1:RyrtJzu5MAmIcbRrwg75b+w3RlZCP0vJByDVzcpAe3M= -go.opentelemetry.io/contrib/bridges/prometheus v0.61.0/go.mod h1:tirr4p9NXbzjlbruiRGp53IzlYrDk5CO2fdHj0sSSaY= +go.opentelemetry.io/contrib/bridges/prometheus v0.68.0 h1:w3zlHYETbDwXyWHZlyyR58ZC39XGi8rAhkBgUgJ9d5w= +go.opentelemetry.io/contrib/bridges/prometheus v0.68.0/go.mod h1:GR/mClR2nn7vE8RLwxKjoBNg+QtgdDhRzxVa93koy5o= go.opentelemetry.io/contrib/exporters/autoexport v0.61.0 h1:XfzKtKSrbtYk9TNCF8dkO0Y9M7IOfb4idCwBOTwGBiI= go.opentelemetry.io/contrib/exporters/autoexport v0.61.0/go.mod h1:N6otC+qXTD5bAnbK2O1f/1SXq3cX+3KYSWrkBUqG0cw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= @@ -1510,8 +1508,8 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= @@ -1539,12 +1537,12 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a h1:+3jdDGGB8NGb1Zktc737jlt3/A5f6UlwSzmvqUuufxw= +golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -1557,8 +1555,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1592,8 +1590,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= @@ -1672,10 +1670,10 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= -golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260519152614-eab6ae52b5e2 h1:2EucmYlcIsc8Y6aLj+kX90Y00hmjqLNlw935kc13R2k= +golang.org/x/telemetry v0.0.0-20260519152614-eab6ae52b5e2/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1685,8 +1683,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1698,8 +1696,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1723,8 +1721,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/relayer/chain/chain.go b/relayer/chain/chain.go index 21690f25..9dc6e79b 100644 --- a/relayer/chain/chain.go +++ b/relayer/chain/chain.go @@ -45,7 +45,7 @@ type Chain interface { ID() string Config() *config.TOMLConfig - TxManager() *txm.StellarTxm + TxManager() txm.TxManager KeyStore() core.Keystore GetClient() (RPCClient, error) } @@ -115,7 +115,7 @@ func NewChain(cfg *config.TOMLConfig, opts Opts, chainInfo chainsel.StellarChain func (c *chain) Name() string { return c.lggr.Name() } func (c *chain) ID() string { return c.chainInfo.ChainID } func (c *chain) Config() *config.TOMLConfig { return c.cfg } -func (c *chain) TxManager() *txm.StellarTxm { return c.txm } +func (c *chain) TxManager() txm.TxManager { return c.txm } func (c *chain) KeyStore() core.Keystore { return c.keyStore } func (c *chain) Start(ctx context.Context) error { diff --git a/relayer/chainwriter/chain_writer.go b/relayer/chainwriter/chain_writer.go new file mode 100644 index 00000000..d747e110 --- /dev/null +++ b/relayer/chainwriter/chain_writer.go @@ -0,0 +1,195 @@ +package chainwriter + +import ( + "context" + "errors" + "fmt" + "math/big" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar" + "github.com/smartcontractkit/chainlink-common/pkg/types/core" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + "github.com/smartcontractkit/chainlink-stellar/bindings/scval" + "github.com/smartcontractkit/chainlink-stellar/relayer/txm" + + "github.com/stellar/go-stellar-sdk/strkey" + "github.com/stellar/go-stellar-sdk/txnbuild" + "github.com/stellar/go-stellar-sdk/xdr" +) + +type stellarChainWriter struct { + logger logger.Logger + txm txm.TxManager + keystore core.Keystore + config ChainWriterConfig + + rrSelector TransmitterSelector + + starter utils.StartStopOnce +} + +var _ commontypes.ContractWriter = (*stellarChainWriter)(nil) + +func NewChainWriter(lgr logger.Logger, txm txm.TxManager, keystore core.Keystore, config ChainWriterConfig) (commontypes.ContractWriter, error) { + return &stellarChainWriter{ + logger: logger.Named(lgr, "StellarChainWriter"), + txm: txm, + keystore: keystore, + config: config, + }, nil +} + +func (s *stellarChainWriter) Name() string { + return s.logger.Name() +} + +func (s *stellarChainWriter) Ready() error { + return s.starter.Ready() +} + +func (s *stellarChainWriter) HealthReport() map[string]error { + return map[string]error{s.Name(): s.starter.Healthy()} +} + +func (s *stellarChainWriter) Start(ctx context.Context) error { + return s.starter.StartOnce(s.Name(), func() error { + selector, err := NewRoundRobinSelector(ctx, s.keystore) + if err != nil { + s.logger.Warnw("failed to initialize round-robin transmitter selection (fallback may fail)", "err", err) + } else { + s.rrSelector = selector + } + return nil + }) +} + +func (s *stellarChainWriter) Close() error { + return s.starter.StopOnce(s.Name(), func() error { + return nil + }) +} + +func (s *stellarChainWriter) SubmitTransaction(ctx context.Context, contractName, method string, args any, transactionID string, toAddress string, meta *commontypes.TxMeta, value *big.Int) error { + if value != nil && value.Sign() != 0 { + return fmt.Errorf("value is not supported") + } + + contractConfig, ok := s.config.Contracts[contractName] + if !ok { + return fmt.Errorf("no such contract: %s", contractName) + } + + funcConfig, ok := contractConfig.Functions[method] + if !ok { + return fmt.Errorf("no such method: %s", method) + } + + // 1. Encode `args` into Soroban `txnbuild.InvokeHostFunction` operations + var stellarArgs []stellartypes.ScVal + switch v := args.(type) { + case []stellartypes.ScVal: + stellarArgs = v + default: + return fmt.Errorf("unsupported args type: expected []stellartypes.ScVal, got %T", args) + } + + var xdrArgs []xdr.ScVal + for _, arg := range stellarArgs { + xdrArg, err := toXDRScVal(arg) + if err != nil { + return fmt.Errorf("failed to convert argument to XDR ScVal: %w", err) + } + xdrArgs = append(xdrArgs, xdrArg) + } + + contractID := toAddress + if contractID == "" { + return fmt.Errorf("toAddress (contract ID) is required") + } + + contractBytes, err := strkey.Decode(strkey.VersionByteContract, contractID) + if err != nil { + return fmt.Errorf("invalid contract ID %q: %w", contractID, err) + } + contractAddr := scval.BuildContractScAddress(contractBytes) + if contractAddr == nil { + return fmt.Errorf("failed to build contract address for %q", contractID) + } + + fromAddress := funcConfig.FromAddress + if fromAddress == "" { + if s.rrSelector == nil { + return fmt.Errorf("no FromAddress specified in config and round-robin selector is unavailable") + } + var err error + fromAddress, err = s.rrSelector.Next() + if err != nil { + return fmt.Errorf("failed to select transmitter: %w", err) + } + } + + op := &txnbuild.InvokeHostFunction{ + HostFunction: xdr.HostFunction{ + Type: xdr.HostFunctionTypeHostFunctionTypeInvokeContract, + InvokeContract: &xdr.InvokeContractArgs{ + ContractAddress: *contractAddr, + FunctionName: xdr.ScSymbol(method), + Args: xdrArgs, + }, + }, + SourceAccount: fromAddress, + } + + req := txm.TxRequest{ + ID: transactionID, + FromAddress: fromAddress, + Operations: []txnbuild.Operation{op}, + Metadata: meta, + } + + // 2. Simulate to check viability before enqueueing + simResult, err := s.txm.Simulate(ctx, req) + if err != nil { + return fmt.Errorf("simulation failed: %w", err) + } + if simResult.RestorePreamble != nil { + s.logger.Warnw("restore required before simulation can be completed, enqueueing transaction anyway for TXM to handle", "contract", contractID, "method", method) + } else if simResult.Error != "" { + return fmt.Errorf("simulation returned error: %s", simResult.Error) + } + + // Stellar transactions do not support manually setting a transaction-level gas limit. + // Resource limits (instructions, bytes) are strictly derived from the SorobanTransactionData + // generated by the simulation, and the total fee is MinResourceFee + InclusionFee. + if meta != nil && meta.GasLimit != nil { + return commontypes.ErrSettingTransactionGasLimitNotSupported + } + + // 3. Enqueue + _, err = s.txm.Enqueue(ctx, req) + if err != nil { + s.logger.Errorw("failed to enqueue transaction", "contractName", contractName, "method", method, "toAddress", toAddress, "error", err) + return fmt.Errorf("failed to enqueue transaction %s: %w", transactionID, err) + } + + s.logger.Infow("submitted transaction for execution", "contractName", contractName, "method", method, "toAddress", toAddress) + return nil +} + +func (s *stellarChainWriter) GetTransactionStatus(ctx context.Context, transactionID string) (commontypes.TransactionStatus, error) { + return s.txm.GetStatus(transactionID) +} + +func (s *stellarChainWriter) GetFeeComponents(ctx context.Context) (*commontypes.ChainFeeComponents, error) { + // Stellar TXM handles fees dynamically using feeTracker, returning empty/zero or fetch from feeTracker + return &commontypes.ChainFeeComponents{ + ExecutionFee: big.NewInt(0), + DataAvailabilityFee: big.NewInt(0), + }, nil +} + +func (s *stellarChainWriter) GetEstimateFee(ctx context.Context, contract, method string, args any, toAddress string, meta *commontypes.TxMeta, val *big.Int) (commontypes.EstimateFee, error) { + return commontypes.EstimateFee{}, errors.New("estimate fee is not implemented for stellar") +} diff --git a/relayer/chainwriter/chain_writer_test.go b/relayer/chainwriter/chain_writer_test.go new file mode 100644 index 00000000..6e6f99a1 --- /dev/null +++ b/relayer/chainwriter/chain_writer_test.go @@ -0,0 +1,185 @@ + +package chainwriter + +import ( + "context" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar" + "github.com/smartcontractkit/chainlink-common/pkg/types/core" + + "github.com/smartcontractkit/chainlink-stellar/relayer/txm" + "github.com/stellar/go-stellar-sdk/strkey" + protocolrpc "github.com/stellar/go-stellar-sdk/protocols/rpc" +) + +type mockTxManager struct { + txm.TxManager // embedded to satisfy interface implicitly + enqueueFunc func(ctx context.Context, req txm.TxRequest) (string, error) + simulateFunc func(ctx context.Context, req txm.TxRequest) (protocolrpc.SimulateTransactionResponse, error) + getStatusFunc func(transactionID string) (commontypes.TransactionStatus, error) +} + +func (m *mockTxManager) Enqueue(ctx context.Context, req txm.TxRequest) (string, error) { + if m.enqueueFunc != nil { + return m.enqueueFunc(ctx, req) + } + return "mock-tx-id", nil +} + +func (m *mockTxManager) Simulate(ctx context.Context, req txm.TxRequest) (protocolrpc.SimulateTransactionResponse, error) { + if m.simulateFunc != nil { + return m.simulateFunc(ctx, req) + } + return protocolrpc.SimulateTransactionResponse{}, nil +} + +func (m *mockTxManager) GetStatus(transactionID string) (commontypes.TransactionStatus, error) { + if m.getStatusFunc != nil { + return m.getStatusFunc(transactionID) + } + return commontypes.Unknown, nil +} + +type mockKeystore struct { + core.Keystore + accounts []string + err error +} + +func (m *mockKeystore) Accounts(ctx context.Context) ([]string, error) { + return m.accounts, m.err +} + +func TestChainWriter_SubmitTransaction(t *testing.T) { + lggr := logger.Test(t) + ks := &mockKeystore{accounts: []string{"GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUWDA"}} + + validConfig := ChainWriterConfig{ + Contracts: map[string]*ContractConfig{ + "MyContract": { + Name: "MyContract", + Functions: map[string]*FunctionConfig{ + "myFunction": { + FromAddress: "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUWDA", + }, + }, + }, + }, + } + + validContractID, _ := strkey.Encode(strkey.VersionByteContract, make([]byte, 32)) + + t.Run("success", func(t *testing.T) { + enqueuedCount := 0 + simulatedCount := 0 + + mockTxm := &mockTxManager{ + enqueueFunc: func(ctx context.Context, req txm.TxRequest) (string, error) { + enqueuedCount++ + assert.Equal(t, "my-tx-id", req.ID) + assert.Equal(t, "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUWDA", req.FromAddress) + assert.Len(t, req.Operations, 1) + return "my-tx-id", nil + }, + simulateFunc: func(ctx context.Context, req txm.TxRequest) (protocolrpc.SimulateTransactionResponse, error) { + simulatedCount++ + return protocolrpc.SimulateTransactionResponse{}, nil + }, + } + + cw, err := NewChainWriter(lggr, mockTxm, ks, validConfig) + require.NoError(t, err) + + err = cw.Start(t.Context()) + require.NoError(t, err) + + args := []stellartypes.ScVal{ + {Type: stellartypes.ScValTypeU32, U32: func(v uint32) *uint32 { return &v }(42)}, + } + + err = cw.SubmitTransaction( + t.Context(), + "MyContract", + "myFunction", + args, + "my-tx-id", + validContractID, + &commontypes.TxMeta{}, + nil, + ) + require.NoError(t, err) + assert.Equal(t, 1, enqueuedCount) + assert.Equal(t, 1, simulatedCount) + }) + + t.Run("unsupported args type", func(t *testing.T) { + mockTxm := &mockTxManager{} + cw, _ := NewChainWriter(lggr, mockTxm, ks, validConfig) + + err := cw.SubmitTransaction( + t.Context(), + "MyContract", + "myFunction", + "invalid-args-type", + "my-tx-id", + validContractID, + &commontypes.TxMeta{}, + nil, + ) + require.Error(t, err) + assert.Contains(t, err.Error(), "unsupported args type") + }) + + t.Run("simulation failure", func(t *testing.T) { + mockTxm := &mockTxManager{ + simulateFunc: func(ctx context.Context, req txm.TxRequest) (protocolrpc.SimulateTransactionResponse, error) { + return protocolrpc.SimulateTransactionResponse{Error: "simulation failed due to insufficient gas"}, nil + }, + } + cw, _ := NewChainWriter(lggr, mockTxm, ks, validConfig) + + args := []stellartypes.ScVal{} + err := cw.SubmitTransaction( + t.Context(), + "MyContract", + "myFunction", + args, + "my-tx-id", + validContractID, + &commontypes.TxMeta{}, + nil, + ) + require.Error(t, err) + assert.Contains(t, err.Error(), "simulation returned error") + }) + + t.Run("gas limit not supported", func(t *testing.T) { + mockTxm := &mockTxManager{ + simulateFunc: func(ctx context.Context, req txm.TxRequest) (protocolrpc.SimulateTransactionResponse, error) { + return protocolrpc.SimulateTransactionResponse{MinResourceFee: 500000}, nil + }, + } + cw, _ := NewChainWriter(lggr, mockTxm, ks, validConfig) + + args := []stellartypes.ScVal{} + err := cw.SubmitTransaction( + t.Context(), + "MyContract", + "myFunction", + args, + "my-tx-id", + validContractID, + &commontypes.TxMeta{GasLimit: big.NewInt(400000)}, + nil, + ) + require.ErrorIs(t, err, commontypes.ErrSettingTransactionGasLimitNotSupported) + }) +} + diff --git a/relayer/chainwriter/config.go b/relayer/chainwriter/config.go new file mode 100644 index 00000000..2c4aa106 --- /dev/null +++ b/relayer/chainwriter/config.go @@ -0,0 +1,47 @@ +package chainwriter + +import ( + "encoding/json" + "fmt" + + "github.com/smartcontractkit/chainlink-common/pkg/types/stellar" +) + +type ChainWriterConfig struct { + Contracts map[string]*ContractConfig `json:"contracts"` +} + +type ContractConfig struct { + Name string `json:"name,omitempty"` + Functions map[string]*FunctionConfig `json:"functions"` +} + +type FunctionConfig struct { + FromAddress string `json:"fromAddress"` +} + +func ParseConfig(configBytes []byte) (ChainWriterConfig, error) { + var cfg stellar.ContractWriterConfig + if err := json.Unmarshal(configBytes, &cfg); err != nil { + return ChainWriterConfig{}, fmt.Errorf("failed to unmarshal chain writer config: %w", err) + } + + chainConfig := ChainWriterConfig{ + Contracts: make(map[string]*ContractConfig), + } + + for contractName, contract := range cfg.Contracts { + functions := make(map[string]*FunctionConfig) + for funcName, function := range contract.Functions { + functions[funcName] = &FunctionConfig{ + FromAddress: function.FromAddress, + } + } + chainConfig.Contracts[contractName] = &ContractConfig{ + Name: contract.Name, + Functions: functions, + } + } + + return chainConfig, nil +} diff --git a/relayer/chainwriter/translator.go b/relayer/chainwriter/translator.go new file mode 100644 index 00000000..1a9a0f89 --- /dev/null +++ b/relayer/chainwriter/translator.go @@ -0,0 +1,229 @@ +package chainwriter + +import ( + "fmt" + + "github.com/stellar/go-stellar-sdk/xdr" + + stellartypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/stellar" +) + +func toXDRScVal(v stellartypes.ScVal) (xdr.ScVal, error) { + switch v.Type { + case stellartypes.ScValTypeBool: + if v.Bool == nil { + return xdr.ScVal{}, fmt.Errorf("missing bool value") + } + b := xdr.ScValTypeScvBool + val := *v.Bool + return xdr.ScVal{Type: b, B: &val}, nil + case stellartypes.ScValTypeVoid: + b := xdr.ScValTypeScvVoid + return xdr.ScVal{Type: b}, nil + case stellartypes.ScValTypeError: + if v.Error == nil { + return xdr.ScVal{}, fmt.Errorf("missing error value") + } + b := xdr.ScValTypeScvError + scErr := xdr.ScError{ + Type: xdr.ScErrorType(v.Error.Type), + } + if v.Error.ContractCode != nil { + code := xdr.Uint32(*v.Error.ContractCode) + scErr.ContractCode = &code + } else if v.Error.Code != nil { + code := xdr.ScErrorCode(*v.Error.Code) + scErr.Code = &code + } + return xdr.ScVal{Type: b, Error: &scErr}, nil + case stellartypes.ScValTypeU32: + if v.U32 == nil { + return xdr.ScVal{}, fmt.Errorf("missing u32 value") + } + val := xdr.Uint32(*v.U32) + return xdr.ScVal{Type: xdr.ScValTypeScvU32, U32: &val}, nil + case stellartypes.ScValTypeI32: + if v.I32 == nil { + return xdr.ScVal{}, fmt.Errorf("missing i32 value") + } + val := xdr.Int32(*v.I32) + return xdr.ScVal{Type: xdr.ScValTypeScvI32, I32: &val}, nil + case stellartypes.ScValTypeU64: + if v.U64 == nil { + return xdr.ScVal{}, fmt.Errorf("missing u64 value") + } + val := xdr.Uint64(*v.U64) + return xdr.ScVal{Type: xdr.ScValTypeScvU64, U64: &val}, nil + case stellartypes.ScValTypeI64: + if v.I64 == nil { + return xdr.ScVal{}, fmt.Errorf("missing i64 value") + } + val := xdr.Int64(*v.I64) + return xdr.ScVal{Type: xdr.ScValTypeScvI64, I64: &val}, nil + case stellartypes.ScValTypeTimepoint: + if v.Timepoint == nil { + return xdr.ScVal{}, fmt.Errorf("missing timepoint value") + } + val := xdr.TimePoint(*v.Timepoint) + return xdr.ScVal{Type: xdr.ScValTypeScvTimepoint, Timepoint: &val}, nil + case stellartypes.ScValTypeDuration: + if v.Duration == nil { + return xdr.ScVal{}, fmt.Errorf("missing duration value") + } + val := xdr.Duration(*v.Duration) + return xdr.ScVal{Type: xdr.ScValTypeScvDuration, Duration: &val}, nil + case stellartypes.ScValTypeU128: + if v.U128 == nil { + return xdr.ScVal{}, fmt.Errorf("missing u128 value") + } + val := xdr.UInt128Parts{ + Hi: xdr.Uint64(v.U128.Hi), + Lo: xdr.Uint64(v.U128.Lo), + } + return xdr.ScVal{Type: xdr.ScValTypeScvU128, U128: &val}, nil + case stellartypes.ScValTypeI128: + if v.I128 == nil { + return xdr.ScVal{}, fmt.Errorf("missing i128 value") + } + val := xdr.Int128Parts{ + Hi: xdr.Int64(v.I128.Hi), + Lo: xdr.Uint64(v.I128.Lo), + } + return xdr.ScVal{Type: xdr.ScValTypeScvI128, I128: &val}, nil + case stellartypes.ScValTypeU256: + if v.U256 == nil { + return xdr.ScVal{}, fmt.Errorf("missing u256 value") + } + val := xdr.UInt256Parts{ + HiHi: xdr.Uint64(v.U256.HiHi), + HiLo: xdr.Uint64(v.U256.HiLo), + LoHi: xdr.Uint64(v.U256.LoHi), + LoLo: xdr.Uint64(v.U256.LoLo), + } + return xdr.ScVal{Type: xdr.ScValTypeScvU256, U256: &val}, nil + case stellartypes.ScValTypeI256: + if v.I256 == nil { + return xdr.ScVal{}, fmt.Errorf("missing i256 value") + } + val := xdr.Int256Parts{ + HiHi: xdr.Int64(v.I256.HiHi), + HiLo: xdr.Uint64(v.I256.HiLo), + LoHi: xdr.Uint64(v.I256.LoHi), + LoLo: xdr.Uint64(v.I256.LoLo), + } + return xdr.ScVal{Type: xdr.ScValTypeScvI256, I256: &val}, nil + case stellartypes.ScValTypeBytes: + val := xdr.ScBytes(v.Bytes) + return xdr.ScVal{Type: xdr.ScValTypeScvBytes, Bytes: &val}, nil + case stellartypes.ScValTypeString: + if v.String == nil { + return xdr.ScVal{}, fmt.Errorf("missing string value") + } + val := xdr.ScString(*v.String) + return xdr.ScVal{Type: xdr.ScValTypeScvString, Str: &val}, nil + case stellartypes.ScValTypeSymbol: + if v.Symbol == nil { + return xdr.ScVal{}, fmt.Errorf("missing symbol value") + } + val := xdr.ScSymbol(*v.Symbol) + return xdr.ScVal{Type: xdr.ScValTypeScvSymbol, Sym: &val}, nil + case stellartypes.ScValTypeVec: + if v.Vec == nil { + return xdr.ScVal{}, fmt.Errorf("missing vec value") + } + var vec xdr.ScVec + for _, item := range v.Vec.Values { + xdrItem, err := toXDRScVal(*item) + if err != nil { + return xdr.ScVal{}, fmt.Errorf("error converting vec item: %w", err) + } + vec = append(vec, xdrItem) + } + var scvVec *xdr.ScVec + if len(vec) > 0 { + scvVec = &vec + } else { + emptyVec := xdr.ScVec{} + scvVec = &emptyVec + } + return xdr.ScVal{Type: xdr.ScValTypeScvVec, Vec: &scvVec}, nil + case stellartypes.ScValTypeMap: + if v.Map == nil { + return xdr.ScVal{}, fmt.Errorf("missing map value") + } + var scMap xdr.ScMap + for _, entry := range v.Map.Entries { + key, err := toXDRScVal(*entry.Key) + if err != nil { + return xdr.ScVal{}, fmt.Errorf("error converting map key: %w", err) + } + val, err := toXDRScVal(*entry.Val) + if err != nil { + return xdr.ScVal{}, fmt.Errorf("error converting map value: %w", err) + } + scMap = append(scMap, xdr.ScMapEntry{Key: key, Val: val}) + } + var scvMap *xdr.ScMap + if len(scMap) > 0 { + scvMap = &scMap + } else { + emptyMap := xdr.ScMap{} + scvMap = &emptyMap + } + return xdr.ScVal{Type: xdr.ScValTypeScvMap, Map: &scvMap}, nil + case stellartypes.ScValTypeAddress: + if v.Address == nil { + return xdr.ScVal{}, fmt.Errorf("missing address value") + } + addr, err := toXDRScAddress(*v.Address) + if err != nil { + return xdr.ScVal{}, err + } + return xdr.ScVal{Type: xdr.ScValTypeScvAddress, Address: &addr}, nil + case stellartypes.ScValTypeContractInstance: + // Not implementing unless needed, as it is complex and rare for contract arguments + return xdr.ScVal{}, fmt.Errorf("ContractInstance conversion not implemented") + case stellartypes.ScValTypeLedgerKeyContractInstance: + return xdr.ScVal{Type: xdr.ScValTypeScvLedgerKeyContractInstance}, nil + case stellartypes.ScValTypeNonceKey: + if v.NonceKey == nil { + return xdr.ScVal{}, fmt.Errorf("missing nonce key") + } + nk := xdr.ScNonceKey{Nonce: xdr.Int64(v.NonceKey.Nonce)} + return xdr.ScVal{Type: xdr.ScValTypeScvLedgerKeyNonce, NonceKey: &nk}, nil + default: + return xdr.ScVal{}, fmt.Errorf("unknown ScValType: %v", v.Type) + } +} + +func toXDRScAddress(v stellartypes.ScAddress) (xdr.ScAddress, error) { + switch v.Type { + case stellartypes.ScAddressTypeAccountID: + if len(v.AccountID) != 32 { + return xdr.ScAddress{}, fmt.Errorf("AccountID must be 32 bytes") + } + var pubKey xdr.Uint256 + copy(pubKey[:], v.AccountID) + accountID := xdr.AccountId{ + Type: xdr.PublicKeyTypePublicKeyTypeEd25519, + Ed25519: &pubKey, + } + return xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeAccount, + AccountId: &accountID, + }, nil + case stellartypes.ScAddressTypeContractID: + if len(v.ContractID) != 32 { + return xdr.ScAddress{}, fmt.Errorf("ContractID must be 32 bytes") + } + var hash xdr.Hash + copy(hash[:], v.ContractID) + cid := xdr.ContractId(hash) + return xdr.ScAddress{ + Type: xdr.ScAddressTypeScAddressTypeContract, + ContractId: &cid, + }, nil + default: + return xdr.ScAddress{}, fmt.Errorf("unsupported ScAddress type for now: %v", v.Type) + } +} diff --git a/relayer/chainwriter/transmitter.go b/relayer/chainwriter/transmitter.go new file mode 100644 index 00000000..99acb937 --- /dev/null +++ b/relayer/chainwriter/transmitter.go @@ -0,0 +1,41 @@ +package chainwriter + +import ( + "context" + "fmt" + "sync/atomic" + + "github.com/smartcontractkit/chainlink-common/pkg/types/core" +) + +type TransmitterSelector interface { + Next() (string, error) +} + +type roundRobinSelector struct { + accounts []string + counter uint64 +} + +func NewRoundRobinSelector(ctx context.Context, ks core.Keystore) (TransmitterSelector, error) { + accounts, err := ks.Accounts(ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch accounts from keystore: %w", err) + } + if len(accounts) == 0 { + return nil, fmt.Errorf("no accounts found in keystore") + } + + return &roundRobinSelector{ + accounts: accounts, + counter: 0, + }, nil +} + +func (s *roundRobinSelector) Next() (string, error) { + if len(s.accounts) == 0 { + return "", fmt.Errorf("no accounts available") + } + idx := atomic.AddUint64(&s.counter, 1) - 1 + return s.accounts[idx%uint64(len(s.accounts))], nil +} diff --git a/relayer/relayer.go b/relayer/relayer.go index af4698e2..67fd4c09 100644 --- a/relayer/relayer.go +++ b/relayer/relayer.go @@ -3,6 +3,7 @@ package relayer import ( "context" "errors" + "fmt" "math/big" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -10,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" relaytypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-stellar/relayer/chain" + "github.com/smartcontractkit/chainlink-stellar/relayer/chainwriter" ) var _ loop.Relayer = (*Relayer)(nil) @@ -96,3 +98,22 @@ func (r *Relayer) Transact(ctx context.Context, from, to string, amount *big.Int func (r *Relayer) Stellar() (relaytypes.StellarService, error) { return &r.stellarService, nil } + +func (r *Relayer) NewContractWriter(_ context.Context, configBytes []byte) (relaytypes.ContractWriter, error) { + cfg, err := chainwriter.ParseConfig(configBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse chain writer config: %w", err) + } + + txm := r.chainService.TxManager() + if txm == nil { + return nil, errors.New("stellar txm is unavailable") + } + + ks := r.chainService.KeyStore() + if ks == nil { + return nil, errors.New("stellar keystore is unavailable") + } + + return chainwriter.NewChainWriter(r.lggr, txm, ks, cfg) +} diff --git a/relayer/txm/interface.go b/relayer/txm/interface.go new file mode 100644 index 00000000..5066ca5a --- /dev/null +++ b/relayer/txm/interface.go @@ -0,0 +1,24 @@ +package txm + +import ( + "context" + "math/big" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + protocolrpc "github.com/stellar/go-stellar-sdk/protocols/rpc" +) + +// TxManager defines the interface for the Stellar Transaction Manager. +type TxManager interface { + services.Service + + Enqueue(ctx context.Context, req TxRequest) (string, error) + EnqueueAndWait(ctx context.Context, req TxRequest) (*TxResult, error) + Simulate(ctx context.Context, req TxRequest) (protocolrpc.SimulateTransactionResponse, error) + + GetStatus(transactionID string) (commontypes.TransactionStatus, error) + GetTransactionResult(transactionID string) (*TxResult, error) + GetTransactionFee(transactionID string) (*big.Int, error) + InflightCount() (int, int) +}