diff --git a/ccv/chains/evm/deployment/go.mod b/ccv/chains/evm/deployment/go.mod index 8e36759f9c..9d5912e1b8 100644 --- a/ccv/chains/evm/deployment/go.mod +++ b/ccv/chains/evm/deployment/go.mod @@ -15,18 +15,18 @@ replace github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment => ../. require ( github.com/Masterminds/semver/v3 v3.4.0 - github.com/ethereum/go-ethereum v1.17.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/smartcontractkit/chain-selectors v1.0.97 github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260312182032-b2b38700f19b github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm v0.0.0-20260312182032-b2b38700f19b github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20260312182032-b2b38700f19b github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260312182032-b2b38700f19b - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 github.com/smartcontractkit/chainlink-deployments-framework v0.80.2 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd github.com/smartcontractkit/mcms v0.36.0 github.com/stretchr/testify v1.11.1 - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 ) require ( @@ -82,7 +82,7 @@ require ( github.com/creachadair/mds v0.13.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dchest/siphash v1.2.3 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v28.5.1+incompatible // indirect @@ -91,7 +91,7 @@ require ( github.com/ebitengine/purego v0.9.0 // indirect github.com/emicklei/dot v1.6.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect @@ -228,9 +228,10 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-evm v0.3.3 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b // indirect github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 // indirect github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.2 // indirect @@ -246,7 +247,7 @@ require ( github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/testcontainers/testcontainers-go v0.39.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect @@ -267,7 +268,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.63.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect @@ -278,12 +279,12 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect @@ -295,11 +296,11 @@ require ( golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/ccv/chains/evm/deployment/go.sum b/ccv/chains/evm/deployment/go.sum index e623e1f03d..10d61e706e 100644 --- a/ccv/chains/evm/deployment/go.sum +++ b/ccv/chains/evm/deployment/go.sum @@ -201,8 +201,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -230,12 +230,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= @@ -733,8 +733,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d/go.mod h1:bgmqE7x9xwmIVr8PqLbC0M5iPm4AV2DBl596lO6S5Sw= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 h1:Z4t2ZY+ZyGWxtcXvPr11y4o3CGqhg3frJB5jXkCSvWA= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 h1:eui+u6ge2RYW01F/DeXWrc5UOqc+8+lyPoi9TIAmMgo= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= 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-deployments-framework v0.80.2 h1:M944dbKDHJiqqGOfiaeQw9nUk/uuci8ggUXSgfSzW5Q= @@ -743,12 +743,14 @@ github.com/smartcontractkit/chainlink-evm v0.3.3 h1:JqwyJEtnNEUaoQQPoOBTT4sn2lpd github.com/smartcontractkit/chainlink-evm v0.3.3/go.mod h1:q0ZBvaoisNaqC8NcMYWNPTjee88nQktDEeJMQHq3hVI= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd h1:sK+pK4epQp20yQ7XztwrVgkTkRAr4FY+TvEegW8RuQk= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd/go.mod h1:7Jlt72+V9891y3LnGwHzmQwt9tfEGYryRKiGlQHo/o8= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 h1:03tbcwjyIEjvHba1IWOj1sfThwebm2XNzyFHSuZtlWc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 h1:xHPmFDhff7QpeFxKsZfk+24j4AlnQiFjjRh5O87Peu4= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b h1:36knUpKHHAZ86K4FGWXtx8i/EQftGdk2bqCoEu/Cha8= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 h1:KyPROV+v7P8VdiU7JhVuGLcDlEBsURSpQmSCgNBTY+s= @@ -804,8 +806,8 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -866,8 +868,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -888,20 +890,20 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -951,8 +953,8 @@ golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZP golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= 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= @@ -1089,8 +1091,8 @@ 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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1122,8 +1124,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1139,8 +1141,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/ccv/chains/evm/deployment/v1_7_0/adapters/chain_family.go b/ccv/chains/evm/deployment/v1_7_0/adapters/chain_family.go index 049f3e8d96..eae6daaf02 100644 --- a/ccv/chains/evm/deployment/v1_7_0/adapters/chain_family.go +++ b/ccv/chains/evm/deployment/v1_7_0/adapters/chain_family.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm/deployment/latest/operations/onramp" "github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm/deployment/v2_0_0/operations/fee_quoter" evm_datastore_utils "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/utils/datastore" + evm_sequences "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/sequences" "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_2_0/operations/router" "github.com/smartcontractkit/chainlink-ccip/deployment/lanes" ) @@ -67,6 +68,10 @@ func (a *ChainFamilyAdapter) GetFQAddress(ds datastore.DataStore, chainSelector return addr, nil } +func (c *ChainFamilyAdapter) DisableRemoteChain() *operations.Sequence[lanes.DisableRemoteChainInput, seq_core.OnChainOutput, chain.BlockChains] { + return evm_sequences.DisableRemoteChainSequence +} + func (a *ChainFamilyAdapter) GetRouterAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) { addr, err := datastore_utils.FindAndFormatRef(ds, datastore.AddressRef{ ChainSelector: chainSelector, diff --git a/chains/evm/deployment/docs/_category_.yaml b/chains/evm/deployment/docs/_category_.yaml new file mode 100644 index 0000000000..16ee0f9aec --- /dev/null +++ b/chains/evm/deployment/docs/_category_.yaml @@ -0,0 +1,2 @@ +label: "EVM Implementation" +position: 2 diff --git a/chains/evm/deployment/docs/adapter.md b/chains/evm/deployment/docs/adapter.md new file mode 100644 index 0000000000..17a4c4db8f --- /dev/null +++ b/chains/evm/deployment/docs/adapter.md @@ -0,0 +1,182 @@ +--- +title: "EVMAdapter Reference" +sidebar_label: "Adapter" +sidebar_position: 2 +--- + +# EVMAdapter Reference + +The `EVMAdapter` is a stateless struct that implements all required shared interfaces for the EVM chain family. + +**Source:** [v1_6_0/sequences/adapter.go](../v1_6_0/sequences/adapter.go) + +For the interfaces it implements, see [Interfaces Reference](../../../../deployment/docs/interfaces.md). + +--- + +## Struct Definition + +```go +type EVMAdapter struct{} +``` + +The EVM adapter is intentionally stateless -- all state is resolved from the DataStore and environment at execution time. + +--- + +## Registration + +### Main Adapter (`sequences/adapter.go`) + +**Source:** [v1_6_0/sequences/adapter.go](../v1_6_0/sequences/adapter.go) + +```go +func init() { + v, _ := semver.NewVersion("1.6.0") + + laneapi.GetLaneAdapterRegistry().RegisterLaneAdapter(chain_selectors.FamilyEVM, v, &EVMAdapter{}) + deployapi.GetRegistry().RegisterDeployer(chain_selectors.FamilyEVM, v, &EVMAdapter{}) + deployapi.GetTransferOwnershipRegistry().RegisterAdapter(chain_selectors.FamilyEVM, v, &EVMAdapter{}) + mcmsreaderapi.GetRegistry().RegisterMCMSReader(chain_selectors.FamilyEVM, &EVMAdapter{}) + tokensapi.GetTokenAdapterRegistry().RegisterTokenAdapter(chain_selectors.FamilyEVM, v, &EVMAdapter{}) + lanes.GetPingPongAdapterRegistry().RegisterPingPongAdapter(chain_selectors.FamilyEVM, v, &EVMAdapter{}) +} +``` + +### Separate Adapters (`adapters/init.go`) + +**Source:** [v1_6_0/adapters/init.go](../v1_6_0/adapters/init.go) + +```go +func init() { + // Curse adapter + fastcurse.GetCurseRegistry().RegisterNewCurse(fastcurse.CurseRegistryInput{ + CursingFamily: chain_selectors.FamilyEVM, + CursingVersion: semver.MustParse("1.6.0"), + CurseAdapter: NewCurseAdapter(), + CurseSubjectAdapter: NewCurseAdapter(), + }) + + // Lane migration adapters + deploy.GetLaneMigratorRegistry().RegisterRampUpdater(chain_selectors.FamilyEVM, semver.MustParse("1.6.0"), &LaneMigrater{}) + deploy.GetLaneMigratorRegistry().RegisterRouterUpdater(chain_selectors.FamilyEVM, semver.MustParse("1.2.0"), &RouterUpdater{}) +} +``` + +--- + +## Interface Implementations + +### Deployer + +| Method | Delegates To | +|--------|-------------| +| `DeployChainContracts()` | `DeployChainContracts` sequence | +| `DeployMCMS()` | v1.0.0 MCMS deployer | +| `FinalizeDeployMCMS()` | v1.0.0 MCMS finalize (no-op for EVM) | +| `SetOCR3Config()` | OCR3 configuration sequence | +| `GrantAdminRoleToTimelock()` | Timelock admin role sequence | + +EVM delegates MCMS deployment to the v1.0.0 implementation since MCMS contracts are version-agnostic on EVM. + +### LaneAdapter + +| Method | Behavior | +|--------|----------| +| `GetOnRampAddress()` | Looks up OnRamp v1.6.0 from DataStore | +| `GetOffRampAddress()` | Looks up OffRamp v1.6.0 from DataStore | +| `GetRouterAddress()` | Looks up Router from DataStore | +| `GetFQAddress()` | Looks up latest FeeQuoter (v1.6.0 -- v1.7.0 range) | +| `ConfigureLaneLegAsSource()` | OnRamp dest config + FeeQuoter dest config + price updates | +| `ConfigureLaneLegAsDest()` | OffRamp source config + Router ramp updates | + +All `Get*Address` methods return 20-byte EVM addresses. + +### TokenAdapter + +| Method | Behavior | +|--------|----------| +| `AddressRefToBytes()` | Hex-decodes the address string to bytes | +| `DeriveTokenAddress()` | Reads token address from the pool contract on-chain | +| `DeriveTokenDecimals()` | Reads decimals from the token contract on-chain | +| `DeriveTokenPoolCounterpart()` | Returns tokenPool unchanged (no PDA derivation needed on EVM) | +| `ConfigureTokenForTransfersSequence()` | Registers token + sets remote chain configs | +| `ManualRegistration()` | Registers customer token via proposeAdministrator | +| `SetTokenPoolRateLimits()` | Sets inbound/outbound rate limits | +| `DeployToken()` | Deploys ERC20 BurnMint token | +| `DeployTokenPoolForToken()` | Deploys token pool for existing token | +| `UpdateAuthorities()` | Transfers token/pool ownership to timelock | + +### TokenPriceProvider (Optional) + +```go +func (e *EVMAdapter) GetDefaultTokenPrices() map[datastore.ContractType]*big.Int +``` + +Returns a default price of $20 per token (18 decimals) for WETH and LINK. + +### TransferOwnershipAdapter + +| Method | Behavior | +|--------|----------| +| `InitializeTimelockAddress()` | No-op on EVM (timelock resolved at execution time) | +| `SequenceTransferOwnershipViaMCMS()` | Calls `transferOwnership()` on each contract | +| `SequenceAcceptOwnership()` | Calls `acceptOwnership()` on each contract | +| `ShouldAcceptOwnershipWithTransferOwnership()` | Returns `false` (two-step ownership on EVM) | + +### MCMSReader + +| Method | Behavior | +|--------|----------| +| `GetChainMetadata()` | Reads MCM starting op count from on-chain | +| `GetTimelockRef()` | Resolves RBACTimelock AddressRef from DataStore | +| `GetMCMSRef()` | Resolves ProposerManyChainMultiSig AddressRef | + +--- + +## Specialized Adapters + +### CurseAdapter (`adapters/fastcurse.go`) + +Implements `CurseAdapter` and `CurseSubjectAdapter` for EVM RMN operations. + +- `Initialize()` -- loads RMNRemote contract address +- `IsSubjectCursedOnChain()` -- calls `isCursed(subject)` on RMNRemote +- `Curse()` / `Uncurse()` -- returns sequences that call `curse()`/`uncurse()` on RMNRemote +- `SelectorToSubject()` -- converts chain selector to 16-byte curse subject +- `SubjectToSelector()` -- reverses the conversion + +### FeeAdapter (`adapters/fees.go`) + +Implements `FeeAdapter` for EVM fee configuration. + +- `SetTokenTransferFee()` -- returns sequence that calls `applyTokenTransferFeeConfigUpdates` on FeeQuoter +- `GetOnchainTokenTransferFeeConfig()` -- reads current fee config from FeeQuoter +- `GetDefaultTokenTransferFeeConfig()` -- returns sensible defaults + +### ConfigImporter (`adapters/configimport.go`) + +Implements `ConfigImporter` for importing existing on-chain state into the DataStore. + +### LaneMigrater (`adapters/lanemigrator.go`) + +Implements `RampUpdateInRouter` and `RouterUpdateInRamp` for lane migration scenarios. + +--- + +## EVM Utilities + +### Address Conversion (`utils/datastore/datastore.go`) + +```go +func ToByteArray(ref datastore.AddressRef) ([]byte, error) // Hex string -> bytes +func ToEVMAddress(ref datastore.AddressRef) (common.Address, error) // Hex string -> common.Address +func ToPaddedEVMAddress(ref datastore.AddressRef) ([]byte, error) // 32-byte left-padded +``` + +### Contract Introspection (`utils/common.go`) + +```go +func TypeAndVersion(addr common.Address, client bind.ContractBackend) (string, *semver.Version, error) +func ValidateEVMAddress(addr string, fieldName string) error +``` diff --git a/chains/evm/deployment/docs/index.md b/chains/evm/deployment/docs/index.md new file mode 100644 index 0000000000..4658ee995f --- /dev/null +++ b/chains/evm/deployment/docs/index.md @@ -0,0 +1,72 @@ +--- +title: "EVM Implementation" +sidebar_label: "Overview" +sidebar_position: 1 +--- + +# EVM Deployment Implementation + +This documentation covers the EVM-specific implementation of the CCIP Deployment Tooling API. The EVM adapter is the most comprehensive reference implementation, supporting contract versions from v1.0.0 through v1.6.5. + +For the shared interfaces this implementation fulfills, see [Interfaces Reference](../../../../deployment/docs/interfaces.md). For the shared types, see [Types Reference](../../../../deployment/docs/types.md). + +--- + +## Package Layout + +``` +chains/evm/deployment/ +├── utils/ +│ ├── common.go # Address/version utilities +│ ├── datastore/ +│ │ └── datastore.go # EVM address format conversions +│ └── operations/ +│ └── contract/ +│ ├── read.go # NewRead operation pattern +│ ├── write.go # NewWrite operation pattern +│ ├── deploy.go # NewDeploy operation pattern +│ └── function.go # FunctionInput struct +├── v1_0_0/ # Core infrastructure (MCMS, Router basics) +│ ├── adapters/ +│ ├── operations/ +│ └── sequences/ +├── v1_2_0/ # Router enhancements +│ ├── adapters/ +│ └── operations/ +├── v1_5_0/ # Token support +│ ├── adapters/ +│ ├── changesets/ +│ ├── operations/ +│ └── sequences/ +├── v1_5_1/ # Token pool patch +│ ├── operations/ +│ └── sequences/ +├── v1_6_0/ # Complete implementation +│ ├── adapters/ # Registration + specialized adapters +│ ├── operations/ # All contract operations +│ ├── sequences/ # All sequence compositions +│ └── testadapter/ # Test utilities +├── v1_6_1/ # Operations + sequences +├── v1_6_2/ # Operations only +├── v1_6_3/ # Operations only +└── v1_6_5/ # Operations only +``` + +## Version Support + +| Version | Scope | Contents | +|---------|-------|----------| +| **v1.0.0** | Core infrastructure | MCMS deployment, Router, WETH, LINK | +| **v1.2.0** | Router updates | Router operations for lane migration | +| **v1.5.0** | Token support | Token pools, token admin registry | +| **v1.5.1** | Token pool patch | LockRelease pool enhancements | +| **v1.6.0** | Complete implementation | Full adapter, all contracts, all sequences | +| **v1.6.1 -- v1.6.5** | Incremental updates | Additional operations and sequences | + +The v1.6.0 version is the primary implementation that registers the `EVMAdapter` with all shared registries. Newer versions add operations and sequences that build upon the v1.6.0 foundation. + +## Documentation + +- [EVMAdapter Reference](adapter.md) -- adapter struct, interface implementations, registration +- [Operations Reference](operations.md) -- operation framework and all contract operations +- [Sequences Reference](sequences.md) -- all sequences and EVM-specific changesets diff --git a/chains/evm/deployment/docs/operations.md b/chains/evm/deployment/docs/operations.md new file mode 100644 index 0000000000..991fb569a8 --- /dev/null +++ b/chains/evm/deployment/docs/operations.md @@ -0,0 +1,245 @@ +--- +title: "EVM Operations Reference" +sidebar_label: "Operations" +sidebar_position: 3 +--- + +# EVM Operations Reference + +Operations are atomic building blocks that perform a single contract interaction. The EVM implementation provides a typed framework for creating read, write, and deploy operations with built-in MCMS support. + +**Source:** [utils/operations/contract/](../utils/operations/contract/) + +--- + +## Operation Framework + +### NewRead + +Creates a read-only contract call operation. + +**Source:** [utils/operations/contract/read.go](../utils/operations/contract/read.go) + +```go +func NewRead[ARGS any, RET any, C any](params ReadParams[ARGS, RET, C]) *Operation[FunctionInput[ARGS], RET, evm.Chain] +``` + +**Parameters:** +```go +type ReadParams[ARGS any, RET any, C any] struct { + Name string // Operation ID + Version *semver.Version // Contract version + Description string + ContractType deployment.ContractType + NewContract func(address common.Address, backend bind.ContractBackend) (C, error) + CallContract func(contract C, opts *bind.CallOpts, input ARGS) (RET, error) +} +``` + +**Behavior:** Instantiates the contract using `NewContract`, then calls `CallContract` with the provided args. Returns the result directly. + +### NewWrite + +Creates a state-modifying contract call operation with automatic MCMS fallback. + +**Source:** [utils/operations/contract/write.go](../utils/operations/contract/write.go) + +```go +func NewWrite[ARGS any, C any](params WriteParams[ARGS, C]) *Operation[FunctionInput[ARGS], WriteOutput, evm.Chain] +``` + +**Parameters:** +```go +type WriteParams[ARGS any, C any] struct { + Name string + Version *semver.Version + Description string + ContractType deployment.ContractType + ContractABI string + NewContract func(address common.Address, backend bind.ContractBackend) (C, error) + IsAllowedCaller func(contract C, opts *bind.CallOpts, caller common.Address, input ARGS) (bool, error) + Validate func(input ARGS) error + CallContract func(contract C, opts *bind.TransactOpts, input ARGS) (*eth_types.Transaction, error) +} +``` + +**Behavior:** +1. Validates input via `Validate` (if provided) +2. Checks if the deployer key is an allowed caller via `IsAllowedCaller` +3. **If allowed:** Executes the transaction directly, waits for confirmation, returns `WriteOutput` with `ExecInfo` populated +4. **If not allowed:** Encodes the transaction as an MCMS batch operation, returns `WriteOutput` without `ExecInfo` + +**Output:** +```go +type WriteOutput struct { + ChainSelector uint64 + Tx mcms_types.Transaction + ExecInfo *ExecInfo // nil if transaction was deferred to MCMS +} + +func (o WriteOutput) Executed() bool // Returns true if executed directly +``` + +**Common Caller Checks:** +- `OnlyOwner()` -- checks if caller is the contract owner (with retry for testnet flakiness) +- `AllCallersAllowed()` -- always returns true (permissive) + +### NewDeploy + +Creates a contract deployment operation. + +**Source:** [utils/operations/contract/deploy.go](../utils/operations/contract/deploy.go) + +```go +func NewDeploy[ARGS any](params DeployParams[ARGS]) *Operation[DeployInput[ARGS], datastore.AddressRef, evm.Chain] +``` + +**Parameters:** +```go +type DeployParams[ARGS any] struct { + Name string + Version *semver.Version + Description string + ContractMetadata *bind.MetaData + BytecodeByTypeAndVersion map[string]Bytecode + Validate func(input ARGS) error +} +``` + +**Behavior:** Deploys the contract using the chain's deployer key. Supports both standard EVM and ZkSync VM bytecodes. Returns a `datastore.AddressRef` with the deployed address, type, and version. + +### Common Input Type + +```go +type FunctionInput[ARGS any] struct { + Address common.Address + ChainSelector uint64 + Args ARGS +} +``` + +### Batch Operation Helper + +```go +func NewBatchOperationFromWrites(writes []WriteOutput) (mcms_types.BatchOperation, error) +``` + +Converts a slice of `WriteOutput` into an MCMS `BatchOperation`. Filters out already-executed transactions. + +--- + +## Operations by Contract + +All operations are in [v1_6_0/operations/](../v1_6_0/operations/). + +### OnRamp (`operations/onramp/`) + +| Operation | Type | Description | +|-----------|------|-------------| +| Deploy | Deploy | Deploys OnRamp contract | +| GetDestChainConfig | Read | Gets destination chain configuration | +| GetDynamicConfig | Read | Gets dynamic configuration | +| GetStaticConfig | Read | Gets static configuration | +| ApplyDestChainConfigUpdates | Write | Updates destination chain configs for remote chains | + +### OffRamp (`operations/offramp/`) + +| Operation | Type | Description | +|-----------|------|-------------| +| Deploy | Deploy | Deploys OffRamp contract | +| GetSourceChainConfig | Read | Gets source chain configuration | +| GetStaticConfig | Read | Gets static configuration | +| GetDynamicConfig | Read | Gets dynamic configuration | +| ApplySourceChainConfigUpdates | Write | Updates source chain configs for remote chains | + +### FeeQuoter (`operations/fee_quoter/`) + +| Operation | Type | Description | +|-----------|------|-------------| +| Deploy | Deploy | Deploys FeeQuoter contract | +| ApplyDestChainConfigUpdates | Write | Updates destination chain fee configs | +| UpdatePrices | Write | Updates gas and token prices | +| ApplyTokenTransferFeeConfigUpdates | Write | Sets per-token transfer fee configurations | +| GetDestChainConfig | Read | Gets destination chain fee config | +| GetTokenTransferFeeConfig | Read | Gets token transfer fee configuration | + +### NonceManager (`operations/nonce_manager/`) + +| Operation | Type | Description | +|-----------|------|-------------| +| Deploy | Deploy | Deploys NonceManager contract | +| ApplyAuthorizedCallerUpdates | Write | Authorizes OnRamp/OffRamp as callers | + +### RMN Remote (`operations/rmn_remote/`) + +| Operation | Type | Description | +|-----------|------|-------------| +| Deploy | Deploy | Deploys RMNRemote contract | + +### CCIP Home (`operations/ccip_home/`) + +Operations for the CCIPHome contract on the home chain. + +### Token Pool Operations + +| Module | Description | +|--------|-------------| +| `burn_mint_with_external_minter_token_pool/` | BurnMint pool with external minter | +| `hybrid_with_external_minter_token_pool/` | Hybrid pool with external minter | + +### Token Governor (`operations/token_governor/`) + +Operations for the Token Governor contract. + +--- + +## Creating New Operations + +### Read Operation Example + +```go +var GetDestChainConfig = contract.NewRead(contract.ReadParams[uint64, DestChainConfig, *onramp.OnRamp]{ + Name: "onramp:getDestChainConfig", + Version: semver.MustParse("1.6.0"), + Description: "Gets the destination chain config for a remote chain", + ContractType: "OnRamp", + NewContract: onramp.NewOnRamp, + CallContract: func(c *onramp.OnRamp, opts *bind.CallOpts, destChainSelector uint64) (DestChainConfig, error) { + return c.GetDestChainConfig(opts, destChainSelector) + }, +}) +``` + +### Write Operation Example + +```go +var ApplyDestChainConfigUpdates = contract.NewWrite(contract.WriteParams[[]DestChainConfigArgs, *onramp.OnRamp]{ + Name: "onramp:applyDestChainConfigUpdates", + Version: semver.MustParse("1.6.0"), + Description: "Updates destination chain configurations", + ContractType: "OnRamp", + ContractABI: onramp.OnRampABI, + NewContract: onramp.NewOnRamp, + IsAllowedCaller: contract.OnlyOwner[[]DestChainConfigArgs](), + CallContract: func(c *onramp.OnRamp, opts *bind.TransactOpts, args []DestChainConfigArgs) (*types.Transaction, error) { + return c.ApplyDestChainConfigUpdates(opts, args) + }, +}) +``` + +### Deploy Operation Example + +```go +var Deploy = contract.NewDeploy(contract.DeployParams[DeployInput]{ + Name: "onramp:deploy", + Version: semver.MustParse("1.6.0"), + Description: "Deploys the OnRamp contract", + ContractMetadata: onramp.OnRampMetaData, +}) +``` + +--- + +## Code Generation + +Many EVM operations are auto-generated from Go bindings (gethwrappers). The generation configuration is in `operations_gen_config.yaml`. Generated operations follow the same `NewRead`/`NewWrite`/`NewDeploy` patterns but are produced automatically from the contract ABI. diff --git a/chains/evm/deployment/docs/sequences.md b/chains/evm/deployment/docs/sequences.md new file mode 100644 index 0000000000..e574da7d68 --- /dev/null +++ b/chains/evm/deployment/docs/sequences.md @@ -0,0 +1,211 @@ +--- +title: "EVM Sequences Reference" +sidebar_label: "Sequences" +sidebar_position: 4 +--- + +# EVM Sequences Reference + +Sequences compose multiple operations into complete workflows. Each sequence is defined as a package-level variable using `operations.NewSequence`. + +**Source:** [v1_6_0/sequences/](../v1_6_0/sequences/) + +--- + +## Sequence Catalog + +### Deploy Chain Contracts + +**Source:** [v1_6_0/sequences/deploy_chain_contracts.go](../v1_6_0/sequences/deploy_chain_contracts.go) + +Deploys all CCIP infrastructure contracts on a single EVM chain. This is the primary deployment sequence. + +**Variable:** `DeployChainContracts` +**Input:** `ContractDeploymentConfigPerChainWithAddress` +**Output:** `OnChainOutput` + +**Deployment order:** +1. WETH9 (wrapped native token) +2. LINK token (or use existing) +3. RMNRemote + RMNProxy (set ARM on proxy) +4. Router + TestRouter +5. TokenAdminRegistry + RegistryModule +6. NonceManager (authorize OnRamp/OffRamp as callers) +7. FeeQuoter (configure authorized callers, LINK/native token pricing) +8. OffRamp (set source chain configs) +9. OnRamp (set destination chain configs) +10. PingPongDemo (optional, if `DeployPingPongDapp` is true) + +### MCMS Sequences + +**Source:** [v1_6_0/sequences/mcms.go](../v1_6_0/sequences/mcms.go) + +EVM delegates MCMS deployment to the v1.0.0 deployer since MCMS contracts are not version-specific on EVM. + +| Method | Behavior | +|--------|----------| +| `DeployMCMS()` | Returns v1.0.0 deployer's `DeployMCMS()` sequence | +| `FinalizeDeployMCMS()` | Returns v1.0.0 deployer's `FinalizeDeployMCMS()` (no-op) | +| `UpdateMCMSConfig()` | Returns v1.0.0 update config sequence | +| `GrantAdminRoleToTimelock()` | Returns admin role granting sequence | + +### Lane Configuration + +**Source:** [v1_6_0/sequences/update_lanes.go](../v1_6_0/sequences/update_lanes.go) + +#### ConfigureLaneLegAsSource + +Configures this chain as the source end of a lane. + +**Input:** `UpdateLanesInput` + +**Steps:** +1. Apply OnRamp destination chain config updates +2. Apply FeeQuoter destination chain config updates +3. Update gas and token prices on FeeQuoter + +#### ConfigureLaneLegAsDest + +Configures this chain as the destination end of a lane. + +**Input:** `UpdateLanesInput` + +**Steps:** +1. Apply OffRamp source chain config updates +2. Apply Router ramp updates (register OnRamp/OffRamp for remote chain) + +### FeeQuoter Sequences + +**Source:** [v1_6_0/sequences/fee_quoter.go](../v1_6_0/sequences/fee_quoter.go) + +| Sequence | Description | +|----------|-------------| +| `FeeQuoterApplyDestChainConfigUpdatesSequence` | Updates destination chain fee configs | +| `FeeQuoterUpdatePricesSequence` | Updates gas and token prices | +| `FeeQuoterApplyTokenTransferFeeConfigUpdatesSequence` | Sets per-token transfer fees | +| `FeeQuoterImportConfigSequence` | Imports existing on-chain fee config | + +### OnRamp Sequences + +**Source:** [v1_6_0/sequences/onramp.go](../v1_6_0/sequences/onramp.go) + +| Sequence | Description | +|----------|-------------| +| `OnRampApplyDestChainConfigUpdatesSequence` | Applies destination chain config | +| `OnRampImportConfigSequence` | Imports existing on-chain config | + +### OffRamp Sequences + +**Source:** [v1_6_0/sequences/offramp.go](../v1_6_0/sequences/offramp.go) + +| Sequence | Description | +|----------|-------------| +| `OffRampApplySourceChainConfigUpdatesSequence` | Applies source chain config | +| `OffRampImportConfigSequence` | Imports existing on-chain config | + +### Router Sequences + +**Source:** [v1_6_0/sequences/router.go](../v1_6_0/sequences/router.go) + +| Sequence | Description | +|----------|-------------| +| `RouterApplyRampUpdatesSequence` | Updates OnRamp/OffRamp references on Router | + +### RMN Remote Sequences + +**Source:** [v1_6_0/sequences/rmn_remote.go](../v1_6_0/sequences/rmn_remote.go) + +RMN configuration sequences for blessed/cursed state management. + +### OCR3 Sequences + +**Source:** [v1_6_0/sequences/ocr.go](../v1_6_0/sequences/ocr.go) + +Sets OCR3 configuration on the OffRamp contract. + +### Token Sequences + +**Source:** [v1_6_0/sequences/token.go](../v1_6_0/sequences/token.go), [v1_6_0/sequences/token_and_pools.go](../v1_6_0/sequences/token_and_pools.go) + +| Sequence | Description | +|----------|-------------| +| `DeployToken` | Deploys ERC20 BurnMint/BurnMintWithDrip token | +| `ConfigureTokenForTransfersSequence` | Registers token + configures remote chains | +| `ManualRegistration` | Registers customer token via proposeAdministrator | +| `SetTokenPoolRateLimits` | Sets inbound/outbound rate limits | +| `DeployTokenPoolForToken` | Deploys token pool for existing token | +| `UpdateAuthorities` | Transfers token/pool ownership to timelock | + +### Token Pool Sequences + +**Source:** [v1_6_0/sequences/deploy_token_pool_contracts.go](../v1_6_0/sequences/deploy_token_pool_contracts.go) + +Handles deployment of various token pool types: +- BurnMintTokenPool +- BurnWithFromMintTokenPool +- BurnFromMintTokenPool +- LockReleaseTokenPool +- BurnMintWithExternalMinterTokenPool +- CCTPTokenPool + +Supports both v1.5.1 and v1.6.1 pool versions. + +### Token Governor Sequences + +**Source:** [v1_6_0/sequences/token_governor.go](../v1_6_0/sequences/token_governor.go) + +Sequences for deploying and configuring the Token Governor contract. + +### PingPong Sequences + +| Sequence | Description | +|----------|-------------| +| `ConfigurePingPongSequence` | Configures PingPong demo for a lane | + +--- + +## Sequence Composition Pattern + +EVM sequences follow a consistent pattern: + +```go +var MySequence = operations.NewSequence( + "my-sequence", + semver.MustParse("1.6.0"), + "Description of what this sequence does", + func(b operations.Bundle, chains cldf_chain.BlockChains, input MyInput) (sequences.OnChainOutput, error) { + var output sequences.OnChainOutput + + // Execute operations + writeResult, err := operations.ExecuteOperation(b, MyWriteOp, chain, contract.FunctionInput[Args]{ + Address: contractAddr, + ChainSelector: input.ChainSelector, + Args: myArgs, + }) + if err != nil { + return output, err + } + + // Collect MCMS batch operations from writes + batchOp, err := contract.NewBatchOperationFromWrites([]contract.WriteOutput{writeResult.Output}) + if err != nil { + return output, err + } + output.BatchOps = append(output.BatchOps, batchOp) + + return output, nil + }, +) +``` + +Key patterns: +- Operations return `WriteOutput` which may or may not have been executed directly +- `NewBatchOperationFromWrites` collects un-executed writes into MCMS proposals +- Deploy operations return `datastore.AddressRef` which gets added to `output.Addresses` +- Sub-sequences can be composed via `sequences.RunAndMergeSequence` + +--- + +## EVM-Specific Changesets + +The EVM implementation also provides version-specific changesets in `v1_5_0/changesets/` and `v1_6_0/changesets/` for MCMS configuration updates and other EVM-specific operations not covered by the shared changeset layer. diff --git a/chains/evm/deployment/go.mod b/chains/evm/deployment/go.mod index e05d18d191..4f8b4a0271 100644 --- a/chains/evm/deployment/go.mod +++ b/chains/evm/deployment/go.mod @@ -12,19 +12,22 @@ replace github.com/smartcontractkit/chainlink-ccip => ../../../. require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/aws/smithy-go v1.24.0 - github.com/ethereum/go-ethereum v1.17.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/rs/zerolog v1.34.0 github.com/smartcontractkit/ccip-contract-examples/chains/evm v0.0.0-20250826190403-aed7f5f33cde github.com/smartcontractkit/chain-selectors v1.0.97 github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260312161144-d895b42081a0 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260312161144-d895b42081a0 - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 github.com/smartcontractkit/chainlink-deployments-framework v0.80.2 github.com/smartcontractkit/chainlink-evm v0.3.3 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd + github.com/smartcontractkit/chainlink-ton v0.0.0-20260213025045-83535910e2c0 github.com/smartcontractkit/mcms v0.36.0 github.com/stretchr/testify v1.11.1 + github.com/xssnick/tonutils-go v1.14.1 github.com/zksync-sdk/zksync2-go v1.1.1-0.20250620124214-2c742ee399c6 + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 golang.org/x/sync v0.19.0 ) @@ -80,7 +83,7 @@ require ( github.com/creachadair/mds v0.13.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dchest/siphash v1.2.3 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v28.5.1+incompatible // indirect @@ -89,7 +92,7 @@ require ( github.com/ebitengine/purego v0.9.0 // indirect github.com/emicklei/dot v1.6.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/fatih/color v1.18.0 // indirect github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b // indirect @@ -223,13 +226,13 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b // indirect github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 // indirect github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.2 // indirect - github.com/smartcontractkit/chainlink-ton v0.0.0-20260213025045-83535910e2c0 // indirect github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 // indirect github.com/smartcontractkit/freeport v0.1.3-0.20250716200817-cb5dfd0e369e // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect @@ -241,7 +244,7 @@ require ( github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/testcontainers/testcontainers-go v0.39.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect @@ -256,13 +259,12 @@ require ( github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - github.com/xssnick/tonutils-go v1.14.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.mongodb.org/mongo-driver v1.17.2 // indirect 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.63.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect @@ -273,28 +275,27 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/zap v1.27.1 // indirect golang.org/x/crypto v0.48.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect diff --git a/chains/evm/deployment/go.sum b/chains/evm/deployment/go.sum index 59661bd9a0..a3ecf5bf44 100644 --- a/chains/evm/deployment/go.sum +++ b/chains/evm/deployment/go.sum @@ -201,8 +201,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -230,12 +230,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= @@ -731,8 +731,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d/go.mod h1:bgmqE7x9xwmIVr8PqLbC0M5iPm4AV2DBl596lO6S5Sw= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 h1:Z4t2ZY+ZyGWxtcXvPr11y4o3CGqhg3frJB5jXkCSvWA= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 h1:eui+u6ge2RYW01F/DeXWrc5UOqc+8+lyPoi9TIAmMgo= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= 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-deployments-framework v0.80.2 h1:M944dbKDHJiqqGOfiaeQw9nUk/uuci8ggUXSgfSzW5Q= @@ -743,12 +743,14 @@ github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd/go.mod h1:7Jlt72+V9891y3LnGwHzmQwt9tfEGYryRKiGlQHo/o8= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 h1:JU1JUrkzdAUHsOYdS9DENPkJfmrxweFRPRSztad6oPM= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2/go.mod h1:+pRGfDej1r7cHMs1dYmuyPuOZzYB9Q+PKu0FvZOYlmw= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 h1:03tbcwjyIEjvHba1IWOj1sfThwebm2XNzyFHSuZtlWc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 h1:xHPmFDhff7QpeFxKsZfk+24j4AlnQiFjjRh5O87Peu4= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b h1:36knUpKHHAZ86K4FGWXtx8i/EQftGdk2bqCoEu/Cha8= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 h1:KyPROV+v7P8VdiU7JhVuGLcDlEBsURSpQmSCgNBTY+s= @@ -804,8 +806,8 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -868,8 +870,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -890,20 +892,20 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -953,8 +955,8 @@ golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZP golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= 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= @@ -1091,8 +1093,8 @@ 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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1124,8 +1126,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1141,8 +1143,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/chains/evm/deployment/v1_0_0/adapters/deployer.go b/chains/evm/deployment/v1_0_0/adapters/deployer.go index 9a6fc8c50a..b71d742963 100644 --- a/chains/evm/deployment/v1_0_0/adapters/deployer.go +++ b/chains/evm/deployment/v1_0_0/adapters/deployer.go @@ -32,6 +32,33 @@ func (a *EVMDeployer) SetOCR3Config() *cldf_ops.Sequence[ccipapi.SetOCR3ConfigIn return nil } +func (a *EVMDeployer) UpdateMCMSConfig() *cldf_ops.Sequence[ccipapi.UpdateMCMSConfigInputPerChainWithSelector, sequtil.OnChainOutput, cldf_chain.BlockChains] { + return cldf_ops.NewSequence( + "update-mcms-config", + semver.MustParse("1.0.0"), + "Updates MCMS Configs of the specified contracts with the specified configs", + func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, in ccipapi.UpdateMCMSConfigInputPerChainWithSelector) (output sequtil.OnChainOutput, err error) { + evmChain, ok := chains.EVMChains()[in.ChainSelector] + if !ok { + return sequtil.OnChainOutput{}, fmt.Errorf("chain with selector %d not found in environment", in.ChainSelector) + } + + // create sequence input + seqInput := seq.SeqSetMCMSConfigInput{ + ChainSelector: in.ChainSelector, + MCMConfig: &in.MCMConfig, + MCMContracts: in.MCMContracts, + } + report, err := cldf_ops.ExecuteSequence(b, seq.SeqSetMCMSConfigs, evmChain, seqInput) + if err != nil { + return sequtil.OnChainOutput{}, fmt.Errorf("failed to update mcms config on chain %d: %w", in.ChainSelector, err) + } + output.BatchOps = append(output.BatchOps, report.Output.BatchOps...) + + return output, nil + }) +} + func (a *EVMDeployer) GrantAdminRoleToTimelock() *cldf_ops.Sequence[ccipapi.GrantAdminRoleToTimelockConfigPerChainWithSelector, sequtil.OnChainOutput, cldf_chain.BlockChains] { return cldf_ops.NewSequence( "grant-admin-role-of-timelock-to-timelock", @@ -51,7 +78,7 @@ func (a *EVMDeployer) GrantAdminRoleToTimelock() *cldf_ops.Sequence[ccipapi.Gran } report, err := cldf_ops.ExecuteSequence(b, seq.SeqGrantAdminRoleOfTimelockToTimelock, evmChain, seqInput) if err != nil { - return sequtil.OnChainOutput{}, fmt.Errorf("failed to deploy and configure proposer MCM on chain %d: %w", in.ChainSelector, err) + return sequtil.OnChainOutput{}, fmt.Errorf("failed to grant admin role to timelock on chain %d: %w", in.ChainSelector, err) } return report.Output, nil diff --git a/chains/evm/deployment/v1_0_0/adapters/deployer_test.go b/chains/evm/deployment/v1_0_0/adapters/deployer_test.go index 4001a604a7..c123e08b2d 100644 --- a/chains/evm/deployment/v1_0_0/adapters/deployer_test.go +++ b/chains/evm/deployment/v1_0_0/adapters/deployer_test.go @@ -13,15 +13,17 @@ import ( "github.com/smartcontractkit/chainlink-deployments-framework/datastore" "github.com/smartcontractkit/chainlink-deployments-framework/engine/test/environment" "github.com/smartcontractkit/mcms/sdk/evm/bindings" + mcms_types "github.com/smartcontractkit/mcms/types" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_0_0/adapters" ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_0_0/operations" - v1_0 "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" + deployops "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" "github.com/smartcontractkit/chainlink-ccip/deployment/testhelpers" "github.com/smartcontractkit/chainlink-ccip/deployment/utils" deploymentutils "github.com/smartcontractkit/chainlink-ccip/deployment/utils" datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" ) func TestDeployMCMS(t *testing.T) { @@ -37,12 +39,12 @@ func TestDeployMCMS(t *testing.T) { evmChain2 := env.BlockChains.EVMChains()[selector2] evmDeployer := &adapters.EVMDeployer{} - dReg := v1_0.GetRegistry() - dReg.RegisterDeployer(chainsel.FamilyEVM, v1_0.MCMSVersion, evmDeployer) - cs := v1_0.DeployMCMS(dReg, nil) - output, err := cs.Apply(*env, v1_0.MCMSDeploymentConfig{ - AdapterVersion: v1_0.MCMSVersion, - Chains: map[uint64]v1_0.MCMSDeploymentConfigPerChain{ + dReg := deployops.GetRegistry() + dReg.RegisterDeployer(chainsel.FamilyEVM, deployops.MCMSVersion, evmDeployer) + cs := deployops.DeployMCMS(dReg, nil) + output, err := cs.Apply(*env, deployops.MCMSDeploymentConfig{ + AdapterVersion: deployops.MCMSVersion, + Chains: map[uint64]deployops.MCMSDeploymentConfigPerChain{ selector1: { Canceller: testhelpers.SingleGroupMCMS(), Bypasser: testhelpers.SingleGroupMCMS(), @@ -124,6 +126,137 @@ func TestDeployMCMS(t *testing.T) { require.True(t, hasRole, "Call Proxy should have admin role for EXECUTOR_ROLE") } +func TestUpdateMCMSConfig(t *testing.T) { + t.Parallel() + selector1 := chainsel.TEST_90000001.Selector + selector2 := chainsel.TEST_90000002.Selector + env, err := environment.New(t.Context(), + environment.WithEVMSimulated(t, []uint64{selector1, selector2}), + ) + require.NoError(t, err) + env.Logger = logger.Test(t) + evmChain1 := env.BlockChains.EVMChains()[selector1] + evmChain2 := env.BlockChains.EVMChains()[selector2] + + evmDeployer := &adapters.EVMDeployer{} + dReg := deployops.GetRegistry() + dReg.RegisterDeployer(chainsel.FamilyEVM, deployops.MCMSVersion, evmDeployer) + + // deploy one set of timelock and MCMS contracts on each chain + deployMCMS := deployops.DeployMCMS(dReg, nil) + output, err := deployMCMS.Apply(*env, deployops.MCMSDeploymentConfig{ + AdapterVersion: semver.MustParse("1.0.0"), + Chains: map[uint64]deployops.MCMSDeploymentConfigPerChain{ + selector1: { + Canceller: testhelpers.SingleGroupMCMS(), + Bypasser: testhelpers.SingleGroupMCMS(), + Proposer: testhelpers.SingleGroupMCMS(), + TimelockMinDelay: big.NewInt(0), + Qualifier: ptr.String("CLLCCIP"), + TimelockAdmin: evmChain1.DeployerKey.From, + }, + selector2: { + Canceller: testhelpers.SingleGroupMCMS(), + Bypasser: testhelpers.SingleGroupMCMS(), + Proposer: testhelpers.SingleGroupMCMS(), + TimelockMinDelay: big.NewInt(0), + Qualifier: ptr.String("CLLCCIP"), + TimelockAdmin: evmChain2.DeployerKey.From, + }, + }, + }) + require.NoError(t, err) + require.Greater(t, len(output.Reports), 0) + env.DataStore = output.DataStore.Seal() + + // get recently deployed MCMS addresses + mcmsRefs := make(map[uint64][]datastore.AddressRef) + for _, sel := range []uint64{selector1, selector2} { + cancellerRef, err := datastore_utils.FindAndFormatRef(env.DataStore, datastore.AddressRef{ + ChainSelector: sel, + Type: datastore.ContractType(deploymentutils.CancellerManyChainMultisig), + Qualifier: "CLLCCIP", + Version: semver.MustParse("1.0.0"), + }, sel, datastore_utils.FullRef) + require.NoError(t, err) + bypasserRef, err := datastore_utils.FindAndFormatRef(env.DataStore, datastore.AddressRef{ + ChainSelector: sel, + Type: datastore.ContractType(deploymentutils.CancellerManyChainMultisig), + Qualifier: "CLLCCIP", + Version: semver.MustParse("1.0.0"), + }, sel, datastore_utils.FullRef) + require.NoError(t, err) + proposerRef, err := datastore_utils.FindAndFormatRef(env.DataStore, datastore.AddressRef{ + ChainSelector: sel, + Type: datastore.ContractType(deploymentutils.CancellerManyChainMultisig), + Qualifier: "CLLCCIP", + Version: semver.MustParse("1.0.0"), + }, sel, datastore_utils.FullRef) + require.NoError(t, err) + mcmsRefs[sel] = append(mcmsRefs[sel], cancellerRef, bypasserRef, proposerRef) + } + + // check that deployed config is correct + for _, sel := range []uint64{selector1, selector2} { + for _, ref := range mcmsRefs[sel] { + evmChain := env.BlockChains.EVMChains()[sel] + mcmsContract, err := bindings.NewManyChainMultiSig(common.HexToAddress(ref.Address), evmChain.Client) + require.NoError(t, err) + + // binding is done, now check config + config, err := mcmsContract.GetConfig(&bind.CallOpts{ + Context: t.Context(), + }) + require.NoError(t, err) + + numOfSigners := len(config.Signers) + require.Equal(t, numOfSigners, len(testhelpers.SingleGroupMCMS().Signers)) // should be 1 + } + } + + // update the config for each MCMS contract + updateMcmsConfigMCMS := deployops.UpdateMCMSConfig(dReg, nil) + output, err = updateMcmsConfigMCMS.Apply(*env, deployops.UpdateMCMSConfigInput{ + AdapterVersion: semver.MustParse("1.0.0"), + Chains: map[uint64]deployops.UpdateMCMSConfigInputPerChain{ + selector1: { + MCMConfig: testhelpers.SingleGroupMCMSTwoSigners(), + MCMContracts: mcmsRefs[selector1], + }, + selector2: { + MCMConfig: testhelpers.SingleGroupMCMSTwoSigners(), + MCMContracts: mcmsRefs[selector2], + }, + }, + MCMS: mcms.Input{ + OverridePreviousRoot: false, + ValidUntil: 3759765795, + TimelockDelay: mcms_types.MustParseDuration("0s"), + TimelockAction: mcms_types.TimelockActionSchedule, + Qualifier: "CLLCCIP", + Description: "update mcms config test", + }, + }) + require.NoError(t, err) + require.Greater(t, len(output.Reports), 0) + + // check that MCMS configs are updated correctly + for _, sel := range []uint64{selector1, selector2} { + for _, ref := range mcmsRefs[sel] { + evmChain := env.BlockChains.EVMChains()[sel] + mcmsContract, err := bindings.NewManyChainMultiSig(common.HexToAddress(ref.Address), evmChain.Client) + require.NoError(t, err) + config, err := mcmsContract.GetConfig(&bind.CallOpts{ + Context: t.Context(), + }) + require.NoError(t, err) + + numOfSigners := len(config.Signers) + require.Equal(t, numOfSigners, len(testhelpers.SingleGroupMCMSTwoSigners().Signers)) // should be 2 + } + } +} + func TestGrantAdminRoleToTimelock(t *testing.T) { t.Parallel() selector1 := chainsel.TEST_90000001.Selector @@ -137,14 +270,14 @@ func TestGrantAdminRoleToTimelock(t *testing.T) { evmChain2 := env.BlockChains.EVMChains()[selector2] evmDeployer := &adapters.EVMDeployer{} - dReg := v1_0.GetRegistry() - dReg.RegisterDeployer(chainsel.FamilyEVM, v1_0.MCMSVersion, evmDeployer) + dReg := deployops.GetRegistry() + dReg.RegisterDeployer(chainsel.FamilyEVM, deployops.MCMSVersion, evmDeployer) // deploy two timelocks on each chain so we can set one as the admin of the other - deployMCMS := v1_0.DeployMCMS(dReg, nil) - output, err := deployMCMS.Apply(*env, v1_0.MCMSDeploymentConfig{ - AdapterVersion: v1_0.MCMSVersion, - Chains: map[uint64]v1_0.MCMSDeploymentConfigPerChain{ + deployMCMS := deployops.DeployMCMS(dReg, nil) + output, err := deployMCMS.Apply(*env, deployops.MCMSDeploymentConfig{ + AdapterVersion: deployops.MCMSVersion, + Chains: map[uint64]deployops.MCMSDeploymentConfigPerChain{ selector1: { Canceller: testhelpers.SingleGroupMCMS(), Bypasser: testhelpers.SingleGroupMCMS(), @@ -167,9 +300,9 @@ func TestGrantAdminRoleToTimelock(t *testing.T) { require.Greater(t, len(output.Reports), 0) ds := output.DataStore - output, err = deployMCMS.Apply(*env, v1_0.MCMSDeploymentConfig{ - AdapterVersion: v1_0.MCMSVersion, - Chains: map[uint64]v1_0.MCMSDeploymentConfigPerChain{ + output, err = deployMCMS.Apply(*env, deployops.MCMSDeploymentConfig{ + AdapterVersion: deployops.MCMSVersion, + Chains: map[uint64]deployops.MCMSDeploymentConfigPerChain{ selector1: { Canceller: testhelpers.SingleGroupMCMS(), Bypasser: testhelpers.SingleGroupMCMS(), @@ -238,10 +371,10 @@ func TestGrantAdminRoleToTimelock(t *testing.T) { } // grant admin role to timelock - grantAdminRoleMCMS := v1_0.GrantAdminRoleToTimelock(dReg, nil) - output, err = grantAdminRoleMCMS.Apply(*env, v1_0.GrantAdminRoleToTimelockConfig{ + grantAdminRoleMCMS := deployops.GrantAdminRoleToTimelock(dReg, nil) + output, err = grantAdminRoleMCMS.Apply(*env, deployops.GrantAdminRoleToTimelockConfig{ AdapterVersion: semver.MustParse("1.0.0"), - Chains: map[uint64]v1_0.GrantAdminRoleToTimelockConfigPerChain{ + Chains: map[uint64]deployops.GrantAdminRoleToTimelockConfigPerChain{ selector1: { TimelockToTransferRef: datastore.AddressRef{ Type: datastore.ContractType(deploymentutils.RBACTimelock), diff --git a/chains/evm/deployment/v1_0_0/sequences/mcms.go b/chains/evm/deployment/v1_0_0/sequences/mcms.go index e5aa260e6b..3d59dbb69a 100644 --- a/chains/evm/deployment/v1_0_0/sequences/mcms.go +++ b/chains/evm/deployment/v1_0_0/sequences/mcms.go @@ -42,6 +42,12 @@ type SeqGrantAdminRoleOfTimelockToTimelockInput struct { NewAdminTimelockAddress common.Address } +type SeqSetMCMSConfigInput struct { + ChainSelector uint64 + MCMConfig *types.Config + MCMContracts []datastore.AddressRef +} + var SeqDeployMCMWithConfig = cldf_ops.NewSequence( "seq-deploy-mcm-with-config", semver.MustParse("1.0.0"), @@ -109,6 +115,43 @@ var SeqDeployMCMWithConfig = cldf_ops.NewSequence( }, ) +var SeqSetMCMSConfigs = cldf_ops.NewSequence( + "seq-set-mcm-config", + semver.MustParse("1.0.0"), + "Sets config on previously deployed MCM contract", + func(b cldf_ops.Bundle, chain cldf_evm.Chain, in SeqSetMCMSConfigInput) (output sequences.OnChainOutput, err error) { + for _, mcmContract := range in.MCMContracts { + // Set config on contract + groupQuorums, groupParents, signerAddresses, signerGroups, err := sdk.ExtractSetConfigInputs(in.MCMConfig) + if err != nil { + return sequences.OnChainOutput{}, err + } + report, err := cldf_ops.ExecuteOperation(b, ops.OpEVMSetConfigMCM, chain, + contract.FunctionInput[ops.OpSetConfigMCMInput]{ + ChainSelector: in.ChainSelector, + Address: common.HexToAddress(mcmContract.Address), + Args: ops.OpSetConfigMCMInput{ + SignerAddresses: signerAddresses, + SignerGroups: signerGroups, + GroupQuorums: groupQuorums, + GroupParents: groupParents, + }, + }) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to update mcms config on chain %d for contract with address %s: %w", + in.ChainSelector, mcmContract.Address, err) + } + batchOp, err := contract.NewBatchOperationFromWrites([]contract.WriteOutput{report.Output}) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to create batch operation from writes: %w", err) + } + output.BatchOps = append(output.BatchOps, batchOp) + } + + return output, nil + }, +) + var SeqGrantAdminRoleOfTimelockToTimelock = cldf_ops.NewSequence( "seq-grant-admin-role-of-timelock-to-timelock", semver.MustParse("1.0.0"), diff --git a/chains/evm/deployment/v1_2_0/adapters/laneversionresolver.go b/chains/evm/deployment/v1_2_0/adapters/laneversionresolver.go index 64771e4779..c4d74becf3 100644 --- a/chains/evm/deployment/v1_2_0/adapters/laneversionresolver.go +++ b/chains/evm/deployment/v1_2_0/adapters/laneversionresolver.go @@ -1,12 +1,14 @@ package adapters import ( + "context" "fmt" "github.com/Masterminds/semver/v3" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_2_0/router" + "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" @@ -84,3 +86,24 @@ func (r *LaneVersionResolver) DeriveLaneVersionsForChain(e cldf.Environment, cha } return laneVersionForRemoteChain, versionList, nil } + +func GetLaneVersionForRemoteChain(ctx context.Context, chain evm.Chain, remoteChain uint64, routerAddr common.Address) (*semver.Version, error) { + routerC, err := router.NewRouter(routerAddr, chain.Client) + if err != nil { + return nil, fmt.Errorf("failed to bind router contract at address %s: %w", routerAddr.Hex(), err) + } + onRamp, err := routerC.GetOnRamp(&bind.CallOpts{ + Context: ctx, + }, remoteChain) + if err != nil { + return nil, fmt.Errorf("failed to get onramp for remote chain %d from router at address %s: %w", remoteChain, routerAddr.Hex(), err) + } + if onRamp == (common.Address{}) { + return nil, nil + } + _, version, err := utils.TypeAndVersion(onRamp, chain.Client) + if err != nil { + return nil, fmt.Errorf("failed to get type and version for onramp at address %s: %w", onRamp.Hex(), err) + } + return version, nil +} diff --git a/chains/evm/deployment/v1_5_0/operations/onramp/onramp.go b/chains/evm/deployment/v1_5_0/operations/onramp/onramp.go index 0fbba7ff1c..9aaf76a23e 100644 --- a/chains/evm/deployment/v1_5_0/operations/onramp/onramp.go +++ b/chains/evm/deployment/v1_5_0/operations/onramp/onramp.go @@ -18,6 +18,28 @@ var ( Version *semver.Version = semver.MustParse("1.5.0") ) +type ConstructorArgs struct { + StaticConfig evm_2_evm_onramp.EVM2EVMOnRampStaticConfig + DynamicConfig evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig + RateLimiterConfig evm_2_evm_onramp.RateLimiterConfig + FeeTokenConfigs []evm_2_evm_onramp.EVM2EVMOnRampFeeTokenConfigArgs + TokenTransferFeeConfigArgs []evm_2_evm_onramp.EVM2EVMOnRampTokenTransferFeeConfigArgs + NopsAndWeights []evm_2_evm_onramp.EVM2EVMOnRampNopAndWeight +} + +var DeployOnRamp = contract.NewDeploy(contract.DeployParams[ConstructorArgs]{ + Name: "onramp:deploy", + Version: Version, + Description: "Deploys the OnRamp 1.5.0 contract", + ContractMetadata: evm_2_evm_onramp.EVM2EVMOnRampMetaData, + Validate: func(args ConstructorArgs) error { return nil }, + BytecodeByTypeAndVersion: map[string]contract.Bytecode{ + cldf_deployment.NewTypeAndVersion(ContractType, *Version).String(): { + EVM: common.FromHex(evm_2_evm_onramp.EVM2EVMOnRampBin), + }, + }, +}) + type SetTokenTransferFeeConfigInput struct { TokenTransferFeeConfigArgs []evm_2_evm_onramp.EVM2EVMOnRampTokenTransferFeeConfigArgs TokensToUseDefaultFeeConfigs []common.Address diff --git a/chains/evm/deployment/v1_6_0/sequences/deploy_chain_contracts.go b/chains/evm/deployment/v1_6_0/sequences/deploy_chain_contracts.go index 2734f70d8d..a4f8471555 100644 --- a/chains/evm/deployment/v1_6_0/sequences/deploy_chain_contracts.go +++ b/chains/evm/deployment/v1_6_0/sequences/deploy_chain_contracts.go @@ -27,6 +27,7 @@ import ( pingpongdappops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/operations/ping_pong_dapp" "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/operations/token_admin_registry" fqops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/fee_quoter" + fq163ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_3/operations/fee_quoter" "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/nonce_manager" offrampops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/offramp" onrampops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/onramp" @@ -57,6 +58,11 @@ func (a *EVMAdapter) GrantAdminRoleToTimelock() *operations.Sequence[deployops.G return evmDeployer.GrantAdminRoleToTimelock() } +func (a *EVMAdapter) UpdateMCMSConfig() *operations.Sequence[deployops.UpdateMCMSConfigInputPerChainWithSelector, sequences.OnChainOutput, chain.BlockChains] { + evmDeployer := &evm1_0_0.EVMDeployer{} + return evmDeployer.UpdateMCMSConfig() +} + var DeployChainContracts = cldf_ops.NewSequence( "deploy-chain-contracts", semver.MustParse("1.6.0"), @@ -180,11 +186,11 @@ var DeployChainContracts = cldf_ops.NewSequence( addresses = append(addresses, nonceManagerRef) // Deploy FeeQuoter - feeQuoterRef, err := contract.MaybeDeployContract(b, fqops.Deploy, chain, contract.DeployInput[fqops.ConstructorArgs]{ - TypeAndVersion: deployment.NewTypeAndVersion(fqops.ContractType, *fqops.Version), + feeQuoterRef, err := contract.MaybeDeployContract(b, fq163ops.Deploy, chain, contract.DeployInput[fq163ops.ConstructorArgs]{ + TypeAndVersion: deployment.NewTypeAndVersion(fq163ops.ContractType, *fq163ops.Version), ChainSelector: chain.Selector, - Args: fqops.ConstructorArgs{ - StaticConfig: fqops.StaticConfig{ + Args: fq163ops.ConstructorArgs{ + StaticConfig: fq163ops.StaticConfig{ MaxFeeJuelsPerMsg: input.MaxFeeJuelsPerMsg, LinkToken: common.HexToAddress(linkRef.Address), TokenPriceStalenessThreshold: input.TokenPriceStalenessThreshold, @@ -197,9 +203,9 @@ var DeployChainContracts = cldf_ops.NewSequence( common.HexToAddress(linkRef.Address), common.HexToAddress(wethRef.Address), }, - TokenPriceFeeds: []fqops.TokenPriceFeedUpdate{}, - TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{}, - PremiumMultiplierWeiPerEthArgs: []fqops.FeeTokenArgs{ + TokenPriceFeeds: []fq163ops.TokenPriceFeedUpdate{}, + TokenTransferFeeConfigArgs: []fq163ops.TokenTransferFeeConfigArgs{}, + PremiumMultiplierWeiPerEthArgs: []fq163ops.PremiumMultiplierWeiPerEthArgs{ { PremiumMultiplierWeiPerEth: input.LinkPremiumMultiplier, Token: common.HexToAddress(linkRef.Address), @@ -209,7 +215,7 @@ var DeployChainContracts = cldf_ops.NewSequence( Token: common.HexToAddress(wethRef.Address), }, }, - DestChainConfigArgs: []fqops.DestChainConfigArgs{}, + DestChainConfigArgs: []fq163ops.DestChainConfigArgs{}, }, }, input.ExistingAddresses) if err != nil { diff --git a/chains/evm/deployment/v1_6_0/sequences/deploy_token_pool_contracts.go b/chains/evm/deployment/v1_6_0/sequences/deploy_token_pool_contracts.go index 1387d8016a..083bfee8da 100644 --- a/chains/evm/deployment/v1_6_0/sequences/deploy_token_pool_contracts.go +++ b/chains/evm/deployment/v1_6_0/sequences/deploy_token_pool_contracts.go @@ -68,10 +68,7 @@ var DeployTokenPool = cldf_ops.NewSequence( // but will error if the provided address is incorrect or not provided at all if input.TokenRef != nil && input.TokenRef.Qualifier != "" { // find token address from the data store - storedAddr, err := datastore_utils.FindAndFormatRef(input.ExistingDataStore, datastore.AddressRef{ - ChainSelector: input.ChainSelector, - Qualifier: input.TokenRef.Qualifier, - }, input.ChainSelector, datastore_utils.FullRef) + storedAddr, err := datastore_utils.FindAndFormatRef(input.ExistingDataStore, *input.TokenRef, input.ChainSelector, datastore_utils.FullRef) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("token with symbol '%s' is not found in datastore, %v", input.TokenRef.Qualifier, err) } diff --git a/chains/evm/deployment/v1_6_0/sequences/disable_remote_chain.go b/chains/evm/deployment/v1_6_0/sequences/disable_remote_chain.go new file mode 100644 index 0000000000..77e99804de --- /dev/null +++ b/chains/evm/deployment/v1_6_0/sequences/disable_remote_chain.go @@ -0,0 +1,51 @@ +package sequences + +import ( + "github.com/Masterminds/semver/v3" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_2_0/operations/router" + "github.com/smartcontractkit/chainlink-ccip/deployment/lanes" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" + cldf_chain "github.com/smartcontractkit/chainlink-deployments-framework/chain" + "github.com/smartcontractkit/chainlink-deployments-framework/operations" +) + +var DisableRemoteChainSequence = operations.NewSequence( + "DisableRemoteChain", + semver.MustParse("1.6.0"), + "Disables both sending to and receiving from a remote chain on an EVM chain via router updates", + func(b operations.Bundle, chains cldf_chain.BlockChains, input lanes.DisableRemoteChainInput) (sequences.OnChainOutput, error) { + var result sequences.OnChainOutput + b.Logger.Infof("EVM Disabling remote chain %d on chain %d", input.RemoteChainSelector, input.LocalChainSelector) + + result, err := sequences.RunAndMergeSequence(b, chains, RouterApplyRampUpdatesSequence, RouterApplyRampUpdatesSequenceInput{ + Address: common.BytesToAddress(input.Router), + ChainSelector: input.LocalChainSelector, + UpdatesByChain: router.ApplyRampsUpdatesArgs{ + OnRampUpdates: []router.OnRamp{ + { + DestChainSelector: input.RemoteChainSelector, + OnRamp: common.HexToAddress("0x0"), + }, + }, + OffRampRemoves: []router.OffRamp{ + { + SourceChainSelector: input.RemoteChainSelector, + OffRamp: common.BytesToAddress(input.OffRamp), + }, + }, + }, + }, result) + if err != nil { + return result, err + } + b.Logger.Infof("Remote chain %d disabled on EVM chain %d", input.RemoteChainSelector, input.LocalChainSelector) + + return result, nil + }, +) + +func (a *EVMAdapter) DisableRemoteChain() *operations.Sequence[lanes.DisableRemoteChainInput, sequences.OnChainOutput, cldf_chain.BlockChains] { + return DisableRemoteChainSequence +} diff --git a/chains/evm/deployment/v1_6_0/testadapter/test_adapter.go b/chains/evm/deployment/v1_6_0/testadapter/test_adapter.go index 8698c6f815..e67b5bc792 100644 --- a/chains/evm/deployment/v1_6_0/testadapter/test_adapter.go +++ b/chains/evm/deployment/v1_6_0/testadapter/test_adapter.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "github.com/xssnick/tonutils-go/tlb" chain_selectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" @@ -24,6 +25,8 @@ import ( "github.com/smartcontractkit/chainlink-deployments-framework/datastore" "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + ton_onramp "github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/onramp" + bnmERC20ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_0_0/operations/burn_mint_erc20" "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_2_0/router" "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_6_0/nonce_manager" @@ -222,6 +225,10 @@ func (a *EVMAdapter) CCIPReceiver() []byte { return common.LeftPadBytes(common.HexToAddress("0xdead").Bytes(), 32) } +func (a *EVMAdapter) SetReceiverRejectAll(ctx context.Context, rejectAll bool) error { + return errors.ErrUnsupported +} + func (a *EVMAdapter) NativeFeeToken() string { return "0x0" } @@ -229,13 +236,45 @@ func (a *EVMAdapter) NativeFeeToken() string { func (a *EVMAdapter) GetExtraArgs(receiver []byte, sourceFamily string, opts ...testadapters.ExtraArgOpt) ([]byte, error) { switch sourceFamily { case chain_selectors.FamilyEVM: - return ccipcommon.SerializeClientGenericExtraArgsV2(msg_hasher163.ClientGenericExtraArgsV2{ + extraArgs := msg_hasher163.ClientGenericExtraArgsV2{ GasLimit: new(big.Int).SetUint64(100_000), AllowOutOfOrderExecution: true, - }) + } + for _, opt := range opts { + switch opt.Name { + case testadapters.ExtraArgGasLimit: + extraArgs.GasLimit = opt.Value.(*big.Int) + case testadapters.ExtraArgOOO: + extraArgs.AllowOutOfOrderExecution = opt.Value.(bool) + default: + // unsupported arg + } + } + return ccipcommon.SerializeClientGenericExtraArgsV2(extraArgs) case chain_selectors.FamilySolana: // EVM allows empty extraArgs return nil, nil + case chain_selectors.FamilyTon: + // TODO: maybe for 1.6 we should look up the source adapter and use a 1.6 method to encode? would be good to avoid other chain SDKs + extraArgs := ton_onramp.GenericExtraArgsV2{ + GasLimit: big.NewInt(1000000), + AllowOutOfOrderExecution: true, + } + for _, opt := range opts { + switch opt.Name { + case testadapters.ExtraArgGasLimit: + extraArgs.GasLimit = opt.Value.(*big.Int) + case testadapters.ExtraArgOOO: + extraArgs.AllowOutOfOrderExecution = opt.Value.(bool) + default: + // unsupported arg + } + } + extraArgsCell, err := tlb.ToCell(extraArgs) + if err != nil { + return nil, err + } + return extraArgsCell.ToBOC(), nil default: // TODO: add support for other families return nil, fmt.Errorf("unsupported source family: %s", sourceFamily) diff --git a/chains/evm/deployment/v2_0_0/adapters/feequoterupdater.go b/chains/evm/deployment/v2_0_0/adapters/feequoterupdater.go new file mode 100644 index 0000000000..edc617b6c2 --- /dev/null +++ b/chains/evm/deployment/v2_0_0/adapters/feequoterupdater.go @@ -0,0 +1,97 @@ +package adapters + +import ( + "fmt" + + "github.com/Masterminds/semver/v3" + "github.com/ethereum/go-ethereum/common" + cldf_chain "github.com/smartcontractkit/chainlink-deployments-framework/chain" + cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" + + fqseq "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/sequences" +) + +// FeeQuoterUpdater uses FeeQUpdateArgs any so it implements deploy.FeeQuoterUpdater[any] and can be registered directly. +// The implementation is for v2.0.0 and uses fqseq.FeeQuoterUpdate internally. +type FeeQuoterUpdater[FeeQUpdateArgs any] struct{} + +func (fqu FeeQuoterUpdater[FeeQUpdateArgs]) SequenceFeeQuoterInputCreation() *cldf_ops.Sequence[deploy.FeeQuoterUpdateInput, FeeQUpdateArgs, cldf_chain.BlockChains] { + return cldf_ops.NewSequence( + "fee-quoter-updater:input-creation", + semver.MustParse("2.0.0"), + "Creates FeeQuoterUpdateInput for FeeQuoter update sequence", + func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, input deploy.FeeQuoterUpdateInput) (output FeeQUpdateArgs, err error) { + var zero FeeQUpdateArgs + chain, ok := chains.EVMChains()[input.ChainSelector] + if !ok { + return zero, fmt.Errorf("chain with selector %d not found in environment", input.ChainSelector) + } + var output16, output15 fqseq.FeeQuoterUpdate + for _, version := range input.PreviousVersions { + switch version.String() { + case "1.6.0": + report, err := cldf_ops.ExecuteSequence(b, fqseq.CreateFeeQuoterUpdateInputFromV16x, chain, input) + if err != nil { + return zero, fmt.Errorf("failed to create FeeQuoterUpdateInput from v1.6.0: %w", err) + } + output16 = report.Output + case "1.5.0": + report15, err := cldf_ops.ExecuteSequence(b, fqseq.CreateFeeQuoterUpdateInputFromV150, chain, input) + if err != nil { + return zero, fmt.Errorf("failed to create FeeQuoterUpdateInput from v1.5.0: %w", err) + } + output15 = report15.Output + } + } + // combine the outputs from both sequences to create the input for the fee quoter update sequence + out, err := fqseq.MergeFeeQuoterUpdateOutputs(output16, output15) + if err != nil { + return zero, fmt.Errorf("failed to merge FeeQuoterUpdateInput from v1.6.0 and v1.5.0: %w", err) + } + // check if output is empty, if so, return an error + empty, err := out.IsEmpty() + if err != nil { + return zero, fmt.Errorf("could not create input for fee quoter 2.0.0 update sequence: %w", err) + } + if empty { + return zero, fmt.Errorf("could not create input for fee quoter 2.0.0 update sequence: output is empty") + } + + out.ChainSelector = input.ChainSelector + out.ExistingAddresses = input.ExistingAddresses + + if input.TimelockAddress != "" { + timelockAddr := common.HexToAddress(input.TimelockAddress) + if !fqseq.IsConstructorArgsEmpty(out.ConstructorArgs) { + out.ConstructorArgs.PriceUpdaters = append(out.ConstructorArgs.PriceUpdaters, timelockAddr) + } else { + out.AuthorizedCallerUpdates.AddedCallers = append(out.AuthorizedCallerUpdates.AddedCallers, timelockAddr) + } + } + + return any(out).(FeeQUpdateArgs), nil + }, + ) +} + +func (fqu FeeQuoterUpdater[FeeQUpdateArgs]) SequenceDeployOrUpdateFeeQuoter() *cldf_ops.Sequence[FeeQUpdateArgs, sequences.OnChainOutput, cldf_chain.BlockChains] { + return cldf_ops.NewSequence( + "fee-quoter-v2.0.0:update-sequence", + semver.MustParse("2.0.0"), + "Deploys or fetches existing FeeQuoter contract and applies config updates", + func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, input FeeQUpdateArgs) (output sequences.OnChainOutput, err error) { + fqInput, ok := any(input).(fqseq.FeeQuoterUpdate) + if !ok { + return sequences.OnChainOutput{}, fmt.Errorf("expected fqseq.FeeQuoterUpdate, got %T", input) + } + report, err := cldf_ops.ExecuteSequence(b, fqseq.SequenceFeeQuoterUpdate, chains, fqInput) + if err != nil { + return sequences.OnChainOutput{}, err + } + return report.Output, nil + }, + ) +} diff --git a/chains/evm/deployment/v2_0_0/adapters/init.go b/chains/evm/deployment/v2_0_0/adapters/init.go new file mode 100644 index 0000000000..8d0620db33 --- /dev/null +++ b/chains/evm/deployment/v2_0_0/adapters/init.go @@ -0,0 +1,20 @@ +package adapters + +import ( + "github.com/Masterminds/semver/v3" + + chainsel "github.com/smartcontractkit/chain-selectors" + adapters1_2 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_2_0/adapters" + adapters1_5 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/adapters" + adapters1_6 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/adapters" + "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" +) + +func init() { + fqReg := deploy.GetFQAndRampUpdaterRegistry() + fqReg.RegisterFeeQuoterUpdater(chainsel.FamilyEVM, semver.MustParse("2.0.0"), FeeQuoterUpdater[any]{}) + fqReg.RegisterRampUpdater(chainsel.FamilyEVM, semver.MustParse("1.6.0"), adapters1_6.RampUpdateWithFQ{}) + fqReg.RegisterConfigImporter(chainsel.FamilyEVM, semver.MustParse("1.6.0"), &adapters1_6.ConfigImportAdapter{}) + fqReg.RegisterConfigImporter(chainsel.FamilyEVM, semver.MustParse("1.5.0"), &adapters1_5.ConfigImportAdapter{}) + fqReg.RegisterConfigImporterVersionResolver(chainsel.FamilyEVM, &adapters1_2.LaneVersionResolver{}) +} diff --git a/chains/evm/deployment/v2_0_0/operations/fee_quoter/fee_quoter.go b/chains/evm/deployment/v2_0_0/operations/fee_quoter/fee_quoter.go new file mode 100644 index 0000000000..a0857a1097 --- /dev/null +++ b/chains/evm/deployment/v2_0_0/operations/fee_quoter/fee_quoter.go @@ -0,0 +1,359 @@ +// Code generated by operations-gen. DO NOT EDIT. + +package fee_quoter + +import ( + "math/big" + + "strings" + + "github.com/Masterminds/semver/v3" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + cldf_deployment "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + + "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/utils/operations/contract" +) + +var ContractType cldf_deployment.ContractType = "FeeQuoter" +var Version = semver.MustParse("2.0.0") +var TypeAndVersion = cldf_deployment.NewTypeAndVersion(ContractType, *Version) + +const FeeQuoterABI = `[{"type":"constructor","inputs":[{"name":"staticConfig","type":"tuple","internalType":"struct FeeQuoter.StaticConfig","components":[{"name":"maxFeeJuelsPerMsg","type":"uint96","internalType":"uint96"},{"name":"linkToken","type":"address","internalType":"address"}]},{"name":"priceUpdaters","type":"address[]","internalType":"address[]"},{"name":"tokenTransferFeeConfigArgs","type":"tuple[]","internalType":"struct FeeQuoter.TokenTransferFeeConfigArgs[]","components":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"tokenTransferFeeConfigs","type":"tuple[]","internalType":"struct FeeQuoter.TokenTransferFeeConfigSingleTokenArgs[]","components":[{"name":"token","type":"address","internalType":"address"},{"name":"tokenTransferFeeConfig","type":"tuple","internalType":"struct FeeQuoter.TokenTransferFeeConfig","components":[{"name":"feeUSDCents","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destBytesOverhead","type":"uint32","internalType":"uint32"},{"name":"isEnabled","type":"bool","internalType":"bool"}]}]}]},{"name":"destChainConfigArgs","type":"tuple[]","internalType":"struct FeeQuoter.DestChainConfigArgs[]","components":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"destChainConfig","type":"tuple","internalType":"struct FeeQuoter.DestChainConfig","components":[{"name":"isEnabled","type":"bool","internalType":"bool"},{"name":"maxDataBytes","type":"uint32","internalType":"uint32"},{"name":"maxPerMsgGasLimit","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destGasPerPayloadByteBase","type":"uint8","internalType":"uint8"},{"name":"chainFamilySelector","type":"bytes4","internalType":"bytes4"},{"name":"defaultTokenFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"defaultTokenDestGasOverhead","type":"uint32","internalType":"uint32"},{"name":"defaultTxGasLimit","type":"uint32","internalType":"uint32"},{"name":"networkFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"linkFeeMultiplierPercent","type":"uint8","internalType":"uint8"}]}]}],"stateMutability":"nonpayable"},{"type":"function","name":"acceptOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"applyAuthorizedCallerUpdates","inputs":[{"name":"authorizedCallerArgs","type":"tuple","internalType":"struct AuthorizedCallers.AuthorizedCallerArgs","components":[{"name":"addedCallers","type":"address[]","internalType":"address[]"},{"name":"removedCallers","type":"address[]","internalType":"address[]"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"applyDestChainConfigUpdates","inputs":[{"name":"destChainConfigArgs","type":"tuple[]","internalType":"struct FeeQuoter.DestChainConfigArgs[]","components":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"destChainConfig","type":"tuple","internalType":"struct FeeQuoter.DestChainConfig","components":[{"name":"isEnabled","type":"bool","internalType":"bool"},{"name":"maxDataBytes","type":"uint32","internalType":"uint32"},{"name":"maxPerMsgGasLimit","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destGasPerPayloadByteBase","type":"uint8","internalType":"uint8"},{"name":"chainFamilySelector","type":"bytes4","internalType":"bytes4"},{"name":"defaultTokenFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"defaultTokenDestGasOverhead","type":"uint32","internalType":"uint32"},{"name":"defaultTxGasLimit","type":"uint32","internalType":"uint32"},{"name":"networkFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"linkFeeMultiplierPercent","type":"uint8","internalType":"uint8"}]}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"applyTokenTransferFeeConfigUpdates","inputs":[{"name":"tokenTransferFeeConfigArgs","type":"tuple[]","internalType":"struct FeeQuoter.TokenTransferFeeConfigArgs[]","components":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"tokenTransferFeeConfigs","type":"tuple[]","internalType":"struct FeeQuoter.TokenTransferFeeConfigSingleTokenArgs[]","components":[{"name":"token","type":"address","internalType":"address"},{"name":"tokenTransferFeeConfig","type":"tuple","internalType":"struct FeeQuoter.TokenTransferFeeConfig","components":[{"name":"feeUSDCents","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destBytesOverhead","type":"uint32","internalType":"uint32"},{"name":"isEnabled","type":"bool","internalType":"bool"}]}]}]},{"name":"tokensToUseDefaultFeeConfigs","type":"tuple[]","internalType":"struct FeeQuoter.TokenTransferFeeConfigRemoveArgs[]","components":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"token","type":"address","internalType":"address"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"convertTokenAmount","inputs":[{"name":"fromToken","type":"address","internalType":"address"},{"name":"fromTokenAmount","type":"uint256","internalType":"uint256"},{"name":"toToken","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getAllAuthorizedCallers","inputs":[],"outputs":[{"name":"","type":"address[]","internalType":"address[]"}],"stateMutability":"view"},{"type":"function","name":"getAllDestChainConfigs","inputs":[],"outputs":[{"name":"","type":"uint64[]","internalType":"uint64[]"},{"name":"","type":"tuple[]","internalType":"struct FeeQuoter.DestChainConfig[]","components":[{"name":"isEnabled","type":"bool","internalType":"bool"},{"name":"maxDataBytes","type":"uint32","internalType":"uint32"},{"name":"maxPerMsgGasLimit","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destGasPerPayloadByteBase","type":"uint8","internalType":"uint8"},{"name":"chainFamilySelector","type":"bytes4","internalType":"bytes4"},{"name":"defaultTokenFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"defaultTokenDestGasOverhead","type":"uint32","internalType":"uint32"},{"name":"defaultTxGasLimit","type":"uint32","internalType":"uint32"},{"name":"networkFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"linkFeeMultiplierPercent","type":"uint8","internalType":"uint8"}]}],"stateMutability":"view"},{"type":"function","name":"getAllTokenTransferFeeConfigs","inputs":[],"outputs":[{"name":"destChainSelectors","type":"uint64[]","internalType":"uint64[]"},{"name":"transferTokens","type":"address[][]","internalType":"address[][]"},{"name":"tokenTransferFeeConfigs","type":"tuple[][]","internalType":"struct FeeQuoter.TokenTransferFeeConfig[][]","components":[{"name":"feeUSDCents","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destBytesOverhead","type":"uint32","internalType":"uint32"},{"name":"isEnabled","type":"bool","internalType":"bool"}]}],"stateMutability":"view"},{"type":"function","name":"getDestChainConfig","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"","type":"tuple","internalType":"struct FeeQuoter.DestChainConfig","components":[{"name":"isEnabled","type":"bool","internalType":"bool"},{"name":"maxDataBytes","type":"uint32","internalType":"uint32"},{"name":"maxPerMsgGasLimit","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destGasPerPayloadByteBase","type":"uint8","internalType":"uint8"},{"name":"chainFamilySelector","type":"bytes4","internalType":"bytes4"},{"name":"defaultTokenFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"defaultTokenDestGasOverhead","type":"uint32","internalType":"uint32"},{"name":"defaultTxGasLimit","type":"uint32","internalType":"uint32"},{"name":"networkFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"linkFeeMultiplierPercent","type":"uint8","internalType":"uint8"}]}],"stateMutability":"view"},{"type":"function","name":"getDestinationChainGasPrice","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"","type":"tuple","internalType":"struct Internal.TimestampedPackedUint224","components":[{"name":"value","type":"uint224","internalType":"uint224"},{"name":"timestamp","type":"uint32","internalType":"uint32"}]}],"stateMutability":"view"},{"type":"function","name":"getFeeTokens","inputs":[],"outputs":[{"name":"","type":"address[]","internalType":"address[]"}],"stateMutability":"view"},{"type":"function","name":"getStaticConfig","inputs":[],"outputs":[{"name":"","type":"tuple","internalType":"struct FeeQuoter.StaticConfig","components":[{"name":"maxFeeJuelsPerMsg","type":"uint96","internalType":"uint96"},{"name":"linkToken","type":"address","internalType":"address"}]}],"stateMutability":"view"},{"type":"function","name":"getTokenAndGasPrices","inputs":[{"name":"token","type":"address","internalType":"address"},{"name":"destChainSelector","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"tokenPrice","type":"uint224","internalType":"uint224"},{"name":"gasPriceValue","type":"uint224","internalType":"uint224"}],"stateMutability":"view"},{"type":"function","name":"getTokenPrice","inputs":[{"name":"token","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"tuple","internalType":"struct Internal.TimestampedPackedUint224","components":[{"name":"value","type":"uint224","internalType":"uint224"},{"name":"timestamp","type":"uint32","internalType":"uint32"}]}],"stateMutability":"view"},{"type":"function","name":"getTokenPrices","inputs":[{"name":"tokens","type":"address[]","internalType":"address[]"}],"outputs":[{"name":"","type":"tuple[]","internalType":"struct Internal.TimestampedPackedUint224[]","components":[{"name":"value","type":"uint224","internalType":"uint224"},{"name":"timestamp","type":"uint32","internalType":"uint32"}]}],"stateMutability":"view"},{"type":"function","name":"getTokenTransferFee","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"token","type":"address","internalType":"address"}],"outputs":[{"name":"feeUSDCents","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destBytesOverhead","type":"uint32","internalType":"uint32"}],"stateMutability":"view"},{"type":"function","name":"getTokenTransferFeeConfig","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"token","type":"address","internalType":"address"}],"outputs":[{"name":"tokenTransferFeeConfig","type":"tuple","internalType":"struct FeeQuoter.TokenTransferFeeConfig","components":[{"name":"feeUSDCents","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destBytesOverhead","type":"uint32","internalType":"uint32"},{"name":"isEnabled","type":"bool","internalType":"bool"}]}],"stateMutability":"view"},{"type":"function","name":"getValidatedFee","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"message","type":"tuple","internalType":"struct Client.EVM2AnyMessage","components":[{"name":"receiver","type":"bytes","internalType":"bytes"},{"name":"data","type":"bytes","internalType":"bytes"},{"name":"tokenAmounts","type":"tuple[]","internalType":"struct Client.EVMTokenAmount[]","components":[{"name":"token","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}]},{"name":"feeToken","type":"address","internalType":"address"},{"name":"extraArgs","type":"bytes","internalType":"bytes"}]}],"outputs":[{"name":"feeTokenAmount","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getValidatedTokenPrice","inputs":[{"name":"token","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint224","internalType":"uint224"}],"stateMutability":"view"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"processMessageArgs","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"feeToken","type":"address","internalType":"address"},{"name":"feeTokenAmount","type":"uint256","internalType":"uint256"},{"name":"extraArgs","type":"bytes","internalType":"bytes"},{"name":"messageReceiver","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"msgFeeJuels","type":"uint256","internalType":"uint256"},{"name":"isOutOfOrderExecution","type":"bool","internalType":"bool"},{"name":"convertedExtraArgs","type":"bytes","internalType":"bytes"},{"name":"tokenReceiver","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"processPoolReturnData","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"onRampTokenTransfers","type":"tuple[]","internalType":"struct Internal.EVM2AnyTokenTransfer[]","components":[{"name":"sourcePoolAddress","type":"address","internalType":"address"},{"name":"destTokenAddress","type":"bytes","internalType":"bytes"},{"name":"extraData","type":"bytes","internalType":"bytes"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"destExecData","type":"bytes","internalType":"bytes"}]},{"name":"sourceTokenAmounts","type":"tuple[]","internalType":"struct Client.EVMTokenAmount[]","components":[{"name":"token","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}]}],"outputs":[{"name":"destExecDataPerToken","type":"bytes[]","internalType":"bytes[]"}],"stateMutability":"view"},{"type":"function","name":"quoteGasForExec","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"nonCalldataGas","type":"uint32","internalType":"uint32"},{"name":"calldataSize","type":"uint32","internalType":"uint32"},{"name":"feeToken","type":"address","internalType":"address"}],"outputs":[{"name":"totalGas","type":"uint32","internalType":"uint32"},{"name":"gasCostInUsdCents","type":"uint256","internalType":"uint256"},{"name":"feeTokenPrice","type":"uint256","internalType":"uint256"},{"name":"premiumPercentMultiplier","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"removeFeeTokens","inputs":[{"name":"feeTokensToRemove","type":"address[]","internalType":"address[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"resolveLegacyArgs","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"extraArgs","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"tokenReceiver","type":"bytes","internalType":"bytes"},{"name":"gasLimit","type":"uint32","internalType":"uint32"},{"name":"executorArgs","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"transferOwnership","inputs":[{"name":"to","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"typeAndVersion","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"updatePrices","inputs":[{"name":"priceUpdates","type":"tuple","internalType":"struct Internal.PriceUpdates","components":[{"name":"tokenPriceUpdates","type":"tuple[]","internalType":"struct Internal.TokenPriceUpdate[]","components":[{"name":"sourceToken","type":"address","internalType":"address"},{"name":"usdPerToken","type":"uint224","internalType":"uint224"}]},{"name":"gasPriceUpdates","type":"tuple[]","internalType":"struct Internal.GasPriceUpdate[]","components":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"usdPerUnitGas","type":"uint224","internalType":"uint224"}]}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"AuthorizedCallerAdded","inputs":[{"name":"caller","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"event","name":"AuthorizedCallerRemoved","inputs":[{"name":"caller","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"event","name":"DestChainAdded","inputs":[{"name":"destChainSelector","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"destChainConfig","type":"tuple","indexed":false,"internalType":"struct FeeQuoter.DestChainConfig","components":[{"name":"isEnabled","type":"bool","internalType":"bool"},{"name":"maxDataBytes","type":"uint32","internalType":"uint32"},{"name":"maxPerMsgGasLimit","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destGasPerPayloadByteBase","type":"uint8","internalType":"uint8"},{"name":"chainFamilySelector","type":"bytes4","internalType":"bytes4"},{"name":"defaultTokenFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"defaultTokenDestGasOverhead","type":"uint32","internalType":"uint32"},{"name":"defaultTxGasLimit","type":"uint32","internalType":"uint32"},{"name":"networkFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"linkFeeMultiplierPercent","type":"uint8","internalType":"uint8"}]}],"anonymous":false},{"type":"event","name":"DestChainConfigUpdated","inputs":[{"name":"destChainSelector","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"destChainConfig","type":"tuple","indexed":false,"internalType":"struct FeeQuoter.DestChainConfig","components":[{"name":"isEnabled","type":"bool","internalType":"bool"},{"name":"maxDataBytes","type":"uint32","internalType":"uint32"},{"name":"maxPerMsgGasLimit","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destGasPerPayloadByteBase","type":"uint8","internalType":"uint8"},{"name":"chainFamilySelector","type":"bytes4","internalType":"bytes4"},{"name":"defaultTokenFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"defaultTokenDestGasOverhead","type":"uint32","internalType":"uint32"},{"name":"defaultTxGasLimit","type":"uint32","internalType":"uint32"},{"name":"networkFeeUSDCents","type":"uint16","internalType":"uint16"},{"name":"linkFeeMultiplierPercent","type":"uint8","internalType":"uint8"}]}],"anonymous":false},{"type":"event","name":"FeeTokenAdded","inputs":[{"name":"feeToken","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"FeeTokenRemoved","inputs":[{"name":"feeToken","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"OwnershipTransferRequested","inputs":[{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"TokenTransferFeeConfigDeleted","inputs":[{"name":"destChainSelector","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"token","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"TokenTransferFeeConfigUpdated","inputs":[{"name":"destChainSelector","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"token","type":"address","indexed":true,"internalType":"address"},{"name":"tokenTransferFeeConfig","type":"tuple","indexed":false,"internalType":"struct FeeQuoter.TokenTransferFeeConfig","components":[{"name":"feeUSDCents","type":"uint32","internalType":"uint32"},{"name":"destGasOverhead","type":"uint32","internalType":"uint32"},{"name":"destBytesOverhead","type":"uint32","internalType":"uint32"},{"name":"isEnabled","type":"bool","internalType":"bool"}]}],"anonymous":false},{"type":"event","name":"UsdPerTokenUpdated","inputs":[{"name":"token","type":"address","indexed":true,"internalType":"address"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"timestamp","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"UsdPerUnitGasUpdated","inputs":[{"name":"destChain","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"timestamp","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"error","name":"CannotTransferToSelf","inputs":[]},{"type":"error","name":"DestinationChainNotEnabled","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"FeeTokenNotSupported","inputs":[{"name":"token","type":"address","internalType":"address"}]},{"type":"error","name":"Invalid32ByteAddress","inputs":[{"name":"encodedAddress","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"InvalidChainFamilySelector","inputs":[{"name":"chainFamilySelector","type":"bytes4","internalType":"bytes4"}]},{"type":"error","name":"InvalidDataLength","inputs":[{"name":"location","type":"uint8","internalType":"enum ExtraArgsCodec.EncodingErrorLocation"},{"name":"offset","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"InvalidDestBytesOverhead","inputs":[{"name":"token","type":"address","internalType":"address"},{"name":"destBytesOverhead","type":"uint32","internalType":"uint32"}]},{"type":"error","name":"InvalidDestChainConfig","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"InvalidEVMAddress","inputs":[{"name":"encodedAddress","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"InvalidExtraArgsData","inputs":[]},{"type":"error","name":"InvalidExtraArgsTag","inputs":[]},{"type":"error","name":"InvalidSVMExtraArgsWritableBitmap","inputs":[{"name":"accountIsWritableBitmap","type":"uint64","internalType":"uint64"},{"name":"numAccounts","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"InvalidStaticConfig","inputs":[]},{"type":"error","name":"InvalidTVMAddress","inputs":[{"name":"encodedAddress","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"InvalidTokenReceiver","inputs":[]},{"type":"error","name":"MessageComputeUnitLimitTooHigh","inputs":[]},{"type":"error","name":"MessageFeeTooHigh","inputs":[{"name":"msgFeeJuels","type":"uint256","internalType":"uint256"},{"name":"maxFeeJuelsPerMsg","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"MessageGasLimitTooHigh","inputs":[]},{"type":"error","name":"MessageTooLarge","inputs":[{"name":"maxSize","type":"uint256","internalType":"uint256"},{"name":"actualSize","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"MustBeProposedOwner","inputs":[]},{"type":"error","name":"NoGasPriceAvailable","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"OnlyCallableByOwner","inputs":[]},{"type":"error","name":"OwnerCannotBeZero","inputs":[]},{"type":"error","name":"SourceTokenDataTooLarge","inputs":[{"name":"token","type":"address","internalType":"address"}]},{"type":"error","name":"TokenNotSupported","inputs":[{"name":"token","type":"address","internalType":"address"}]},{"type":"error","name":"TokenTransferConfigMustBeEnabled","inputs":[{"name":"destChainSelector","type":"uint64","internalType":"uint64"},{"name":"token","type":"address","internalType":"address"}]},{"type":"error","name":"TooManySVMExtraArgsAccounts","inputs":[{"name":"numAccounts","type":"uint256","internalType":"uint256"},{"name":"maxAccounts","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TooManySuiExtraArgsReceiverObjectIds","inputs":[{"name":"numReceiverObjectIds","type":"uint256","internalType":"uint256"},{"name":"maxReceiverObjectIds","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"UnauthorizedCaller","inputs":[{"name":"caller","type":"address","internalType":"address"}]},{"type":"error","name":"UnsupportedNumberOfTokens","inputs":[{"name":"numberOfTokens","type":"uint256","internalType":"uint256"},{"name":"maxNumberOfTokensPerMsg","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ZeroAddressNotAllowed","inputs":[]}]` +const FeeQuoterBin = "0x60c060405234610aca57616e508038038061001981610c5c565b92833981019080820360a08112610aca57604013610aca57610039610c3d565b81516001600160601b0381168103610aca57815261005960208301610c81565b6020820190815260408301519093906001600160401b038111610aca5783019381601f86011215610aca5784519461009861009387610c95565b610c5c565b9560208088838152019160051b83010191848311610aca57602001905b828210610c255750505060608401516001600160401b038111610aca5784019382601f86011215610aca578451946100ef61009387610c95565b9560208088838152019160051b83010191858311610aca5760208101915b838310610ae557505050506080810151906001600160401b038211610aca570182601f82011215610aca5780519061014761009383610c95565b936020610180818786815201940283010191818311610aca57602001925b8284106109a05750505050331561098f57600180546001600160a01b0319163317905560209261019484610c5c565b926000845260003681376101a6610c3d565b968752838588015260005b8451811015610218576001906001600160a01b036101cf8288610cfb565b5116876101db82610f2a565b6101e8575b5050016101b1565b7fc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda7758091604051908152a138876101e0565b508493508587519260005b8451811015610294576001600160a01b0361023e8287610cfb565b5116908115610283577feb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef8883610275600195610d7f565b50604051908152a101610223565b6342bcdf7f60e11b60005260046000fd5b50845186919086906001600160a01b031615801561097d575b61096c57516001600160a01b031660a052516001600160601b031660805260005b83518110156106ae576102e18185610cfb565b51826001600160401b036102f58488610cfb565b5151169101518115801561069b575b801561067d575b801561066d575b8015610657575b610642578161055c9160019493600052600a865263ffffffff60e01b60406000205460701b161560001461056357817f4efe320c85221c7c3684c54561bea5a9c4dcfad794c6ef9ff9e6b43fb307c0f86040518061041e858291909161014060ff8161016084019580511515855263ffffffff602082015116602086015263ffffffff604082015116604086015263ffffffff606082015116606086015282608082015116608086015263ffffffff60e01b60a08201511660a086015261ffff60c08201511660c086015263ffffffff60e08201511660e086015263ffffffff6101008201511661010086015261ffff61012082015116610120860152015116910152565b0390a25b6000828152600a875260409081902082518154848a0151938501516060860151608087015160a08089015160c0808b015160e0808d01516101008e01516101208f0151610140909f01517fff00000000000000000000000000000000000000000000000000000000000000909b1660ff9c15159c909c169b909b1760089d909d1b64ffffffff00169c909c1760289890981b68ffffffff0000000000169790971760489690961b6cffffffff000000000000000000169590951760689490941b6dff00000000000000000000000000169390931760709190911c63ffffffff60701b161760909390931b61ffff60901b16929092179690911b63ffffffff60a01b16959095179290941b63ffffffff60c01b16919091179390921b61ffff60e01b169290921760f09190911b60ff60f01b16179055610dbe565b50016102ce565b817f0c6380a4766d45f5d53ca170bf865bebfab44958dec379d5a90177264e6645b76040518061063a858291909161014060ff8161016084019580511515855263ffffffff602082015116602086015263ffffffff604082015116604086015263ffffffff606082015116606086015282608082015116608086015263ffffffff60e01b60a08201511660a086015261ffff60c08201511660c086015263ffffffff60e08201511660e086015263ffffffff6101008201511661010086015261ffff61012082015116610120860152015116910152565b0390a2610422565b5063c35aa79d60e01b60005260045260246000fd5b5060a08101516001600160e01b03191615610319565b5060ff6101408201511615610312565b5063ffffffff6101008201511663ffffffff6040830151161061030b565b5063ffffffff6101008201511615610304565b5060016106ba82610c5c565b9260008452600091610967575b81925b8151841015610892576106dd8483610cfb565b5180516001600160401b031692831561087e578285979201965b8751805182101561086e5761070d828692610cfb565b51015188516001600160a01b0390610726908490610cfb565b5151169060608101805115610857576040820163ffffffff8151168881106108405750608084938a9363ffffffff7f5c55501634b3b87e45686082d77f017b6639b436c21cb423ba6313d843f66ed194818f61081a8f9b60408360019f9e600c908e8067ffffffff000000009852600b825284842060018060a01b038716855282528484208d8d8c8c835116926cff0000000000000000000000006bffffffff000000000000000088875493019e8f518a1b1693518c1b169351151560601b16936cff000000000000000000000000199160018060601b0319161716171717905561081081610dbe565b5083525220610df7565b5081604051965116865251168d850152511660408301525115156060820152a3016106f7565b6312766e0160e11b8b52600485905260245260448afd5b604489848a632ce1527960e21b8352600452602452fd5b50509250945092600101926106ca565b63c35aa79d60e01b85526004849052602485fd5b849150825b825181101561092a576001906001600160401b036108b58286610cfb565b515116828060a01b03846108c98488610cfb565b5101511690808752600b855260408720848060a01b03831688528552866040812055808752600c85526108ff8260408920610e74565b507f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b8780a301610897565b604051615e919081610fbf823960805181818161045c01526110a0015260a0518181816104840152818161103701528181611dd001526124bb0152f35b6106c7565b63d794ef9560e01b60005260046000fd5b5081516001600160601b0316156102ad565b639b15e16f60e01b60005260046000fd5b8382036101808112610aca576101606109b7610c3d565b916109c187610cac565b8352601f190112610aca576040519161016083016001600160401b03811184821017610acf576040526109f660208701610cd1565b8352610a0460408701610cc0565b6020840152610a1560608701610cc0565b6040840152610a2660808701610cc0565b6060840152610a3760a08701610cde565b608084015260c0860151916001600160e01b031983168303610aca578360209360a0610180960152610a6b60e08901610cec565b60c0820152610a7d6101008901610cc0565b60e0820152610a8f6101208901610cc0565b610100820152610aa26101408901610cec565b610120820152610ab56101608901610cde565b61014082015283820152815201930192610165565b600080fd5b634e487b7160e01b600052604160045260246000fd5b82516001600160401b038111610aca5782016040818903601f190112610aca57610b0d610c3d565b90610b1a60208201610cac565b825260408101516001600160401b038111610aca57602091010188601f82011215610aca578051610b4d61009382610c95565b91602060a08185858152019302820101908b8211610aca57602001915b818310610b89575050509181602093848094015281520192019161010d565b828c0360a08112610aca576080610b9e610c3d565b91610ba886610c81565b8352601f190112610aca576040519160808301916001600160401b03831184841017610acf5760a093602093604052610be2848801610cc0565b8152610bf060408801610cc0565b84820152610c0060608801610cc0565b6040820152610c1160808801610cd1565b606082015283820152815201920191610b6a565b60208091610c3284610c81565b8152019101906100b5565b60408051919082016001600160401b03811183821017610acf57604052565b6040519190601f01601f191682016001600160401b03811183821017610acf57604052565b51906001600160a01b0382168203610aca57565b6001600160401b038111610acf5760051b60200190565b51906001600160401b0382168203610aca57565b519063ffffffff82168203610aca57565b51908115158203610aca57565b519060ff82168203610aca57565b519061ffff82168203610aca57565b8051821015610d0f5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b8054821015610d0f5760005260206000200190600090565b80549068010000000000000000821015610acf5781610d64916001610d7b94018155610d25565b819391549060031b91821b91600019901b19161790565b9055565b80600052600360205260406000205415600014610db857610da1816002610d3d565b600254906000526003602052604060002055600190565b50600090565b80600052600960205260406000205415600014610db857610de0816008610d3d565b600854906000526009602052604060002055600190565b6000828152600182016020526040902054610e2e5780610e1983600193610d3d565b80549260005201602052604060002055600190565b5050600090565b80548015610e5e576000190190610e4c8282610d25565b8154906000199060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b906001820191816000528260205260406000205490811515600014610f2157600019820191808311610f0b5781546000198101908111610f0b578381610ec29503610ed4575b505050610e35565b60005260205260006040812055600190565b610ef4610ee4610d649386610d25565b90549060031b1c92839286610d25565b905560005284602052604060002055388080610eba565b634e487b7160e01b600052601160045260246000fd5b50505050600090565b6000818152600360205260409020548015610e2e576000198101818111610f0b57600254600019810191908211610f0b57808203610f84575b505050610f706002610e35565b600052600360205260006040812055600190565b610fa6610f95610d64936002610d25565b90549060031b1c9283926002610d25565b90556000526003602052604060002055388080610f6356fe6080604052600436101561001257600080fd5b60003560e01c806241e5be146101d657806301447eaa146101d157806306285c69146101cc578063080d711a146101c757806315c34d5b146101c2578063181f5a77146101bd5780632451a627146101b85780633937306f146101b35780633a49bb49146101ae5780633f37a52c146101a957806345ac924d146101a45780634ab35b0b1461019f578063514e8cff1461019a5780636def4ce71461019557806379ba50971461019057806382b49eb01461018b57806389933a51146101865780638da5cb5b14610181578063910d8f591461017c57806391a2749a1461017757806391b2f60714610172578063947f82171461016d5780639cc1999614610168578063cdc73d5114610163578063d02641a01461015e578063d8694ccd14610159578063f2fde38b146101545763ffdb4b371461014f57600080fd5b6127dd565b612707565b61236e565b612312565b61229b565b612221565b6121d2565b61204e565b611f70565b611c27565b611bf3565b611b06565b6119f3565b6118e5565b6117c0565b611638565b6115eb565b611522565b6112a8565b610fc5565b610b9a565b610b17565b610a5a565b61082c565b61063a565b61040f565b610370565b6101fe565b73ffffffffffffffffffffffffffffffffffffffff8116036101f957565b600080fd5b346101f95760606003193601126101f9576020610235600435610220816101db565b60243560443591610230836101db565b6129a4565b604051908152f35b6004359067ffffffffffffffff821682036101f957565b6024359067ffffffffffffffff821682036101f957565b359067ffffffffffffffff821682036101f957565b9181601f840112156101f95782359167ffffffffffffffff83116101f9576020808501948460051b0101116101f957565b919082519283825260005b8481106102dd575050601f19601f8460006020809697860101520116010190565b806020809284010151828286010152016102bc565b602081016020825282518091526040820191602060408360051b8301019401926000915b83831061032557505050505090565b9091929394602080610361837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0866001960301875289516102b1565b97019301930191939290610316565b346101f95760606003193601126101f95761038961023d565b60243567ffffffffffffffff81116101f9576103a9903690600401610280565b6044929192359167ffffffffffffffff83116101f957366023840112156101f95782600401359167ffffffffffffffff83116101f9573660248460061b860101116101f95761040b9460246103ff950192612ba2565b604051918291826102f2565b0390f35b346101f95760006003193601126101f957610428612de6565b5060408051610436816104e3565b73ffffffffffffffffffffffffffffffffffffffff60206bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169283815201817f0000000000000000000000000000000000000000000000000000000000000000168152835192835251166020820152f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff8211176104ff57604052565b6104b4565b6080810190811067ffffffffffffffff8211176104ff57604052565b610160810190811067ffffffffffffffff8211176104ff57604052565b60a0810190811067ffffffffffffffff8211176104ff57604052565b90601f601f19910116810190811067ffffffffffffffff8211176104ff57604052565b6040519061058b604083610559565b565b6040519061058b61016083610559565b6040519061058b606083610559565b6040519061058b602083610559565b67ffffffffffffffff81116104ff5760051b60200190565b9080601f830112156101f95781356105ea816105bb565b926105f86040519485610559565b81845260208085019260051b8201019283116101f957602001905b8282106106205750505090565b60208091833561062f816101db565b815201910190610613565b346101f95760206003193601126101f95760043567ffffffffffffffff81116101f95761066b9036906004016105d3565b610673614108565b60005b815181101561074e57806106ab73ffffffffffffffffffffffffffffffffffffffff6106a460019486612b8e565b5116615920565b610703575b60006106fc73ffffffffffffffffffffffffffffffffffffffff6106d48487612b8e565b511673ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b5501610676565b73ffffffffffffffffffffffffffffffffffffffff6107228285612b8e565b51167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f91600080a26106b0565b005b6024359063ffffffff821682036101f957565b6044359063ffffffff821682036101f957565b359063ffffffff821682036101f957565b801515036101f957565b359061058b82610787565b81601f820112156101f9578035906107b3826105bb565b926107c16040519485610559565b82845260208085019360061b830101918183116101f957602001925b8284106107eb575050505090565b6040848303126101f95760206040918251610805816104e3565b61080e8761026b565b81528287013561081d816101db565b838201528152019301926107dd565b346101f95760406003193601126101f95760043567ffffffffffffffff81116101f957366023820112156101f9578060040135610868816105bb565b916108766040519384610559565b8183526024602084019260051b820101903682116101f95760248101925b8284106108c5576024358567ffffffffffffffff82116101f9576108bf61074e92369060040161079c565b90612dff565b833567ffffffffffffffff81116101f957820160407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82360301126101f95760405190610911826104e3565b61091d6024820161026b565b8252604481013567ffffffffffffffff81116101f957602491010136601f820112156101f957803561094e816105bb565b9161095c6040519384610559565b818352602060a08185019302820101903682116101f957602001915b8183106109975750505091816020938480940152815201930192610894565b82360360a081126101f9576080601f19604051926109b4846104e3565b86356109bf816101db565b845201126101f95760a0916020916040516109d981610504565b6109e4848801610776565b81526109f260408801610776565b84820152610a0260608801610776565b60408201526080870135610a1581610787565b606082015283820152815201920191610978565b67ffffffffffffffff81116104ff57601f01601f191660200190565b60405190610a54602083610559565b60008252565b346101f95760006003193601126101f95761040b6040805190610a7d8183610559565b600f82527f46656551756f74657220322e302e3000000000000000000000000000000000006020830152519182916020835260208301906102b1565b906020808351928381520192019060005b818110610ad75750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101610aca565b906020610b14928181520190610ab9565b90565b346101f95760006003193601126101f95760405180602060025491828152019060026000527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9060005b818110610b845761040b85610b7881870382610559565b60405191829182610b03565b8254845260209093019260019283019201610b61565b346101f95760206003193601126101f95760043567ffffffffffffffff81116101f9578060040190604060031982360301126101f957610bd8614173565b610be282806131c0565b4263ffffffff1692915060005b818110610dbc57505060240190610c0682846131c0565b92905060005b838110610c1557005b80610c34610c2f600193610c29868a6131c0565b90612a62565b613274565b7fdd84a3fa9ef9409f550d54d6affec7e9c480c878c6ab27b78912a03e1b371c6e67ffffffffffffffff610d83610d606020850194610d52610c9287517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b610cc1610c9d61057c565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092168252565b63ffffffff8c166020820152610cfc610ce2845167ffffffffffffffff1690565b67ffffffffffffffff166000526004602052604060002090565b815160209092015160e01b7fffffffff00000000000000000000000000000000000000000000000000000000167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff92909216919091179055565b5167ffffffffffffffff1690565b93517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b604080517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9290921682524260208301529190931692a201610c0c565b80610dd5610dd0600193610c2989806131c0565b61323d565b7f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a73ffffffffffffffffffffffffffffffffffffffff610ee2610d606020850194610e9d610e3f87517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b610e4a610c9d61057c565b63ffffffff8d166020820152610cfc610e77845173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff166000526005602052604060002090565b610ec3610ebe825173ffffffffffffffffffffffffffffffffffffffff1690565b6141b7565b610f1b575b5173ffffffffffffffffffffffffffffffffffffffff1690565b604080517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9290921682524260208301529190931692a201610bef565b83610f3a825173ffffffffffffffffffffffffffffffffffffffff1690565b167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba23600080a2610ec8565b9181601f840112156101f95782359167ffffffffffffffff83116101f957602083818601950101116101f957565b92610b149492610fb7928552151560208501526080604085015260808401906102b1565b9160608184039101526102b1565b346101f95760a06003193601126101f957610fde61023d565b60243590610feb826101db565b6044359160643567ffffffffffffffff81116101f95761100f903690600401610f65565b93909160843567ffffffffffffffff81116101f957611032903690600401610f65565b9290917f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff821673ffffffffffffffffffffffffffffffffffffffff82161460001461111c575050935b6bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168086116110eb5750926110da926001969261040b956141f8565b919390509260405194859485610f93565b857f6a92a4830000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b91611126926129a4565b93611091565b906020808351928381520192019060005b81811061114a5750505090565b825167ffffffffffffffff1684526020938401939092019160010161113d565b9061117d9060608352606083019061112c565b8181036020830152825180825260208201916020808360051b8301019501926000915b8383106112755750505050506040818303910152815180825260208201906020808260051b8501019401916000905b8282106111de57505050505090565b909192939594601f19878203018252845190602080835192838152019201906000905b80821061122357505050602080600192960192019201909291959394956111cf565b909192602060808261126a60019488516060809163ffffffff815116845263ffffffff602082015116602085015263ffffffff604082015116604085015201511515910152565b019401920190611201565b9091929397969560208061129583601f1986600196030187528c51610ab9565b9a019301930191939290979596976111a0565b346101f95760006003193601126101f9576008546112c581613299565b6112ce826129ea565b916112d8816129ea565b906000905b8082106112f657505061040b906040519384938461116a565b61132b61131261130584615d88565b67ffffffffffffffff1690565b61131c8487612b8e565b9067ffffffffffffffff169052565b61135e61135861133e610d528588612b8e565b67ffffffffffffffff16600052600c602052604060002090565b54613299565b6113688387612b8e565b526113738286612b8e565b5061138d61138761133e610d528588612b8e565b546132ef565b6113978385612b8e565b526113a28284612b8e565b5060005b85856113b861133e610d528784612b8e565b548310156114a3578261147883611454610ec88461140e8b61144e611434610d52838c60019f9e61142f9061149c9f8a61140e866114088361140361133e610d528561141499612b8e565b6144ca565b94612b8e565b51612b8e565b9073ffffffffffffffffffffffffffffffffffffffff169052565b612b8e565b67ffffffffffffffff16600052600b602052604060002090565b95612b8e565b73ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b61148c611485888a612b8e565b5191612b49565b6114968383612b8e565b52612b8e565b50016113a6565b50505090600101906112dd565b602060408183019282815284518094520192019060005b8181106114d45750505090565b9091926020604082611517600194885163ffffffff602080927bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8151168552015116910152565b0194019291016114c7565b346101f95760206003193601126101f95760043567ffffffffffffffff81116101f957611553903690600401610280565b61155c816105bb565b9161156a6040519384610559565b818352601f19611579836105bb565b0160005b8181106115d457505060005b828110156115c6576001906115aa6115a58260051b8501612a77565b613db9565b6115b48287612b8e565b526115bf8186612b8e565b5001611589565b6040518061040b86826114b0565b6020906115df612de6565b8282880101520161157d565b346101f95760206003193601126101f957602061161260043561160d816101db565b613378565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101f95760206003193601126101f95767ffffffffffffffff61165a61023d565b611662612de6565b5016600052600460205260406000206040519061167e826104e3565b547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116825260e01c6020820152604051809161040b82604081019263ffffffff602080927bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8151168552015116910152565b906101408061058b936116fe84825115159052565b60208181015163ffffffff169085015260408181015163ffffffff169085015260608181015163ffffffff169085015260808181015160ff169085015260a0818101517fffffffff00000000000000000000000000000000000000000000000000000000169085015260c08181015161ffff169085015260e08181015163ffffffff16908501526101008181015163ffffffff16908501526101208181015161ffff1690850152015160ff16910152565b6101608101929161058b91906116e9565b346101f95760206003193601126101f95767ffffffffffffffff6117e261023d565b6117ea613476565b5016600052600a60205261040b60406000206118d96118ce6040519261180f84610520565b5461181e60ff82165b15158552565b63ffffffff600882901c16602085015263ffffffff602882901c16604085015263ffffffff604882901c16606085015260ff606882901c1660808501527fffffffff00000000000000000000000000000000000000000000000000000000607082901b1660a085015261ffff609082901c1660c085015263ffffffff60a082901c1660e085015263ffffffff60c082901c1661010085015261ffff60e082901c1661012085015260f01c60ff1690565b60ff16610140830152565b604051918291826117af565b346101f95760006003193601126101f95760005473ffffffffffffffffffffffffffffffffffffffff81163303611986577fffffffffffffffffffffffff00000000000000000000000000000000000000006001549133828416176001551660005573ffffffffffffffffffffffffffffffffffffffff3391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b7f02b543c60000000000000000000000000000000000000000000000000000000060005260046000fd5b61058b9092919260808101936060809163ffffffff815116845263ffffffff602082015116602085015263ffffffff604082015116604085015201511515910152565b346101f95760406003193601126101f9576080611a6b611a66611a1461023d565b67ffffffffffffffff60243591611a2a836101db565b611a326132ca565b5016600052600b60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b612b49565b611aaa60405180926060809163ffffffff815116845263ffffffff602082015116602085015263ffffffff604082015116604085015201511515910152565bf35b90611abf9060408352604083019061112c565b9060208183039101526020808351928381520192019060005b818110611ae55750505090565b909192602061016082611afb60019488516116e9565b019401929101611ad8565b346101f95760006003193601126101f957600854611b23816105bb565b90611b316040519283610559565b808252601f19611b40826105bb565b0160005b818110611bdc575050611b5681613299565b9060005b818110611b7257505061040b60405192839283611aac565b80611b8e611b84611305600194615d88565b61131c8387612b8e565b611bc0611bbb611ba1610d528488612b8e565b67ffffffffffffffff16600052600a602052604060002090565b6134c8565b611bca8287612b8e565b52611bd58186612b8e565b5001611b5a565b602090611be7613476565b82828701015201611b44565b346101f95760006003193601126101f957602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346101f95760806003193601126101f957611c4061023d565b611c48610750565b611c50610763565b9060643591611c5e836101db565b611c7f611bbb8567ffffffffffffffff16600052600a602052604060002090565b91611c91611c8d8451151590565b1590565b611f3857611cbf90611cb9611cb3611cad608087015160ff1690565b60ff1690565b846134f6565b90613513565b93611cdd611cd4604085015163ffffffff1690565b63ffffffff1690565b9163ffffffff8616928311611f0e57602084015163ffffffff1663ffffffff811663ffffffff831611611ed5575050611d32611d2d8267ffffffffffffffff166000526004602052604060002090565b61333e565b9063ffffffff611d49602084015163ffffffff1690565b1615611e9a575091611db8611dab611da660ff9794611da0611d8d611d8d61040b99517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b6dffffffffffffffffffffffffffff1690565b90612958565b61352d565b662386f26fc10000900490565b9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff861614600014611e7157611e44611e3e6101407bffffffffffffffffffffffffffffffffffffffffffffffffffffffff93015160ff1690565b95613378565b16906040519586951692859094939260609263ffffffff6080840197168352602083015260408201520152565b507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff611e44606495613378565b7fa96740690000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045260246000fd5b6000fd5b7f869337890000000000000000000000000000000000000000000000000000000060005263ffffffff9081166004521660245260446000fd5b7f4c4fc93a0000000000000000000000000000000000000000000000000000000060005260046000fd5b7f99ac52f20000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff851660045260246000fd5b346101f95760206003193601126101f95760043567ffffffffffffffff81116101f957604060031982360301126101f957604051611fad816104e3565b816004013567ffffffffffffffff81116101f957611fd190600436918501016105d3565b8152602482013567ffffffffffffffff81116101f95761074e926004611ffa92369201016105d3565b6020820152613578565b359060ff821682036101f957565b35907fffffffff00000000000000000000000000000000000000000000000000000000821682036101f957565b359061ffff821682036101f957565b346101f95760206003193601126101f95760043567ffffffffffffffff81116101f957366023820112156101f95780600401359061208b826105bb565b906120996040519283610559565b8282526024610180602084019402820101903682116101f957602401925b8184106120c75761074e83613718565b83360361018081126101f957610160601f19604051926120e6846104e3565b6120ef8861026b565b845201126101f9576101809160209161210661058d565b612111848901610791565b815261211f60408901610776565b8482015261212f60608901610776565b604082015261214060808901610776565b606082015261215160a08901612004565b608082015261216260c08901612012565b60a082015261217360e0890161203f565b60c08201526121856101008901610776565b60e08201526121976101208901610776565b6101008201526121aa610140890161203f565b6101208201526121bd6101608901612004565b610140820152838201528152019301926120b7565b346101f95760406003193601126101f957606063ffffffff806122086121f661023d565b60243590612203826101db565b613943565b9193908160405195168552166020840152166040820152f35b346101f95760406003193601126101f95761223a61023d565b6024359067ffffffffffffffff82116101f95761226b61040b91612265612287943690600401610f65565b916139f2565b63ffffffff6040949394519586956060875260608701906102b1565b9216602085015283820360408501526102b1565b346101f95760006003193601126101f95760405180602060065491828152019060066000527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9060005b8181106122fc5761040b85610b7881870382610559565b82548452602090930192600192830192016122e5565b346101f95760206003193601126101f95760406123346004356115a5816101db565b611aaa8251809263ffffffff602080927bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8151168552015116910152565b346101f95760406003193601126101f95761238761023d565b60243567ffffffffffffffff81116101f957806004019160a060031983360301126101f9576123cd611bbb8267ffffffffffffffff16600052600a602052604060002090565b906123db611c8d8351151590565b6126d057606483019161241f611c8d6123f385612a77565b73ffffffffffffffffffffffffffffffffffffffff166000526001600601602052604060002054151590565b6126825761242e858284614ed1565b909161243c61160d86612a77565b96604487019660008061244f8a856131c0565b15905061265257505061249961ffff8798996124a39861247460c088015161ffff1690565b9161249061248960e08a015163ffffffff1690565b91886131c0565b949093166155e2565b9099909790612a77565b73ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001691161460001461261b576101408401516124f79060ff166128c9565b61250091612958565b9761255d60606125516125406125679a63ffffffff6125386125629b6125316125629b6024611cd49c5b0190612ac1565b905061356b565b91169061356b565b611da0611cad60808a015160ff1690565b95015163ffffffff1690565b613513565b61356b565b90612589611d2d8267ffffffffffffffff166000526004602052604060002090565b9061259e611cd4602084015163ffffffff1690565b15611e9a5761040b61260b867bffffffffffffffffffffffffffffffffffffffffffffffffffffffff612603886125626125fe8a611da0611d8d611d8d8d517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b6128f0565b91169061296b565b6040519081529081906020820190565b612624906128f0565b9761255d60606125516125406125679a63ffffffff6125386125629b6125316125629b6024611cd49c61252a565b96979098506124a361267c61267761267061012088015161ffff1690565b61ffff1690565b6128c9565b91612a77565b611ed161268e84612a77565b7f2502348c0000000000000000000000000000000000000000000000000000000060005273ffffffffffffffffffffffffffffffffffffffff16600452602490565b7f99ac52f20000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045260246000fd5b346101f95760206003193601126101f95773ffffffffffffffffffffffffffffffffffffffff600435612739816101db565b612741614108565b163381146127b357807fffffffffffffffffffffffff0000000000000000000000000000000000000000600054161760005573ffffffffffffffffffffffffffffffffffffffff600154167fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278600080a3005b7fdad89dca0000000000000000000000000000000000000000000000000000000060005260046000fd5b346101f95760406003193601126101f9576004356127fa816101db565b67ffffffffffffffff61280b610254565b169081600052600a60205260ff604060002054161561286c5761282d90613378565b6000918252600460209081526040928390205483517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9384168152921690820152f35b507f99ac52f20000000000000000000000000000000000000000000000000000000060005260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b90662386f26fc10000820291808304662386f26fc1000014901517156128eb57565b61289a565b90670de0b6b3a7640000820291808304670de0b6b3a764000014901517156128eb57565b908160051b91808304602014901517156128eb57565b9061012c82029180830461012c14901517156128eb57565b90606c820291808304606c14901517156128eb57565b818102929181159184041417156128eb57565b8115612975570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6129e36129dd610b1494937bffffffffffffffffffffffffffffffffffffffffffffffffffffffff6129d68195613378565b1690612958565b92613378565b169061296b565b906129f4826105bb565b612a016040519182610559565b8281526020601f19612a1383956105bb565b01910160005b828110612a2557505050565b606082820152602001612a19565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190811015612a725760061b0190565b612a33565b35610b14816101db565b9190811015612a725760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61813603018212156101f9570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101f9570180359067ffffffffffffffff82116101f9576020019181360383136101f957565b929192612b1e82610a29565b91612b2c6040519384610559565b8294818452818301116101f9578281602093846000960137010152565b90604051612b5681610504565b606060ff82945463ffffffff8116845263ffffffff8160201c16602085015263ffffffff8160401c166040850152821c161515910152565b8051821015612a725760209160051b010190565b909291612bef612bc68367ffffffffffffffff16600052600a602052604060002090565b5460701b7fffffffff000000000000000000000000000000000000000000000000000000001690565b90612bf9816129ea565b9560005b828110612c0e575050505050505090565b612c21612c1c828489612a62565b612a77565b8388612c3b612c31858484612a81565b6040810190612ac1565b905060208111612d5e575b508392612c7c612c76612c6f612c65600198612cbf97611a6697612a81565b6020810190612ac1565b3691612b12565b89613e20565b612c9a8967ffffffffffffffff16600052600b602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b606081015115612d2457612d08612ce06020612cfa93015163ffffffff1690565b6040805163ffffffff909216602083015290928391820190565b03601f198101835282610559565b612d12828b612b8e565b52612d1d818a612b8e565b5001612bfd565b50612cfa612d08612d59612d4c8967ffffffffffffffff16600052600a602052604060002090565b5460a01c63ffffffff1690565b612ce0565b915050612d96611cd4612d8984612c9a8b67ffffffffffffffff16600052600b602052604060002090565b5460401c63ffffffff1690565b10612da357838838612c46565b7f36f536ca0000000000000000000000000000000000000000000000000000000060005273ffffffffffffffffffffffffffffffffffffffff1660045260246000fd5b60405190612df3826104e3565b60006020838281520152565b612e07614108565b6000915b81518310156130ca57612e1e8383612b8e565b51805167ffffffffffffffff1694859283156130925760206000959301945b8551805182101561307e57612e5482602092612b8e565b510151612e80612e65838951612b8e565b515173ffffffffffffffffffffffffffffffffffffffff1690565b612e90611c8d6060840151151590565b61302d57604082015163ffffffff1660208110612fdf575090867f5c55501634b3b87e45686082d77f017b6639b436c21cb423ba6313d843f66ed173ffffffffffffffffffffffffffffffffffffffff84612fc4818f80612f9b89612f1360019d9c612c9a612fbf9667ffffffffffffffff16600052600b602052604060002090565b815181546020808501516040808701516060978801517fffffffffffffffffffffffffffffffffffffff0000000000000000000000000090951663ffffffff96909616959095179190921b67ffffffff00000000161792901b6bffffffff0000000000000000169190911790151590921b6cff00000000000000000000000016919091179055565b612fa488615b41565b5067ffffffffffffffff16600052600c602052604060002090565b6141d8565b50612fd66040519283921695826119b0565b0390a301612e3d565b7f24ecdc020000000000000000000000000000000000000000000000000000000060005273ffffffffffffffffffffffffffffffffffffffff90911660045263ffffffff1660245260446000fd5b7fb38549e40000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff8a1660045273ffffffffffffffffffffffffffffffffffffffff1660245260446000fd5b505095509250926001915001919092612e0b565b7fc35aa79d0000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff871660045260246000fd5b91505060005b81518110156131bc57806130f86130e960019385612b8e565b515167ffffffffffffffff1690565b67ffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff61314160206131258689612b8e565b51015173ffffffffffffffffffffffffffffffffffffffff1690565b600061316582612c9a8767ffffffffffffffff16600052600b602052604060002090565b5561318d816131888667ffffffffffffffff16600052600c602052604060002090565b614153565b501691167f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b600080a3016130d0565b5050565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101f9570180359067ffffffffffffffff82116101f957602001918160061b360383136101f957565b35907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff821682036101f957565b6040813603126101f95761326c602060405192613259846104e3565b8035613264816101db565b845201613214565b602082015290565b6040813603126101f95761326c602060405192613290846104e3565b6132648161026b565b906132a3826105bb565b6132b06040519182610559565b828152601f196132c082946105bb565b0190602036910137565b604051906132d782610504565b60006060838281528260208201528260408201520152565b906132f9826105bb565b6133066040519182610559565b828152601f1961331682946105bb565b019060005b82811061332757505050565b6020906133326132ca565b8282850101520161331b565b9060405161334b816104e3565b91547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116835260e01c6020830152565b73ffffffffffffffffffffffffffffffffffffffff81166000526005602052604060002090604051916133aa836104e3565b547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811680845260e09190911c602084018190521590811561344f575b5061340b5750517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff907f06439c6b000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16159050386133e3565b6040519061348382610520565b6000610140838281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201520152565b9061058b6040516134d881610520565b6101406134ee82955461181e6118188260ff1690565b60ff16910152565b9063ffffffff8091169116029063ffffffff82169182036128eb57565b9063ffffffff8091169116019063ffffffff82116128eb57565b90662386f26fc0ffff82018092116128eb57565b90600282018092116128eb57565b90602082018092116128eb57565b90600182018092116128eb57565b919082018092116128eb57565b613580614108565b60208101519160005b835181101561363457806135a2610ec860019387612b8e565b6135de6135d973ffffffffffffffffffffffffffffffffffffffff83165b73ffffffffffffffffffffffffffffffffffffffff1690565b615dbd565b6135ea575b5001613589565b60405173ffffffffffffffffffffffffffffffffffffffff9190911681527fc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda7758090602090a1386135e3565b5091505160005b81518110156131bc57613651610ec88284612b8e565b9073ffffffffffffffffffffffffffffffffffffffff8216156136ee577feb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef6136e5836136bd6136b86135c060019773ffffffffffffffffffffffffffffffffffffffff1690565b615b7c565b5060405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390a10161363b565b7f8579befe0000000000000000000000000000000000000000000000000000000060005260046000fd5b90613721614108565b60005b825181101561393e576137378184612b8e565b5160206137476130e98487612b8e565b91015167ffffffffffffffff8216918215801561391f575b80156138f1575b80156138d8575b80156138a1575b61386a579161382661382b92613821856137d06137ab612bc660019a9967ffffffffffffffff16600052600a602052604060002090565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b61383257847f4efe320c85221c7c3684c54561bea5a9c4dcfad794c6ef9ff9e6b43fb307c0f86040518061380487826117af565b0390a267ffffffffffffffff16600052600a602052604060002090565b6144f4565b615b41565b5001613724565b847f0c6380a4766d45f5d53ca170bf865bebfab44958dec379d5a90177264e6645b76040518061386287826117af565b0390a2611ba1565b7fc35aa79d0000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045260246000fd5b506138d26137ab60a08401517fffffffff000000000000000000000000000000000000000000000000000000001690565b15613774565b5060ff6138ea61014084015160ff1690565b161561376d565b5061010082015163ffffffff1663ffffffff613917611cd4604086015163ffffffff1690565b911611613766565b5063ffffffff61393761010084015163ffffffff1690565b161561375f565b509050565b9190611a666139899167ffffffffffffffff8516600052600b60205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b9160608301516139d0576139b391925067ffffffffffffffff16600052600a602052604060002090565b54609081901c61ffff169160a09190911c63ffffffff1690602090565b5063ffffffff8251169063ffffffff6040816020860151169401511691929190565b611bbb613a169194929467ffffffffffffffff16600052600a602052604060002090565b9260a08401927fffffffff00000000000000000000000000000000000000000000000000000000613a6785517fffffffff000000000000000000000000000000000000000000000000000000001690565b167f2812d52c000000000000000000000000000000000000000000000000000000008114908115613d8f575b8115613d65575b50613d11577f1e10bdc4000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000613b0b86517fffffffff000000000000000000000000000000000000000000000000000000001690565b1614613c78577fc4e05953000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000613b7d86517fffffffff000000000000000000000000000000000000000000000000000000001690565b1614613bfe57611ed1613bb085517fffffffff000000000000000000000000000000000000000000000000000000001690565b7f2ee82075000000000000000000000000000000000000000000000000000000006000527fffffffff0000000000000000000000000000000000000000000000000000000016600452602490565b613c3e9350613c1b611cd46040613c219597015163ffffffff1690565b91614d83565b91613c4c6040840151604051938491602083019190602083019252565b03601f198101845283610559565b613c726060613c5f855163ffffffff1690565b940151613c6a6105ac565b908152614e19565b91929190565b613c3e9350613c95611cd46040613c9b9597015163ffffffff1690565b91614a5e565b91613cb86060840151604051938491602083019190602083019252565b613c72613cc9845163ffffffff1690565b93613ce46020608083015192015167ffffffffffffffff1690565b90613d07613cf061059d565b6000815267ffffffffffffffff9093166020840152565b6040820152614c5c565b613d52935093613d489294613d42611cd46040613d3661010086015163ffffffff1690565b94015163ffffffff1690565b926148b8565b5163ffffffff1690565b90613d5b610a45565b9190610b14610a45565b7f647e2ba90000000000000000000000000000000000000000000000000000000091501438613a9a565b7fac77ffec0000000000000000000000000000000000000000000000000000000081149150613a93565b73ffffffffffffffffffffffffffffffffffffffff90613dd7612de6565b50166000526005602052604060002060405190613df3826104e3565b547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116825260e01c602082015290565b7fffffffff000000000000000000000000000000000000000000000000000000001691907f2812d52c000000000000000000000000000000000000000000000000000000008314613f68577f1e10bdc4000000000000000000000000000000000000000000000000000000008314613f5a577fac77ffec000000000000000000000000000000000000000000000000000000008314613f4f577f647e2ba9000000000000000000000000000000000000000000000000000000008314613f44577fc4e05953000000000000000000000000000000000000000000000000000000008314613f3557827f2ee820750000000000000000000000000000000000000000000000000000000060005260045260246000fd5b61058b91925061dee990615785565b61058b9192506157e6565b61058b919250615722565b61058b919250600190615785565b61058b919250615692565b917fffffffff0000000000000000000000000000000000000000000000000000000083167f2812d52c0000000000000000000000000000000000000000000000000000000081146140fc577f1e10bdc40000000000000000000000000000000000000000000000000000000081146140dc577fac77ffec0000000000000000000000000000000000000000000000000000000081146140d0577f647e2ba90000000000000000000000000000000000000000000000000000000081146140c4577fc4e0595300000000000000000000000000000000000000000000000000000000146140a9577f2ee82075000000000000000000000000000000000000000000000000000000006000527fffffffff00000000000000000000000000000000000000000000000000000000831660045260246000fd5b61058b9250156140bc5761dee990615785565b600090615785565b505061058b91506157e6565b505061058b9150615722565b5061058b9250156140f35760ff60015b1690615785565b60ff60006140ec565b505061058b9150615692565b73ffffffffffffffffffffffffffffffffffffffff60015416330361412957565b7f2b5c74de0000000000000000000000000000000000000000000000000000000060005260046000fd5b73ffffffffffffffffffffffffffffffffffffffff610b14921690615a04565b3360005260036020526040600020541561418957565b7fd86ad9cf000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff610b1491166006615bb1565b73ffffffffffffffffffffffffffffffffffffffff610b14921690615bb1565b611bbb61421f9196949395929667ffffffffffffffff16600052600a602052604060002090565b9460a08601947fffffffff0000000000000000000000000000000000000000000000000000000061427087517fffffffff000000000000000000000000000000000000000000000000000000001690565b167f2812d52c0000000000000000000000000000000000000000000000000000000081149081156144a0575b8115614476575b5061443d5750507fc4e05953000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000061431686517fffffffff000000000000000000000000000000000000000000000000000000001690565b1614614412577f1e10bdc4000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000061438886517fffffffff000000000000000000000000000000000000000000000000000000001690565b16146143bb57611ed1613bb085517fffffffff000000000000000000000000000000000000000000000000000000001690565b6143fd9350612c6f60606143e76143e0611cd4604061440b989a015163ffffffff1690565b8486614a5e565b0151604051958691602083019190602083019252565b03601f198101865285610559565b9160019190565b6143fd9350612c6f60406143e7614436611cd48361440b989a015163ffffffff1690565b8486614d83565b945094610b149261446b92614460611cd461010061446695015163ffffffff1690565b91615c11565b615d3e565b936001933691612b12565b7f647e2ba900000000000000000000000000000000000000000000000000000000915014386142a3565b7fac77ffec000000000000000000000000000000000000000000000000000000008114915061429c565b73ffffffffffffffffffffffffffffffffffffffff916144e991615839565b90549060031b1c1690565b61486961014061058b9361453c61450b8251151590565b859060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b614586614550602083015163ffffffff1690565b85547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1660089190911b64ffffffff0016178555565b6145d461459a604083015163ffffffff1690565b85547fffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff1660289190911b68ffffffff000000000016178555565b6146266145e8606083015163ffffffff1690565b85547fffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffff1660489190911b6cffffffff00000000000000000016178555565b614676614637608083015160ff1690565b85547fffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffff1660689190911b6dff0000000000000000000000000016178555565b6146e96146a660a08301517fffffffff000000000000000000000000000000000000000000000000000000001690565b85547fffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff1660709190911c71ffffffff000000000000000000000000000016178555565b6147406146fb60c083015161ffff1690565b85547fffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffffffff1660909190911b73ffff00000000000000000000000000000000000016178555565b61479d61475460e083015163ffffffff1690565b85547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b77ffffffff000000000000000000000000000000000000000016178555565b6147ff6147b261010083015163ffffffff1690565b85547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff1660c09190911b7bffffffff00000000000000000000000000000000000000000000000016178555565b61486161481261012083015161ffff1690565b85547fffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660e09190911b7dffff0000000000000000000000000000000000000000000000000000000016178555565b015160ff1690565b7fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7eff00000000000000000000000000000000000000000000000000000000000083549260f01b169116179055565b9063ffffffff6148d2936148ca612de6565b501691615c11565b90815111611f0e5790565b906004116101f95790600490565b90929192836004116101f95783116101f957600401916003190190565b919091357fffffffff000000000000000000000000000000000000000000000000000000008116926004811061493c575050565b7fffffffff00000000000000000000000000000000000000000000000000000000929350829060040360031b1b161690565b9080601f830112156101f9578135614985816105bb565b926149936040519485610559565b81845260208085019260051b8201019283116101f957602001905b8282106149bb5750505090565b81358152602091820191016149ae565b6020818303126101f95780359067ffffffffffffffff82116101f9570160a0818303126101f957604051916149ff8361053d565b614a0882610776565b8352614a166020830161026b565b60208401526040820135614a2981610787565b604084015260608201356060840152608082013567ffffffffffffffff81116101f957614a56920161496e565b608082015290565b60606080604051614a6e8161053d565b600081526000602082015260006040820152600083820152015260048210614b72577f1f3b3aba000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000614ae5614adf85856148dd565b90614908565b1603614b485781614b0192614af9926148eb565b8101906149cb565b9063ffffffff614b15835163ffffffff1690565b1611614b1e5790565b7f2e2b0c290000000000000000000000000000000000000000000000000000000060005260046000fd5b7f5247fdce0000000000000000000000000000000000000000000000000000000060005260046000fd5b7fb00b53dc0000000000000000000000000000000000000000000000000000000060005260046000fd5b805160209091019060005b818110614bb45750505090565b8251845260209384019390920191600101614ba7565b90610b1494937fffffffffffffffff000000000000000000000000000000000000000000000000600e947fff0000000000000000000000000000000000000000000000000000000000000080947f1a2b3c4d00000000000000000000000000000000000000000000000000000000875260f81b16600486015260c01b16600584015260f81b16600d8201520190614b9c565b604081019081515160ff8111614cd4578151916003831015614ca5576020015160ff93610b1493612cfa9267ffffffffffffffff16915191604051968795169160208601614bca565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7fd9437f9d00000000000000000000000000000000000000000000000000000000600052600d600452600060245260446000fd5b6020818303126101f95780359067ffffffffffffffff82116101f957016080818303126101f95760405191614d3c83610504565b813583526020820135614d4e81610787565b602084015260408201356040840152606082013567ffffffffffffffff81116101f957614d7b920161496e565b606082015290565b606080604051614d9281610504565b600081526000602082015260006040820152015260048210614b72577f21ea4ca9000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000614dfd614adf85856148dd565b1603614b4857816148d292614e11926148eb565b810190614d08565b8051519060ff8211614e8e577fff0000000000000000000000000000000000000000000000000000000000000091612cfa610b1492516040519485937f5e6f7a8b00000000000000000000000000000000000000000000000000000000602086015260f81b1660248401526025830190614b9c565b7fd9437f9d00000000000000000000000000000000000000000000000000000000600052600e600452600060245260446000fd5b908160209103126101f9573590565b909291600093614ee46020830183612ac1565b90506040830193614ef585856131c0565b90506020840190614f0d611cd4835163ffffffff1690565b8085116155b057506001811161557e5760a0850196614f4c88517fffffffff000000000000000000000000000000000000000000000000000000001690565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f2812d52c0000000000000000000000000000000000000000000000000000000081148015615555575b801561552c575b1561502257505050505050509181615018612c6f614fe761501196614fcb608061501e980186612ac1565b613d42611cd46040613d36610100879697015163ffffffff1690565b51958694517fffffffff000000000000000000000000000000000000000000000000000000001690565b9280612ac1565b90613f73565b9190565b7fc4e0595300000000000000000000000000000000000000000000000000000000819c94979b9a9395989c999699146000146152b15750506150c26150896150b5999a9b6040613c1b611cd461507b60808b018b612ac1565b939094015163ffffffff1690565b918251998a91517fffffffff000000000000000000000000000000000000000000000000000000001690565b615018612c6f8880612ac1565b606081015151908a6150df6150d78780612ac1565b810190614ec2565b61528c575081615256575b8515159081615249575b5061521f57604081116151ed57506151239061511d6151168695949896612942565b809261356b565b9961356b565b946000935b838510615182575050505050611cd4615145915163ffffffff1690565b8082116151525750509190565b7f869337890000000000000000000000000000000000000000000000000000000060005260045260245260446000fd5b90919293956001906151c2611cd4612d896151b18667ffffffffffffffff16600052600b602052604060002090565b611454612c1c8d610c298b8d6131c0565b80156151dd576151d19161356b565b965b0193929190615128565b506151e79061354f565b966151d3565b7fc327a56c00000000000000000000000000000000000000000000000000000000600052600452604060245260446000fd5b7f5bed51920000000000000000000000000000000000000000000000000000000060005260046000fd5b60409150015115386150f4565b611ed1827fc327a56c00000000000000000000000000000000000000000000000000000000600052906044916004526000602452565b925099506152ab6152a461529f8361355d565b612914565b809361356b565b996150ea565b7f1e10bdc400000000000000000000000000000000000000000000000000000000036154dd57506153346152f96150b5999a9b6040613c95611cd461507b60808b018b612ac1565b9161530b611cd4845163ffffffff1690565b998a91517fffffffff000000000000000000000000000000000000000000000000000000001690565b608081015151908a6153496150d78780612ac1565b6154c457508161548e575b85151580615482575b61521f576040821161544e576020015167ffffffffffffffff9081169081831c166154145750506153989061511d615116869594989661292a565b946000935b8385106153ba575050505050611cd4615145915163ffffffff1690565b90919293956001906153e9611cd4612d896151b18667ffffffffffffffff16600052600b602052604060002090565b8015615404576153f89161356b565b965b019392919061539d565b5061540e9061354f565b966153fa565b7fafa933080000000000000000000000000000000000000000000000000000000060005267ffffffffffffffff1660045260245260446000fd5b7f8a0d71f7000000000000000000000000000000000000000000000000000000006000526004829052604060245260446000fd5b5060608101511561535d565b611ed1827f8a0d71f700000000000000000000000000000000000000000000000000000000600052906044916004526000602452565b925099506154d76152a461529f83613541565b99615354565b7f2ee82075000000000000000000000000000000000000000000000000000000006000527fffffffff000000000000000000000000000000000000000000000000000000001660045260246000fd5b507f647e2ba9000000000000000000000000000000000000000000000000000000008114614fa0565b507fac77ffec000000000000000000000000000000000000000000000000000000008114614f99565b7fd88dddd600000000000000000000000000000000000000000000000000000000600052600452600160245260446000fd5b7f8693378900000000000000000000000000000000000000000000000000000000600052600452602484905260446000fd5b94939192909282156156725767ffffffffffffffff16600052600b60205260406000209115612a725761561e91611a66913590612c9a826101db565b9261562f611c8d6060860151151590565b615660575050615649612677611cd4845163ffffffff1690565b90613c726040613d36602086015163ffffffff1690565b61566b9193506128c9565b9190602090565b505050509050600090600090600090565b908160209103126101f9575190565b60208151036156d5576156ae6020825183010160208301615683565b73ffffffffffffffffffffffffffffffffffffffff8111908115615716575b506156d55750565b615712906040519182917f8d666f6000000000000000000000000000000000000000000000000000000000835260206004840181815201906102b1565b0390fd5b610400915010386156cd565b602081510361574857600b6157406020835184010160208401615683565b106157485750565b615712906040519182917fe0d7fb0200000000000000000000000000000000000000000000000000000000835260206004840181815201906102b1565b9060208251036157ab5780615798575050565b6157406020835184010160208401615683565b6040517fe0d7fb02000000000000000000000000000000000000000000000000000000008152602060048201528061571260248201856102b1565b60248151036157fc576022810151156157fc5750565b615712906040519182917f373b0e4400000000000000000000000000000000000000000000000000000000835260206004840181815201906102b1565b8054821015612a725760005260206000200190600090565b91615889918354907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b9055565b805480156158f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01906158c28282615839565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82549160031b1b1916905555565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6000818152600760205260409020549081156159fd577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201908282116128eb57600654927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84019384116128eb5783836000956159bc95036159c2575b5050506159ab600661588d565b600790600052602052604060002090565b55600190565b6159ab6159ee916159e46159da6159f4956006615839565b90549060031b1c90565b9283916006615839565b90615851565b5538808061599e565b5050600090565b6001810191806000528260205260406000205492831515600014615adc577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84018481116128eb578354937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85019485116128eb5760009585836159bc97615a949503615aa3575b50505061588d565b90600052602052604060002090565b615ac36159ee91615aba6159da615ad39588615839565b92839187615839565b8590600052602052604060002090565b55388080615a8c565b50505050600090565b805490680100000000000000008210156104ff5781615b0c91600161588994018155615839565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b600081815260096020526040902054615b7657615b5f816008615ae5565b600854906000526009602052604060002055600190565b50600090565b600081815260036020526040902054615b7657615b9a816002615ae5565b600254906000526003602052604060002055600190565b60008281526001820160205260409020546159fd5780615bd383600193615ae5565b80549260005201602052604060002055600190565b908160409103126101f957602060405191615c02836104e3565b80518352015161326c81610787565b91615c1a612de6565b5060048210615d1c5750615c5d612c6f8280615c577fffffffff000000000000000000000000000000000000000000000000000000009587614908565b956148eb565b91167f181dcf10000000000000000000000000000000000000000000000000000000008103615ca4575080602080615c9a93518301019101615be8565b6001602082015290565b7f97a657c90000000000000000000000000000000000000000000000000000000014615cf4577f5247fdce0000000000000000000000000000000000000000000000000000000060005260046000fd5b80602080615d0793518301019101615683565b615d0f61057c565b9081526001602082015290565b91505067ffffffffffffffff615d3061057c565b911681526001602082015290565b6020604051917f181dcf1000000000000000000000000000000000000000000000000000000000828401528051602484015201511515604482015260448152610b14606482610559565b600854811015612a725760086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3015490565b6000818152600360205260409020549081156159fd577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201908282116128eb57600254927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84019384116128eb5783836159bc9460009603615e59575b505050615e48600261588d565b600390600052602052604060002090565b615e486159ee91615e716159da615e7b956002615839565b9283916002615839565b55388080615e3b56fea164736f6c634300081a000a" + +type FeeQuoterContract struct { + address common.Address + abi abi.ABI + backend bind.ContractBackend + contract *bind.BoundContract +} + +func NewFeeQuoterContract( + address common.Address, + backend bind.ContractBackend, +) (*FeeQuoterContract, error) { + parsed, err := abi.JSON(strings.NewReader(FeeQuoterABI)) + if err != nil { + return nil, err + } + return &FeeQuoterContract{ + address: address, + abi: parsed, + backend: backend, + contract: bind.NewBoundContract(address, parsed, backend, backend, backend), + }, nil +} + +func (c *FeeQuoterContract) Address() common.Address { + return c.address +} + +func (c *FeeQuoterContract) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []any + err := c.contract.Call(opts, &out, "owner") + if err != nil { + return common.Address{}, err + } + return *abi.ConvertType(out[0], new(common.Address)).(*common.Address), nil +} + +func (c *FeeQuoterContract) ApplyDestChainConfigUpdates(opts *bind.TransactOpts, args []DestChainConfigArgs) (*types.Transaction, error) { + return c.contract.Transact(opts, "applyDestChainConfigUpdates", args) +} + +func (c *FeeQuoterContract) UpdatePrices(opts *bind.TransactOpts, args PriceUpdates) (*types.Transaction, error) { + return c.contract.Transact(opts, "updatePrices", args) +} + +func (c *FeeQuoterContract) ApplyAuthorizedCallerUpdates(opts *bind.TransactOpts, args AuthorizedCallerArgs) (*types.Transaction, error) { + return c.contract.Transact(opts, "applyAuthorizedCallerUpdates", args) +} + +func (c *FeeQuoterContract) ApplyTokenTransferFeeConfigUpdates(opts *bind.TransactOpts, tokenTransferFeeConfigArgs []TokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs []TokenTransferFeeConfigRemoveArgs) (*types.Transaction, error) { + return c.contract.Transact(opts, "applyTokenTransferFeeConfigUpdates", tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) +} + +func (c *FeeQuoterContract) GetDestChainConfig(opts *bind.CallOpts, args uint64) (DestChainConfig, error) { + var out []any + err := c.contract.Call(opts, &out, "getDestChainConfig", args) + if err != nil { + var zero DestChainConfig + return zero, err + } + return *abi.ConvertType(out[0], new(DestChainConfig)).(*DestChainConfig), nil +} + +func (c *FeeQuoterContract) GetDestinationChainGasPrice(opts *bind.CallOpts, args uint64) (TimestampedPackedUint224, error) { + var out []any + err := c.contract.Call(opts, &out, "getDestinationChainGasPrice", args) + if err != nil { + var zero TimestampedPackedUint224 + return zero, err + } + return *abi.ConvertType(out[0], new(TimestampedPackedUint224)).(*TimestampedPackedUint224), nil +} + +func (c *FeeQuoterContract) GetTokenTransferFeeConfig(opts *bind.CallOpts, destChainSelector uint64, token common.Address) (TokenTransferFeeConfig, error) { + var out []any + err := c.contract.Call(opts, &out, "getTokenTransferFeeConfig", destChainSelector, token) + if err != nil { + var zero TokenTransferFeeConfig + return zero, err + } + return *abi.ConvertType(out[0], new(TokenTransferFeeConfig)).(*TokenTransferFeeConfig), nil +} + +func (c *FeeQuoterContract) GetStaticConfig(opts *bind.CallOpts) (StaticConfig, error) { + var out []any + err := c.contract.Call(opts, &out, "getStaticConfig") + if err != nil { + var zero StaticConfig + return zero, err + } + return *abi.ConvertType(out[0], new(StaticConfig)).(*StaticConfig), nil +} + +func (c *FeeQuoterContract) GetAllAuthorizedCallers(opts *bind.CallOpts) ([]common.Address, error) { + var out []any + err := c.contract.Call(opts, &out, "getAllAuthorizedCallers") + if err != nil { + var zero []common.Address + return zero, err + } + return *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address), nil +} + +type AuthorizedCallerArgs struct { + AddedCallers []common.Address + RemovedCallers []common.Address +} + +type DestChainConfig struct { + IsEnabled bool + MaxDataBytes uint32 + MaxPerMsgGasLimit uint32 + DestGasOverhead uint32 + DestGasPerPayloadByteBase uint8 + ChainFamilySelector [4]byte + DefaultTokenFeeUSDCents uint16 + DefaultTokenDestGasOverhead uint32 + DefaultTxGasLimit uint32 + NetworkFeeUSDCents uint16 + LinkFeeMultiplierPercent uint8 +} + +type DestChainConfigArgs struct { + DestChainSelector uint64 + DestChainConfig DestChainConfig +} + +type GasPriceUpdate struct { + DestChainSelector uint64 + UsdPerUnitGas *big.Int +} + +type PriceUpdates struct { + TokenPriceUpdates []TokenPriceUpdate + GasPriceUpdates []GasPriceUpdate +} + +type StaticConfig struct { + MaxFeeJuelsPerMsg *big.Int + LinkToken common.Address +} + +type TimestampedPackedUint224 struct { + Value *big.Int + Timestamp uint32 +} + +type TokenPriceUpdate struct { + SourceToken common.Address + UsdPerToken *big.Int +} + +type TokenTransferFeeConfig struct { + FeeUSDCents uint32 + DestGasOverhead uint32 + DestBytesOverhead uint32 + IsEnabled bool +} + +type TokenTransferFeeConfigArgs struct { + DestChainSelector uint64 + TokenTransferFeeConfigs []TokenTransferFeeConfigSingleTokenArgs +} + +type TokenTransferFeeConfigRemoveArgs struct { + DestChainSelector uint64 + Token common.Address +} + +type TokenTransferFeeConfigSingleTokenArgs struct { + Token common.Address + TokenTransferFeeConfig TokenTransferFeeConfig +} + +type ApplyTokenTransferFeeConfigUpdatesArgs struct { + TokenTransferFeeConfigArgs []TokenTransferFeeConfigArgs + TokensToUseDefaultFeeConfigs []TokenTransferFeeConfigRemoveArgs +} + +type GetTokenTransferFeeConfigArgs struct { + DestChainSelector uint64 + Token common.Address +} + +type ConstructorArgs struct { + StaticConfig StaticConfig + PriceUpdaters []common.Address + TokenTransferFeeConfigArgs []TokenTransferFeeConfigArgs + DestChainConfigArgs []DestChainConfigArgs +} + +var Deploy = contract.NewDeploy(contract.DeployParams[ConstructorArgs]{ + Name: "fee-quoter:deploy", + Version: Version, + Description: "Deploys the FeeQuoter contract", + ContractMetadata: &bind.MetaData{ + ABI: FeeQuoterABI, + Bin: FeeQuoterBin, + }, + BytecodeByTypeAndVersion: map[string]contract.Bytecode{ + cldf_deployment.NewTypeAndVersion(ContractType, *Version).String(): { + EVM: common.FromHex(FeeQuoterBin), + }, + }, + Validate: func(ConstructorArgs) error { return nil }, +}) + +var ApplyDestChainConfigUpdates = contract.NewWrite(contract.WriteParams[[]DestChainConfigArgs, *FeeQuoterContract]{ + Name: "fee-quoter:apply-dest-chain-config-updates", + Version: Version, + Description: "Calls applyDestChainConfigUpdates on the contract", + ContractType: ContractType, + ContractABI: FeeQuoterABI, + NewContract: NewFeeQuoterContract, + IsAllowedCaller: contract.OnlyOwner[*FeeQuoterContract, []DestChainConfigArgs], + Validate: func([]DestChainConfigArgs) error { return nil }, + CallContract: func( + c *FeeQuoterContract, + opts *bind.TransactOpts, + args []DestChainConfigArgs, + ) (*types.Transaction, error) { + return c.ApplyDestChainConfigUpdates(opts, args) + }, +}) + +var UpdatePrices = contract.NewWrite(contract.WriteParams[PriceUpdates, *FeeQuoterContract]{ + Name: "fee-quoter:update-prices", + Version: Version, + Description: "Calls updatePrices on the contract", + ContractType: ContractType, + ContractABI: FeeQuoterABI, + NewContract: NewFeeQuoterContract, + IsAllowedCaller: contract.OnlyOwner[*FeeQuoterContract, PriceUpdates], + Validate: func(PriceUpdates) error { return nil }, + CallContract: func( + c *FeeQuoterContract, + opts *bind.TransactOpts, + args PriceUpdates, + ) (*types.Transaction, error) { + return c.UpdatePrices(opts, args) + }, +}) + +var ApplyAuthorizedCallerUpdates = contract.NewWrite(contract.WriteParams[AuthorizedCallerArgs, *FeeQuoterContract]{ + Name: "fee-quoter:apply-authorized-caller-updates", + Version: Version, + Description: "Calls applyAuthorizedCallerUpdates on the contract", + ContractType: ContractType, + ContractABI: FeeQuoterABI, + NewContract: NewFeeQuoterContract, + IsAllowedCaller: contract.OnlyOwner[*FeeQuoterContract, AuthorizedCallerArgs], + Validate: func(AuthorizedCallerArgs) error { return nil }, + CallContract: func( + c *FeeQuoterContract, + opts *bind.TransactOpts, + args AuthorizedCallerArgs, + ) (*types.Transaction, error) { + return c.ApplyAuthorizedCallerUpdates(opts, args) + }, +}) + +var ApplyTokenTransferFeeConfigUpdates = contract.NewWrite(contract.WriteParams[ApplyTokenTransferFeeConfigUpdatesArgs, *FeeQuoterContract]{ + Name: "fee-quoter:apply-token-transfer-fee-config-updates", + Version: Version, + Description: "Calls applyTokenTransferFeeConfigUpdates on the contract", + ContractType: ContractType, + ContractABI: FeeQuoterABI, + NewContract: NewFeeQuoterContract, + IsAllowedCaller: contract.OnlyOwner[*FeeQuoterContract, ApplyTokenTransferFeeConfigUpdatesArgs], + Validate: func(ApplyTokenTransferFeeConfigUpdatesArgs) error { return nil }, + CallContract: func( + c *FeeQuoterContract, + opts *bind.TransactOpts, + args ApplyTokenTransferFeeConfigUpdatesArgs, + ) (*types.Transaction, error) { + return c.ApplyTokenTransferFeeConfigUpdates(opts, args.TokenTransferFeeConfigArgs, args.TokensToUseDefaultFeeConfigs) + }, +}) + +var GetDestChainConfig = contract.NewRead(contract.ReadParams[uint64, DestChainConfig, *FeeQuoterContract]{ + Name: "fee-quoter:get-dest-chain-config", + Version: Version, + Description: "Calls getDestChainConfig on the contract", + ContractType: ContractType, + NewContract: NewFeeQuoterContract, + CallContract: func(c *FeeQuoterContract, opts *bind.CallOpts, args uint64) (DestChainConfig, error) { + return c.GetDestChainConfig(opts, args) + }, +}) + +var GetDestinationChainGasPrice = contract.NewRead(contract.ReadParams[uint64, TimestampedPackedUint224, *FeeQuoterContract]{ + Name: "fee-quoter:get-destination-chain-gas-price", + Version: Version, + Description: "Calls getDestinationChainGasPrice on the contract", + ContractType: ContractType, + NewContract: NewFeeQuoterContract, + CallContract: func(c *FeeQuoterContract, opts *bind.CallOpts, args uint64) (TimestampedPackedUint224, error) { + return c.GetDestinationChainGasPrice(opts, args) + }, +}) + +var GetTokenTransferFeeConfig = contract.NewRead(contract.ReadParams[GetTokenTransferFeeConfigArgs, TokenTransferFeeConfig, *FeeQuoterContract]{ + Name: "fee-quoter:get-token-transfer-fee-config", + Version: Version, + Description: "Calls getTokenTransferFeeConfig on the contract", + ContractType: ContractType, + NewContract: NewFeeQuoterContract, + CallContract: func(c *FeeQuoterContract, opts *bind.CallOpts, args GetTokenTransferFeeConfigArgs) (TokenTransferFeeConfig, error) { + return c.GetTokenTransferFeeConfig(opts, args.DestChainSelector, args.Token) + }, +}) + +var GetStaticConfig = contract.NewRead(contract.ReadParams[struct{}, StaticConfig, *FeeQuoterContract]{ + Name: "fee-quoter:get-static-config", + Version: Version, + Description: "Calls getStaticConfig on the contract", + ContractType: ContractType, + NewContract: NewFeeQuoterContract, + CallContract: func(c *FeeQuoterContract, opts *bind.CallOpts, args struct{}) (StaticConfig, error) { + return c.GetStaticConfig(opts) + }, +}) + +var GetAllAuthorizedCallers = contract.NewRead(contract.ReadParams[struct{}, []common.Address, *FeeQuoterContract]{ + Name: "fee-quoter:get-all-authorized-callers", + Version: Version, + Description: "Calls getAllAuthorizedCallers on the contract", + ContractType: ContractType, + NewContract: NewFeeQuoterContract, + CallContract: func(c *FeeQuoterContract, opts *bind.CallOpts, args struct{}) ([]common.Address, error) { + return c.GetAllAuthorizedCallers(opts) + }, +}) diff --git a/chains/evm/deployment/v2_0_0/sequences/SequenceFeeQuoterInputCreation_Mapping.md b/chains/evm/deployment/v2_0_0/sequences/SequenceFeeQuoterInputCreation_Mapping.md new file mode 100644 index 0000000000..13bc747f1d --- /dev/null +++ b/chains/evm/deployment/v2_0_0/sequences/SequenceFeeQuoterInputCreation_Mapping.md @@ -0,0 +1,55 @@ +# SequenceFeeQuoterInputCreation Mapping Document + +## From v1.6.0 (`CreateFeeQuoterUpdateInputFromV160`) + +**Source Contract**: FeeQuoter v1.6.3 +**Target Contract**: FeeQuoter v2.0.0 + +### Field Mapping +| Target Field (FeeQuoter v2.0.0) | Source Field (FeeQuoter v1.6.3) | Notes | +|----------------------------------|----------------------------------|-------| +| `ConstructorArgs.StaticConfig.LinkToken` | `StaticCfg.LinkToken` | Direct copy | +| `ConstructorArgs.StaticConfig.MaxFeeJuelsPerMsg` | `StaticCfg.MaxFeeJuelsPerMsg` | Direct copy | +| `ConstructorArgs.PriceUpdaters` or `AuthorizedCallerUpdates.AddedCallers` | `PriceUpdaters` | Depends on new deployment vs update | +| `DestChainConfig.IsEnabled` | `RemoteChainCfgs[chain].DestChainCfg.IsEnabled` | Direct copy | +| `DestChainConfig.MaxDataBytes` | `RemoteChainCfgs[chain].DestChainCfg.MaxDataBytes` | Direct copy | +| `DestChainConfig.MaxPerMsgGasLimit` | `RemoteChainCfgs[chain].DestChainCfg.MaxPerMsgGasLimit` | Direct copy | +| `DestChainConfig.DestGasOverhead` | `RemoteChainCfgs[chain].DestChainCfg.DestGasOverhead` | Direct copy | +| `DestChainConfig.DestGasPerPayloadByteBase` | `RemoteChainCfgs[chain].DestChainCfg.DestGasPerPayloadByteBase` | Direct copy | +| `DestChainConfig.ChainFamilySelector` | `RemoteChainCfgs[chain].DestChainCfg.ChainFamilySelector` | Direct copy | +| `DestChainConfig.DefaultTokenFeeUSDCents` | `RemoteChainCfgs[chain].DestChainCfg.DefaultTokenFeeUSDCents` | Direct copy | +| `DestChainConfig.DefaultTokenDestGasOverhead` | `RemoteChainCfgs[chain].DestChainCfg.DefaultTokenDestGasOverhead` | Direct copy | +| `DestChainConfig.DefaultTxGasLimit` | `RemoteChainCfgs[chain].DestChainCfg.DefaultTxGasLimit` | Direct copy | +| `DestChainConfig.NetworkFeeUSDCents` | `RemoteChainCfgs[chain].DestChainCfg.NetworkFeeUSDCents` | Cast from `uint8` to `uint16` | +| `DestChainConfig.LinkFeeMultiplierPercent` | N/A | Hardcoded to `90` | +| `TokenTransferFeeConfig.FeeUSDCents` | `RemoteChainCfgs[chain].TokenTransferFeeCfgs[token].MinFeeUSDCents` | Direct copy | +| `TokenTransferFeeConfig.DestGasOverhead` | `RemoteChainCfgs[chain].TokenTransferFeeCfgs[token].DestGasOverhead` | Direct copy | +| `TokenTransferFeeConfig.DestBytesOverhead` | `RemoteChainCfgs[chain].TokenTransferFeeCfgs[token].DestBytesOverhead` | Direct copy | +| `TokenTransferFeeConfig.IsEnabled` | `RemoteChainCfgs[chain].TokenTransferFeeCfgs[token].IsEnabled` | Direct copy | + +## From v1.5.0 (`CreateFeeQuoterUpdateInputFromV150`) + +**Source Contract**: EVM2EVMOnRamp v1.5.0 +**Target Contract**: FeeQuoter v2.0.0 + +### Field Mapping +| Target Field (FeeQuoter v2.0.0) | Source Field (EVM2EVMOnRamp v1.5.0) | Notes | +|----------------------------------|--------------------------------------|-------| +| `ConstructorArgs.StaticConfig.LinkToken` | `OnRampCfg.StaticConfig.LinkToken` | From first OnRamp (if empty) | +| `ConstructorArgs.StaticConfig.MaxFeeJuelsPerMsg` | `OnRampCfg.StaticConfig.MaxNopFeesJuels` | From first OnRamp (if empty) | +| `ConstructorArgs.PriceUpdaters` | N/A | Empty array `[]` (TODO: what to do with price updaters for 1.5 if there is no 1.6 lanes here) | +| `DestChainConfig.IsEnabled` | N/A | Hardcoded to `true` (if chain is supported on OnRamp, enable it on FeeQuoter) | +| `DestChainConfig.MaxDataBytes` | `OnRampCfg.DynamicConfig.MaxDataBytes` | Direct copy | +| `DestChainConfig.MaxPerMsgGasLimit` | `OnRampCfg.DynamicConfig.MaxPerMsgGasLimit` | Direct copy | +| `DestChainConfig.DestGasOverhead` | `OnRampCfg.DynamicConfig.DestGasOverhead` | Direct copy | +| `DestChainConfig.DestGasPerPayloadByteBase` | `OnRampCfg.DynamicConfig.DestGasPerPayloadByte` | Cast from `uint8` | +| `DestChainConfig.ChainFamilySelector` | N/A | Hardcoded to EVM family selector `0x2812d52c` | +| `DestChainConfig.DefaultTokenFeeUSDCents` | `OnRampCfg.DynamicConfig.DefaultTokenFeeUSDCents` | Direct copy | +| `DestChainConfig.DefaultTokenDestGasOverhead` | `OnRampCfg.DynamicConfig.DefaultTokenDestGasOverhead` | Direct copy | +| `DestChainConfig.DefaultTxGasLimit` | `OnRampCfg.StaticConfig.DefaultTxGasLimit` | Cast to `uint32` | +| `DestChainConfig.NetworkFeeUSDCents` | `OnRampCfg.FeeTokenConfig[].NetworkFeeUSDCents` | From first non-zero value (same across all fee tokens) | +| `DestChainConfig.LinkFeeMultiplierPercent` | N/A | Hardcoded to `90` | +| `TokenTransferFeeConfig.FeeUSDCents` | `OnRampCfg.TokenTransferFeeConfig[token].MinFeeUSDCents` | Direct copy | +| `TokenTransferFeeConfig.DestGasOverhead` | `OnRampCfg.TokenTransferFeeConfig[token].DestGasOverhead` | Direct copy | +| `TokenTransferFeeConfig.DestBytesOverhead` | `OnRampCfg.TokenTransferFeeConfig[token].DestBytesOverhead` | Direct copy | +| `TokenTransferFeeConfig.IsEnabled` | `OnRampCfg.TokenTransferFeeConfig[token].IsEnabled` | Direct copy | diff --git a/chains/evm/deployment/v2_0_0/sequences/fee_quoter.go b/chains/evm/deployment/v2_0_0/sequences/fee_quoter.go new file mode 100644 index 0000000000..1741745573 --- /dev/null +++ b/chains/evm/deployment/v2_0_0/sequences/fee_quoter.go @@ -0,0 +1,718 @@ +package sequences + +import ( + "encoding/json" + "fmt" + "math/big" + "strings" + + "github.com/Masterminds/semver/v3" + "github.com/ethereum/go-ethereum/common" + chain_selectors "github.com/smartcontractkit/chain-selectors" + cldf_chain "github.com/smartcontractkit/chainlink-deployments-framework/chain" + "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm" + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + mcms_types "github.com/smartcontractkit/mcms/types" + "golang.org/x/exp/maps" + + "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/utils/operations/contract" + adapters1_2 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_2_0/adapters" + routerops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_2_0/operations/router" + onrampops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/operations/onramp" + seq1_5 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/sequences" + fq1_6 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/fee_quoter" + seq1_6 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/sequences" + "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils" + datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" + + fqops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/operations/fee_quoter" +) + +const ( + LinkFeeMultiplierPercent uint8 = 90 + NetworkFeeUSDCents uint16 = 10 +) + +var ( + gasPriceMandatoryForChainFamily = map[string]struct{}{ + chain_selectors.FamilyAptos: {}, + chain_selectors.FamilySui: {}, + } +) + +type FeeQuoterUpdate struct { + ChainSelector uint64 + ExistingAddresses []datastore.AddressRef + ConstructorArgs fqops.ConstructorArgs + PriceUpdates fqops.PriceUpdates + DestChainConfigs []fqops.DestChainConfigArgs + TokenTransferFeeConfigUpdates fqops.ApplyTokenTransferFeeConfigUpdatesArgs + AuthorizedCallerUpdates fqops.AuthorizedCallerArgs +} + +func (fqu FeeQuoterUpdate) IsEmpty() (bool, error) { + empty := FeeQuoterUpdate{} + // marshal into JSON + emptyBytes, err := json.Marshal(empty) + if err != nil { + return false, fmt.Errorf("failed to marshal empty FeeQuoterUpdate: %w", err) + } + inputBytes, err := json.Marshal(fqu) + if err != nil { + return false, fmt.Errorf("failed to marshal FeeQuoterUpdate: %w", err) + } + return string(emptyBytes) == string(inputBytes), nil +} + +var ( + // SequenceFeeQuoterUpdate is a sequence that deploys or fetches existing FeeQuoter contract + // and does the following if the corresponding input is provided - + // 1. applies destination chain config updates + // 2. price updates + // 3. token transfer fee config updates + // 4. authorized caller updates + SequenceFeeQuoterUpdate = cldf_ops.NewSequence( + "fee-quoter-v2.0.0:update-sequence", + semver.MustParse("2.0.0"), + "Deploys or fetches existing FeeQuoter contract and applies destination chain config updates and price updates", + func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, input FeeQuoterUpdate) (output sequences.OnChainOutput, err error) { + chain, ok := chains.EVMChains()[input.ChainSelector] + if !ok { + return sequences.OnChainOutput{}, fmt.Errorf("chain with selector %d not found in environment", input.ChainSelector) + } + + // deploy fee quoter or fetch existing fee quoter address + feeQuoterRef, err := contract.MaybeDeployContract( + b, fqops.Deploy, chain, contract.DeployInput[fqops.ConstructorArgs]{ + TypeAndVersion: deployment.NewTypeAndVersion(fqops.ContractType, *fqops.Version), + ChainSelector: chain.Selector, + Args: input.ConstructorArgs, + }, input.ExistingAddresses) + if err != nil { + return sequences.OnChainOutput{}, err + } + if feeQuoterRef.Address == "" { + return sequences.OnChainOutput{}, fmt.Errorf("failed to deploy or "+ + "fetch FeeQuoter on chain %s", chain.String()) + } + writes := make([]contract.WriteOutput, 0) + output.Addresses = append(output.Addresses, feeQuoterRef) + fqAddr := common.HexToAddress(feeQuoterRef.Address) + // ApplyDestChainConfigUpdates on FeeQuoter + if len(input.DestChainConfigs) > 0 { + feeQuoterReport, err := cldf_ops.ExecuteOperation( + b, fqops.ApplyDestChainConfigUpdates, chain, + contract.FunctionInput[[]fqops.DestChainConfigArgs]{ + ChainSelector: chain.Selector, + Address: fqAddr, + Args: input.DestChainConfigs, + }) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to apply dest chain "+ + "config updates to FeeQuoter(%s) on chain %s: %w", fqAddr.Hex(), chain, err) + } + writes = append(writes, feeQuoterReport.Output) + } + // update price + if len(input.PriceUpdates.GasPriceUpdates) > 0 || len(input.PriceUpdates.TokenPriceUpdates) > 0 { + feeQuoterUpdatePricesReport, err := cldf_ops.ExecuteOperation( + b, fqops.UpdatePrices, chain, contract.FunctionInput[fqops.PriceUpdates]{ + ChainSelector: chain.Selector, + Address: fqAddr, + Args: fqops.PriceUpdates{ + GasPriceUpdates: input.PriceUpdates.GasPriceUpdates, + TokenPriceUpdates: input.PriceUpdates.TokenPriceUpdates, + }, + }) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to update gas prices on "+ + "FeeQuoter(%s) on chain %s: %w", fqAddr.Hex(), chain, err) + } + writes = append(writes, feeQuoterUpdatePricesReport.Output) + } + // TokenTransferFeeConfigUpdates on FeeQuoter + if len(input.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs) > 0 || + len(input.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs) > 0 { + feeQuoterTokenTransferFeeConfigReport, err := cldf_ops.ExecuteOperation( + b, fqops.ApplyTokenTransferFeeConfigUpdates, chain, + contract.FunctionInput[fqops.ApplyTokenTransferFeeConfigUpdatesArgs]{ + ChainSelector: chain.Selector, + Address: fqAddr, + Args: fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokenTransferFeeConfigArgs: input.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs, + TokensToUseDefaultFeeConfigs: input.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs, + }, + }) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to apply token transfer fee "+ + "config updates to FeeQuoter(%s) on chain %s: %w", fqAddr.Hex(), chain, err) + } + writes = append(writes, feeQuoterTokenTransferFeeConfigReport.Output) + } + // ApplyAuthorizedCallerUpdates on FeeQuoter + if len(input.AuthorizedCallerUpdates.AddedCallers) > 0 || + len(input.AuthorizedCallerUpdates.RemovedCallers) > 0 { + feeQuoterAuthorizedCallerReport, err := cldf_ops.ExecuteOperation( + b, fqops.ApplyAuthorizedCallerUpdates, chain, + contract.FunctionInput[fqops.AuthorizedCallerArgs]{ + ChainSelector: chain.Selector, + Address: fqAddr, + Args: input.AuthorizedCallerUpdates, + }) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to apply authorized caller "+ + "updates to FeeQuoter(%s) on chain %s: %w", fqAddr.Hex(), chain, err) + } + writes = append(writes, feeQuoterAuthorizedCallerReport.Output) + } + batch, err := contract.NewBatchOperationFromWrites(writes) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to create batch operation from writes: %w", err) + } + output.BatchOps = []mcms_types.BatchOperation{batch} + return output, nil + }, + ) + + // CreateFeeQuoterUpdateInputFromV16x creates FeeQuoterUpdate input by importing configuration from FeeQuoter v1.6.x + CreateFeeQuoterUpdateInputFromV16x = cldf_ops.NewSequence( + "fetches-feequoter-config-values-from-v1.6.x", + semver.MustParse("2.0.0"), + "Creates FeeQuoterUpdate input by importing configuration from FeeQuoter v1.6.x", + func(b cldf_ops.Bundle, chain evm.Chain, input deploy.FeeQuoterUpdateInput) (output FeeQuoterUpdate, err error) { + // check if FeeQuoter v1.6.x is present in existing addresses, if not, we return empty output + // it means there is no existing fee quoter deployed from v1.6.x deployment, and we can skip the config import from v1.6.x + fq16AddressRef, err := seq1_6.GetFeeQuoterAddress(input.ExistingAddresses, input.ChainSelector) + if err != nil { + if strings.Contains(err.Error(), "no fee quoter address found") { + return FeeQuoterUpdate{}, nil + } + return FeeQuoterUpdate{}, fmt.Errorf("failed to get FeeQuoter 1.6.x address: %w", err) + } + output.ChainSelector = input.ChainSelector + output.ExistingAddresses = input.ExistingAddresses + + // get feeQuoter 1.6 address meta + metadataForFq16, err := datastore_utils.FilterContractMetaByContractTypeAndVersion( + input.ExistingAddresses, + input.ContractMeta, + fq1_6.ContractType, + fq16AddressRef.Version, + "", + input.ChainSelector, + ) + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to get FeeQuoter 1.6.x address: %w", err) + } + if len(metadataForFq16) == 0 { + return FeeQuoterUpdate{}, fmt.Errorf("no metadata found for FeeQuoter v1.6.x on chain selector %d", input.ChainSelector) + } + if len(metadataForFq16) > 1 { + return FeeQuoterUpdate{}, fmt.Errorf("multiple metadata entries found for FeeQuoter v1.6.x on chain selector %d", input.ChainSelector) + } + // Convert metadata to typed struct if needed + fqOutput, err := datastore_utils.ConvertMetadataToType[seq1_6.FeeQuoterImportConfigSequenceOutput](metadataForFq16[0].Metadata) + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to convert metadata to "+ + "FeeQuoterImportConfigSequenceOutput for chain selector %d: %w", input.ChainSelector, err) + } + routerAddr := datastore_utils.GetAddressRef( + input.ExistingAddresses, + input.ChainSelector, + routerops.ContractType, + routerops.Version, + "", + ) + if routerAddr.Address == "" { + return FeeQuoterUpdate{}, fmt.Errorf("failed to find router address ref for chain selector %d", input.ChainSelector) + } + + // is feeQuoter going to be deployed or fetched from existing addresses? + feeQuoterRef := datastore_utils.GetAddressRef( + input.ExistingAddresses, + input.ChainSelector, + fqops.ContractType, + fqops.Version, + "", + ) + isNewFQV2Deployment := datastore_utils.IsAddressRefEmpty(feeQuoterRef) + tokenTransferFeeConfigArgs := make([]fqops.TokenTransferFeeConfigArgs, 0) + allDestChainConfigs := make([]fqops.DestChainConfigArgs, 0) + var providedRemoteChains map[uint64]struct{} + if len(input.RemoteChainSelectors) > 0 { + // initialize providedRemoteChains map if remote chains are provided in the input, + // this means we only want to import config for those remote chains from 1.6 + providedRemoteChains = make(map[uint64]struct{}) + for _, remoteChain := range input.RemoteChainSelectors { + providedRemoteChains[remoteChain] = struct{}{} + } + } + for remoteChain, cfg := range fqOutput.RemoteChainCfgs { + if !cfg.DestChainCfg.IsEnabled { + continue + } + // check if the remote chain is connected with 1.6 deployment, if not, we skip importing config for that remote chain from FQ 1.6 + // this is to safeguard against having incorrect config import from 1.6 + version, err := adapters1_2.GetLaneVersionForRemoteChain(b.GetContext(), chain, remoteChain, common.HexToAddress(routerAddr.Address)) + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to get lane version for remote chain %d: %w", remoteChain, err) + } + if version == nil || !version.Equal(semver.MustParse("1.6.0")) { + continue + } + // if remote chains are provided in the input, we only import config for those remote chains, + // otherwise we import config for all supported remote chains in 1.6 + if providedRemoteChains != nil { + if _, exists := providedRemoteChains[remoteChain]; !exists { + continue + } + } + destChainConfig := cfg.DestChainCfg + // check if gasprice stateness threashold is zero + if destChainConfig.GasPriceStalenessThreshold == 0 { + priceUpdates, err := HandleEmptyGasPriceStalenessThreshold(remoteChain, input) + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to handle empty gas price staleness threshold for remote chain %d: %w", remoteChain, err) + } + output.PriceUpdates.GasPriceUpdates = append(output.PriceUpdates.GasPriceUpdates, priceUpdates.GasPriceUpdates...) + output.PriceUpdates.TokenPriceUpdates = append(output.PriceUpdates.TokenPriceUpdates, priceUpdates.TokenPriceUpdates...) + } + outDestchainCfg := fqops.DestChainConfigArgs{ + DestChainSelector: remoteChain, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: destChainConfig.IsEnabled, + MaxDataBytes: destChainConfig.MaxDataBytes, + MaxPerMsgGasLimit: destChainConfig.MaxPerMsgGasLimit, + DestGasOverhead: destChainConfig.DestGasOverhead, + DestGasPerPayloadByteBase: destChainConfig.DestGasPerPayloadByteBase, + ChainFamilySelector: destChainConfig.ChainFamilySelector, + DefaultTokenFeeUSDCents: destChainConfig.DefaultTokenFeeUSDCents, + DefaultTokenDestGasOverhead: destChainConfig.DefaultTokenDestGasOverhead, + DefaultTxGasLimit: destChainConfig.DefaultTxGasLimit, + NetworkFeeUSDCents: uint16(destChainConfig.NetworkFeeUSDCents), + LinkFeeMultiplierPercent: LinkFeeMultiplierPercent, + }, + } + tokenTransferFeeCfgs := make([]fqops.TokenTransferFeeConfigSingleTokenArgs, 0) + for token, transferCfg := range cfg.TokenTransferFeeCfgs { + if !transferCfg.IsEnabled { + continue + } + tokenTransferFeeCfgs = append(tokenTransferFeeCfgs, fqops.TokenTransferFeeConfigSingleTokenArgs{ + Token: token, + TokenTransferFeeConfig: fqops.TokenTransferFeeConfig{ + FeeUSDCents: transferCfg.MinFeeUSDCents, + DestGasOverhead: transferCfg.DestGasOverhead, + DestBytesOverhead: transferCfg.DestBytesOverhead, + IsEnabled: transferCfg.IsEnabled, + }, + }) + } + tokenTransferFeeConfigArgs = append(tokenTransferFeeConfigArgs, fqops.TokenTransferFeeConfigArgs{ + DestChainSelector: remoteChain, + TokenTransferFeeConfigs: tokenTransferFeeCfgs, + }) + allDestChainConfigs = append(allDestChainConfigs, outDestchainCfg) + } + if isNewFQV2Deployment { + // if new deployment, adding deployer key as price updater so that + // manual gas prices can be set right after deployment if needed + output.ConstructorArgs = fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: fqOutput.StaticCfg.LinkToken, + MaxFeeJuelsPerMsg: fqOutput.StaticCfg.MaxFeeJuelsPerMsg, + }, + PriceUpdaters: append(fqOutput.PriceUpdaters, chain.DeployerKey.From), + TokenTransferFeeConfigArgs: tokenTransferFeeConfigArgs, + DestChainConfigArgs: allDestChainConfigs, + } + } else { + output.AuthorizedCallerUpdates = fqops.AuthorizedCallerArgs{ + AddedCallers: fqOutput.PriceUpdaters, + } + output.TokenTransferFeeConfigUpdates = fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokenTransferFeeConfigArgs: tokenTransferFeeConfigArgs, + } + output.DestChainConfigs = allDestChainConfigs + } + return output, nil + }) + + // CreateFeeQuoterUpdateInputFromV150 creates FeeQuoterUpdate input by importing configuration from PriceRegistry v1.5.0 and EVM2EVMOnRamp v1.5.0 + CreateFeeQuoterUpdateInputFromV150 = cldf_ops.NewSequence( + "fetches-feequoter-config-values-from-v1.5.0", + semver.MustParse("2.0.0"), + "Creates FeeQuoterUpdate input by importing configuration from PriceRegistry v1.5.0 and EVM2EVMOnRamp v1.5.0", + func(b cldf_ops.Bundle, chain evm.Chain, input deploy.FeeQuoterUpdateInput) (output FeeQuoterUpdate, err error) { + // get address ref for onramp 1.5.0 + onRampRef := datastore_utils.GetAddressRef( + input.ExistingAddresses, + input.ChainSelector, + onrampops.ContractType, + onrampops.Version, + "", + ) + // if there is no address ref for onRamp 1.5.0, it means onRamp 1.5.0 is not deployed and we can skip the config import from onRamp 1.5.0 + if datastore_utils.IsAddressRefEmpty(onRampRef) { + return FeeQuoterUpdate{}, nil + } + // get address meta for onRamp 1.5.0 to read the config values from onRamp 1.5.0 + onRampMetadata, err := datastore_utils.FilterContractMetaByContractTypeAndVersion( + input.ExistingAddresses, + input.ContractMeta, + onrampops.ContractType, + onrampops.Version, + "", + input.ChainSelector, + ) + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to get EVM2EVMOnRamp v1.5.0 address: %w", err) + } + if len(onRampMetadata) == 0 { + return FeeQuoterUpdate{}, fmt.Errorf("no metadata found for EVM2EVMOnRamp v1.5.0 on chain selector %d", input.ChainSelector) + } + routerAddr := datastore_utils.GetAddressRef( + input.ExistingAddresses, + input.ChainSelector, + routerops.ContractType, + routerops.Version, + "", + ) + if routerAddr.Address == "" { + return FeeQuoterUpdate{}, fmt.Errorf("failed to find router address ref for chain selector %d", input.ChainSelector) + } + // get the commit stores and that will act like price updaters for fee quoter + var commitStoreRefs []datastore.AddressRef + for _, addressRef := range input.ExistingAddresses { + if addressRef.Type == "CommitStore" && + addressRef.Version.Equal(semver.MustParse("1.5.0")) && + addressRef.ChainSelector == input.ChainSelector { + commitStoreRefs = append(commitStoreRefs, addressRef) + } + } + + if len(commitStoreRefs) == 0 { + return FeeQuoterUpdate{}, fmt.Errorf("failed to get commit store ref for chain %d", input.ChainSelector) + } + var priceUpdaters []common.Address + for _, ref := range commitStoreRefs { + priceUpdaters = append(priceUpdaters, common.HexToAddress(ref.Address)) + } + output.ChainSelector = input.ChainSelector + output.ExistingAddresses = input.ExistingAddresses + // is feeQuoter going to be deployed or fetched from existing addresses? + feeQuoterV2Ref := datastore_utils.GetAddressRef( + input.ExistingAddresses, + input.ChainSelector, + fqops.ContractType, + fqops.Version, + "", + ) + isNewFQv2Deployment := datastore_utils.IsAddressRefEmpty(feeQuoterV2Ref) + + var staticCfg fqops.StaticConfig + var destChainCfgs []fqops.DestChainConfigArgs + var tokenTransferFeeConfigArgsForAll []fqops.TokenTransferFeeConfigArgs + var providedRemoteChains map[uint64]struct{} + if len(input.RemoteChainSelectors) > 0 { + // initialize providedRemoteChains map if remote chain selectors are provided in the input, + // so that we can check against this map when importing config for each remote chain from onRamp 1.5.0 + providedRemoteChains = make(map[uint64]struct{}) + for _, remoteChain := range input.RemoteChainSelectors { + providedRemoteChains[remoteChain] = struct{}{} + } + } + for _, meta := range onRampMetadata { + var tokenTransferFeeConfigArgs []fqops.TokenTransferFeeConfigSingleTokenArgs + + // Convert metadata to typed struct if needed + onRampCfg, err := datastore_utils.ConvertMetadataToType[seq1_5.OnRampImportConfigSequenceOutput](meta.Metadata) + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to convert metadata to "+ + "OnRampImportConfigSequenceOutput for chain selector %d: %w", input.ChainSelector, err) + } + remoteChain := onRampCfg.RemoteChainSelector + // check if the remote chain is connected with 1.5 deployment, if not, we skip importing config for that remote chain from OnRamp 1.5 + // this is to safeguard against having incorrect config import from 1.5 + version, err := adapters1_2.GetLaneVersionForRemoteChain(b.GetContext(), chain, remoteChain, common.HexToAddress(routerAddr.Address)) + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to get lane version for remote chain %d: %w", remoteChain, err) + } + if version == nil || !version.Equal(semver.MustParse("1.5.0")) { + continue + } + // if remote chains are provided in the input, we only import config for those remote chains, + // otherwise we import config for all supported remote chains in the 1.5 + if providedRemoteChains != nil { + if _, exists := providedRemoteChains[remoteChain]; !exists { + continue + } + } + if staticCfg.LinkToken == (common.Address{}) { + staticCfg = fqops.StaticConfig{ + LinkToken: onRampCfg.StaticConfig.LinkToken, + MaxFeeJuelsPerMsg: onRampCfg.StaticConfig.MaxNopFeesJuels, + } + } + chainFamilySelector := utils.GetSelectorHex(onRampCfg.RemoteChainSelector) + + destChainCfgs = append(destChainCfgs, fqops.DestChainConfigArgs{ + DestChainSelector: onRampCfg.RemoteChainSelector, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: onRampCfg.DynamicConfig.MaxDataBytes, + MaxPerMsgGasLimit: onRampCfg.DynamicConfig.MaxPerMsgGasLimit, + DestGasOverhead: onRampCfg.DynamicConfig.DestGasOverhead, + DestGasPerPayloadByteBase: uint8(onRampCfg.DynamicConfig.DestGasPerPayloadByte), + ChainFamilySelector: chainFamilySelector, + DefaultTokenFeeUSDCents: onRampCfg.DynamicConfig.DefaultTokenFeeUSDCents, + DefaultTokenDestGasOverhead: onRampCfg.DynamicConfig.DefaultTokenDestGasOverhead, + DefaultTxGasLimit: uint32(onRampCfg.StaticConfig.DefaultTxGasLimit), + NetworkFeeUSDCents: NetworkFeeUSDCents, + LinkFeeMultiplierPercent: LinkFeeMultiplierPercent, + }, + }) + for token, tokenCfg := range onRampCfg.TokenTransferFeeConfig { + tokenTransferFeeConfigArgs = append(tokenTransferFeeConfigArgs, fqops.TokenTransferFeeConfigSingleTokenArgs{ + Token: token, + TokenTransferFeeConfig: fqops.TokenTransferFeeConfig{ + FeeUSDCents: tokenCfg.MinFeeUSDCents, + DestGasOverhead: tokenCfg.DestGasOverhead, + DestBytesOverhead: tokenCfg.DestBytesOverhead, + IsEnabled: tokenCfg.IsEnabled, + }, + }) + } + tokenTransferFeeConfigArgsForAll = append(tokenTransferFeeConfigArgsForAll, fqops.TokenTransferFeeConfigArgs{ + DestChainSelector: onRampCfg.RemoteChainSelector, + TokenTransferFeeConfigs: tokenTransferFeeConfigArgs, + }) + } + if isNewFQv2Deployment { + output.ConstructorArgs = fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: staticCfg.LinkToken, + MaxFeeJuelsPerMsg: staticCfg.MaxFeeJuelsPerMsg, + }, + DestChainConfigArgs: destChainCfgs, + TokenTransferFeeConfigArgs: tokenTransferFeeConfigArgsForAll, + PriceUpdaters: append(priceUpdaters, chain.DeployerKey.From), + } + } else { + output.DestChainConfigs = destChainCfgs + output.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs = tokenTransferFeeConfigArgsForAll + output.AuthorizedCallerUpdates = fqops.AuthorizedCallerArgs{ + AddedCallers: priceUpdaters, + } + } + return output, nil + }) +) + +// MergeFeeQuoterUpdateOutputs merges FeeQuoterUpdate outputs from the v1.6.x and v1.5.0 import +// sequences into a single update. output16 is the base; output15 supplements it. Where both +// provide values (e.g. ConstructorArgs, dest chain configs, token transfer fee configs), +// output16 takes precedence and output15 fills in only missing entries. +func MergeFeeQuoterUpdateOutputs(output16, output15 FeeQuoterUpdate) (FeeQuoterUpdate, error) { + result := output16 + empty16, err := output16.IsEmpty() + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to check if output16 is empty: %w", err) + } + empty15, err := output15.IsEmpty() + if err != nil { + return FeeQuoterUpdate{}, fmt.Errorf("failed to check if output15 is empty: %w", err) + } + + if empty16 && empty15 { + return FeeQuoterUpdate{}, nil + } + + // if output16 is empty, we can just return output15 + if empty16 { + return output15, nil + } + // if output15 is empty, we can just return output16 + if empty15 { + return output16, nil + } + // ConstructorArgs: use output15 if output16 is empty + if IsConstructorArgsEmpty(result.ConstructorArgs) { + result.ConstructorArgs = output15.ConstructorArgs + } else { + // merge the dest chainConfig args + result.ConstructorArgs.DestChainConfigArgs = mergeDestChainConfigs( + result.ConstructorArgs.DestChainConfigArgs, + output15.ConstructorArgs.DestChainConfigArgs) + + resultPriceUpdatersMap := make(map[common.Address]bool) + for _, updater := range output15.ConstructorArgs.PriceUpdaters { + resultPriceUpdatersMap[updater] = true + } + for _, updater := range output16.ConstructorArgs.PriceUpdaters { + resultPriceUpdatersMap[updater] = true + } + + result.ConstructorArgs.PriceUpdaters = maps.Keys(resultPriceUpdatersMap) + + result.ConstructorArgs.TokenTransferFeeConfigArgs = mergeTokenTransferFeeConfigArgs( + result.ConstructorArgs.TokenTransferFeeConfigArgs, + output15.ConstructorArgs.TokenTransferFeeConfigArgs) + } + + result.DestChainConfigs = mergeDestChainConfigs(result.DestChainConfigs, output15.DestChainConfigs) + + // TokenTransferFeeConfigUpdates: merge by DestChainSelector, output16 takes precedence for duplicates + result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs = mergeTokenTransferFeeConfigArgs( + result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs, + output15.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs) + + // TokensToUseDefaultFeeConfigs: merge by DestChainSelector and Token + if len(result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs) == 0 { + result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs = output15.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs + } else { + // Create a map of (DestChainSelector, Token) pairs from output16 + tokenRemoveMap := make(map[string]bool) + for _, cfg := range result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs { + key := fmt.Sprintf("%d:%s", cfg.DestChainSelector, cfg.Token.Hex()) + tokenRemoveMap[key] = true + } + // Add configs from output15 that don't exist in output16 + for _, cfg := range output15.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs { + key := fmt.Sprintf("%d:%s", cfg.DestChainSelector, cfg.Token.Hex()) + if !tokenRemoveMap[key] { + result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs = append(result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs, cfg) + } + // If it exists in both, output16's value is already used (takes precedence) + } + } + + // AuthorizedCallerUpdates: merge unique entries from both outputs + result.AuthorizedCallerUpdates = mergePriceUpdaters(result.AuthorizedCallerUpdates, output15.AuthorizedCallerUpdates) + + return result, nil +} + +func mergeTokenTransferFeeConfigArgs(args1, args2 []fqops.TokenTransferFeeConfigArgs) []fqops.TokenTransferFeeConfigArgs { + result := args1 + // TokenTransferFeeConfigArgs: merge by DestChainSelector + if len(result) == 0 { + result = args2 + } else { + // Create a map of dest chain selectors from output16 + tokenConfigMap := make(map[uint64]int) + for i, cfg := range result { + tokenConfigMap[cfg.DestChainSelector] = i + } + // Add configs from output15 that don't exist in output16 + for _, cfg := range args2 { + if _, exists := tokenConfigMap[cfg.DestChainSelector]; !exists && len(cfg.TokenTransferFeeConfigs) > 0 { + result = append(result, cfg) + } + // If it exists in both, output16's value is already used (takes precedence) + } + } + return result +} + +func mergePriceUpdaters(updaters1, updaters2 fqops.AuthorizedCallerArgs) fqops.AuthorizedCallerArgs { + result := updaters1 + // AddedCallers: merge unique addresses from both outputs + addedCallersMap := make(map[common.Address]bool) + for _, addr := range result.AddedCallers { + addedCallersMap[addr] = true + } + for _, addr := range updaters2.AddedCallers { + if !addedCallersMap[addr] { + result.AddedCallers = append(result.AddedCallers, addr) + addedCallersMap[addr] = true + } + } + // RemovedCallers: merge unique addresses from both outputs + removedCallersMap := make(map[common.Address]bool) + for _, addr := range result.RemovedCallers { + removedCallersMap[addr] = true + } + for _, addr := range updaters2.RemovedCallers { + if !removedCallersMap[addr] { + result.RemovedCallers = append(result.RemovedCallers, addr) + removedCallersMap[addr] = true + } + } + return result +} + +// mergeDestChainConfigs merges two slices of DestChainConfigArgs, giving precedence to the first slice in case of duplicate DestChainSelectors. +func mergeDestChainConfigs(cfgs1, cfgs2 []fqops.DestChainConfigArgs) []fqops.DestChainConfigArgs { + result := cfgs1 + + // Create a map of dest chain selectors from cfgs1 which will be skipped when adding from cfgs2 + destChainMap := make(map[uint64]fqops.DestChainConfigArgs) + for _, cfg := range cfgs1 { + destChainMap[cfg.DestChainSelector] = cfg + } + + // Add configs from cfgs2 that don't exist in cfgs1 + for _, cfg := range cfgs2 { + if _, exists := destChainMap[cfg.DestChainSelector]; !exists { + result = append(result, cfg) + } + // If it exists in both, cfgs1's value is already used (takes precedence) + } + + return result +} + +func IsConstructorArgsEmpty(a fqops.ConstructorArgs) bool { + return (a.StaticConfig == fqops.StaticConfig{}) && + len(a.PriceUpdaters) == 0 && + len(a.TokenTransferFeeConfigArgs) == 0 && + len(a.DestChainConfigArgs) == 0 +} + +// HandleEmptyGasPriceStalenessThreshold handles the case when GasPriceStalenessThreshold is zero for a remote chain. +// It checks if gas price needs to be set manually for the chain family ( mainly for aptos and sui), +// if yes it - +// +// - looks for gas price for that remote chain in the input additional config and adds it to the price updates output; +// - throws an error if gas price for that remote chain is not provided in the input additional config +// because gas price staleness threshold cannot be zero without providing gas price for chains that need manual gas price. +// +// if not, it returns empty price updates output because gas price does not need to be manually set for that remote chain. +// It is exported for testing. +func HandleEmptyGasPriceStalenessThreshold(remoteChain uint64, input deploy.FeeQuoterUpdateInput) (output fqops.PriceUpdates, err error) { + // check if gasprice can be set manually for the chain family, + // if not, we return an error because gas price staleness threshold cannot be zero + // for chains that do not have manual gas price + chainFamily, err := chain_selectors.GetSelectorFamily(remoteChain) + if err != nil { + return fqops.PriceUpdates{}, fmt.Errorf("failed to get chain family for remote chain %d: %w", remoteChain, err) + } + _, exists := gasPriceMandatoryForChainFamily[chainFamily] + // if manual gas price is not mandatory for the chain family but gas price staleness threshold is zero, + // we can skip setting gas price for that remote chain and return empty price updates + if !exists { + return fqops.PriceUpdates{}, nil + } + if input.AdditionalConfig == nil || input.AdditionalConfig.GasPricesPerRemoteChain == nil { + return fqops.PriceUpdates{}, fmt.Errorf("gas price staleness threshold is zero for remote chain %d, "+ + "please provide gas price for this remote chain in the input additional config", remoteChain) + } + if gaspriceStr, ok := input.AdditionalConfig.GasPricesPerRemoteChain[remoteChain]; ok { + gasprice, success := new(big.Int).SetString(gaspriceStr, 10) + if !success { + return fqops.PriceUpdates{}, fmt.Errorf("invalid gas price %s for remote chain %d in input additional config", gaspriceStr, remoteChain) + } + output.GasPriceUpdates = append(output.GasPriceUpdates, fqops.GasPriceUpdate{ + DestChainSelector: remoteChain, + UsdPerUnitGas: gasprice, + }) + return output, nil + } + return fqops.PriceUpdates{}, fmt.Errorf("gas price staleness threshold is zero for remote chain %d, "+ + "please provide gas price for this remote chain in the input additional config", remoteChain) +} diff --git a/chains/evm/deployment/v2_0_0/sequences/fee_quoter_test.go b/chains/evm/deployment/v2_0_0/sequences/fee_quoter_test.go new file mode 100644 index 0000000000..41b047c210 --- /dev/null +++ b/chains/evm/deployment/v2_0_0/sequences/fee_quoter_test.go @@ -0,0 +1,414 @@ +package sequences_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + fqops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/operations/fee_quoter" + "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/sequences" +) + +func TestFeeQuoterUpdate_IsEmpty(t *testing.T) { + empty := sequences.FeeQuoterUpdate{} + isEmpty, err := empty.IsEmpty() + require.NoError(t, err) + require.True(t, isEmpty, "Empty FeeQuoterUpdate should return true") + + nonEmpty := sequences.FeeQuoterUpdate{ + ChainSelector: 1, + } + isEmpty, err = nonEmpty.IsEmpty() + require.NoError(t, err) + require.False(t, isEmpty, "Non-empty FeeQuoterUpdate should return false") +} + +func TestMergeFeeQuoterUpdateOutputs(t *testing.T) { + addr1 := common.HexToAddress("0x1111111111111111111111111111111111111111") + addr2 := common.HexToAddress("0x2222222222222222222222222222222222222222") + addr3 := common.HexToAddress("0x3333333333333333333333333333333333333333") + addr4 := common.HexToAddress("0x4444444444444444444444444444444444444444") + addr5 := common.HexToAddress("0x5555555555555555555555555555555555555555") + + t.Run("empty outputs", func(t *testing.T) { + output16 := sequences.FeeQuoterUpdate{} + output15 := sequences.FeeQuoterUpdate{} + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + require.Equal(t, sequences.FeeQuoterUpdate{}, result) + }) + + t.Run("ConstructorArgs - output15 used when output16 is empty", func(t *testing.T) { + linkToken := common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA") + maxFeeJuelsPerMsg := big.NewInt(1000000000000000000) // 1 LINK + output16 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{}, // empty + } + output15 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken, + MaxFeeJuelsPerMsg: maxFeeJuelsPerMsg, + }, + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + require.Equal(t, linkToken, result.ConstructorArgs.StaticConfig.LinkToken) + require.Equal(t, maxFeeJuelsPerMsg, result.ConstructorArgs.StaticConfig.MaxFeeJuelsPerMsg) + }) + + t.Run("ConstructorArgs - output16 takes precedence when not empty", func(t *testing.T) { + linkToken16 := common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA") + linkToken15 := common.HexToAddress("0x326C977E6efc84E512bB9C30f76E30c160eD06FB") + maxFeeJuelsPerMsg16 := big.NewInt(2000000000000000000) // 2 LINK + maxFeeJuelsPerMsg15 := big.NewInt(1000000000000000000) // 1 LINK + output16 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken16, + MaxFeeJuelsPerMsg: maxFeeJuelsPerMsg16, + }, + }, + } + output15 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken15, + MaxFeeJuelsPerMsg: maxFeeJuelsPerMsg15, + }, + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + // output16's StaticConfig should be used (takes precedence) + require.Equal(t, linkToken16, result.ConstructorArgs.StaticConfig.LinkToken) + require.Equal(t, maxFeeJuelsPerMsg16, result.ConstructorArgs.StaticConfig.MaxFeeJuelsPerMsg) + }) + + t.Run("ConstructorArgs - merge DestChainConfig,PriceUpdaters TokenTransferFeeConfigArgs", func(t *testing.T) { + output16 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + MaxFeeJuelsPerMsg: big.NewInt(2000000000000000000), // 2 LINK + }, + PriceUpdaters: []common.Address{addr1, addr2}, + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 100, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr1}, + }, + }, + }, + DestChainConfigArgs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 100, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 1000, + }, + }, + }, + }, + } + output15 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: common.HexToAddress("0x326C977E6efc84E512bB9C30f76E30c160eD06FB"), + MaxFeeJuelsPerMsg: big.NewInt(1000000000000000000), // 1 LINK + }, + PriceUpdaters: []common.Address{addr2, addr3}, // addr2 is duplicate + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 100, // duplicate selector + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr2}, + }, + }, + { + DestChainSelector: 200, // unique selector + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr3}, + }, + }, + }, + DestChainConfigArgs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 100, // duplicate selector + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: false, + MaxDataBytes: 2000, + }, + }, + { + DestChainSelector: 200, // unique selector + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 3000, + }, + }, + }, + }, + } + expected := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + MaxFeeJuelsPerMsg: big.NewInt(2000000000000000000), // from output16 + }, + PriceUpdaters: []common.Address{addr1, addr2, addr3}, // merged with duplicates removed + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 100, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr1}, // from output16 (takes precedence) + }, + }, + { + DestChainSelector: 200, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr3}, // from output15 + }, + }, + }, + DestChainConfigArgs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 100, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 1000, + }, + }, + { + DestChainSelector: 200, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 3000, + }, + }, + }, + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + require.Equal(t, expected.ConstructorArgs.StaticConfig, result.ConstructorArgs.StaticConfig) + require.ElementsMatch(t, expected.ConstructorArgs.PriceUpdaters, result.ConstructorArgs.PriceUpdaters) + require.Len(t, result.ConstructorArgs.TokenTransferFeeConfigArgs, 2) + require.Equal(t, uint64(100), result.ConstructorArgs.TokenTransferFeeConfigArgs[0].DestChainSelector) + require.Equal(t, addr1, result.ConstructorArgs.TokenTransferFeeConfigArgs[0].TokenTransferFeeConfigs[0].Token) // from output16 + require.Equal(t, uint64(200), result.ConstructorArgs.TokenTransferFeeConfigArgs[1].DestChainSelector) + require.Equal(t, addr3, result.ConstructorArgs.TokenTransferFeeConfigArgs[1].TokenTransferFeeConfigs[0].Token) // from output15 + require.Len(t, result.ConstructorArgs.DestChainConfigArgs, 2) + require.Equal(t, uint64(100), result.ConstructorArgs.DestChainConfigArgs[0].DestChainSelector) + require.True(t, result.ConstructorArgs.DestChainConfigArgs[0].DestChainConfig.IsEnabled) // from output16 + require.Equal(t, uint32(1000), result.ConstructorArgs.DestChainConfigArgs[0].DestChainConfig.MaxDataBytes) // from output16 + require.Equal(t, uint64(200), result.ConstructorArgs.DestChainConfigArgs[1].DestChainSelector) + require.True(t, result.ConstructorArgs.DestChainConfigArgs[1].DestChainConfig.IsEnabled) // from output15 + require.Equal(t, uint32(3000), result.ConstructorArgs.DestChainConfigArgs[1].DestChainConfig.MaxDataBytes) // from output15 + }) + + t.Run("DestChainConfigs - output16 takes precedence for duplicates", func(t *testing.T) { + output16 := sequences.FeeQuoterUpdate{ + DestChainConfigs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 100, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 1000, + }, + }, + }, + } + output15 := sequences.FeeQuoterUpdate{ + DestChainConfigs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 100, // duplicate selector + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: false, + MaxDataBytes: 2000, + }, + }, + { + DestChainSelector: 200, // unique selector + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 3000, + }, + }, + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + require.Len(t, result.DestChainConfigs, 2) + // output16's config for selector 100 should be used + require.Equal(t, uint64(100), result.DestChainConfigs[0].DestChainSelector) + require.True(t, result.DestChainConfigs[0].DestChainConfig.IsEnabled) + require.Equal(t, uint32(1000), result.DestChainConfigs[0].DestChainConfig.MaxDataBytes) + // output15's config for selector 200 should be added + require.Equal(t, uint64(200), result.DestChainConfigs[1].DestChainSelector) + require.Equal(t, uint32(3000), result.DestChainConfigs[1].DestChainConfig.MaxDataBytes) + }) + + t.Run("TokenTransferFeeConfigArgs - output16 takes precedence for duplicates", func(t *testing.T) { + output16 := sequences.FeeQuoterUpdate{ + TokenTransferFeeConfigUpdates: fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 100, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr1}, + }, + }, + }, + }, + } + output15 := sequences.FeeQuoterUpdate{ + TokenTransferFeeConfigUpdates: fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 100, // duplicate + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr2}, + }, + }, + { + DestChainSelector: 200, // unique + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + {Token: addr3}, + }, + }, + }, + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + require.Len(t, result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs, 2) + // output16's config for selector 100 should be used + require.Equal(t, uint64(100), result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs[0].DestChainSelector) + require.Equal(t, addr1, result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs[0].TokenTransferFeeConfigs[0].Token) + // output15's config for selector 200 should be added + require.Equal(t, uint64(200), result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs[1].DestChainSelector) + require.Equal(t, addr3, result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs[1].TokenTransferFeeConfigs[0].Token) + }) + + t.Run("TokensToUseDefaultFeeConfigs - merge by DestChainSelector and Token", func(t *testing.T) { + output16 := sequences.FeeQuoterUpdate{ + TokenTransferFeeConfigUpdates: fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokensToUseDefaultFeeConfigs: []fqops.TokenTransferFeeConfigRemoveArgs{ + {DestChainSelector: 100, Token: addr1}, + {DestChainSelector: 100, Token: addr2}, + }, + }, + } + output15 := sequences.FeeQuoterUpdate{ + TokenTransferFeeConfigUpdates: fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokensToUseDefaultFeeConfigs: []fqops.TokenTransferFeeConfigRemoveArgs{ + {DestChainSelector: 100, Token: addr2}, // duplicate + {DestChainSelector: 200, Token: addr3}, // unique + }, + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + require.Len(t, result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs, 3) + // Verify all expected entries are present + require.Contains(t, result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs, + fqops.TokenTransferFeeConfigRemoveArgs{DestChainSelector: 100, Token: addr1}) + require.Contains(t, result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs, + fqops.TokenTransferFeeConfigRemoveArgs{DestChainSelector: 100, Token: addr2}) + require.Contains(t, result.TokenTransferFeeConfigUpdates.TokensToUseDefaultFeeConfigs, + fqops.TokenTransferFeeConfigRemoveArgs{DestChainSelector: 200, Token: addr3}) + }) + + t.Run("AuthorizedCallerUpdates - merge unique entries", func(t *testing.T) { + output16 := sequences.FeeQuoterUpdate{ + AuthorizedCallerUpdates: fqops.AuthorizedCallerArgs{ + AddedCallers: []common.Address{addr1, addr2}, + RemovedCallers: []common.Address{addr3}, + }, + } + output15 := sequences.FeeQuoterUpdate{ + AuthorizedCallerUpdates: fqops.AuthorizedCallerArgs{ + AddedCallers: []common.Address{addr2, addr4}, // addr2 is duplicate + RemovedCallers: []common.Address{addr3, addr5}, // addr3 is duplicate + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + require.Len(t, result.AuthorizedCallerUpdates.AddedCallers, 3) + require.Contains(t, result.AuthorizedCallerUpdates.AddedCallers, addr1) + require.Contains(t, result.AuthorizedCallerUpdates.AddedCallers, addr2) + require.Contains(t, result.AuthorizedCallerUpdates.AddedCallers, addr4) + require.Len(t, result.AuthorizedCallerUpdates.RemovedCallers, 2) + require.Contains(t, result.AuthorizedCallerUpdates.RemovedCallers, addr3) + require.Contains(t, result.AuthorizedCallerUpdates.RemovedCallers, addr5) + }) + + t.Run("comprehensive merge", func(t *testing.T) { + linkToken16 := common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA") + linkToken15 := common.HexToAddress("0x326C977E6efc84E512bB9C30f76E30c160eD06FB") + maxFeeJuelsPerMsg16 := big.NewInt(2000000000000000000) // 2 LINK + maxFeeJuelsPerMsg15 := big.NewInt(1000000000000000000) // 1 LINK + output16 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken16, + MaxFeeJuelsPerMsg: maxFeeJuelsPerMsg16, + }, + }, + DestChainConfigs: []fqops.DestChainConfigArgs{ + {DestChainSelector: 200, DestChainConfig: fqops.DestChainConfig{IsEnabled: true}}, + }, + TokenTransferFeeConfigUpdates: fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + {DestChainSelector: 400, TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{{Token: addr1}}}, + }, + }, + AuthorizedCallerUpdates: fqops.AuthorizedCallerArgs{ + AddedCallers: []common.Address{addr1}, + }, + } + output15 := sequences.FeeQuoterUpdate{ + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken15, + MaxFeeJuelsPerMsg: maxFeeJuelsPerMsg15, + }, + }, + DestChainConfigs: []fqops.DestChainConfigArgs{ + {DestChainSelector: 200, DestChainConfig: fqops.DestChainConfig{IsEnabled: false}}, // duplicate + {DestChainSelector: 300, DestChainConfig: fqops.DestChainConfig{IsEnabled: true}}, // unique + }, + TokenTransferFeeConfigUpdates: fqops.ApplyTokenTransferFeeConfigUpdatesArgs{ + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + {DestChainSelector: 400, TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{{Token: addr2}}}, // duplicate + {DestChainSelector: 500, TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{{Token: addr3}}}, // unique + }, + }, + AuthorizedCallerUpdates: fqops.AuthorizedCallerArgs{ + AddedCallers: []common.Address{addr2, addr3}, + }, + } + result, err := sequences.MergeFeeQuoterUpdateOutputs(output16, output15) + require.NoError(t, err) + // ConstructorArgs from output16 (not empty) - StaticConfig takes precedence + require.Equal(t, linkToken16, result.ConstructorArgs.StaticConfig.LinkToken) + require.Equal(t, maxFeeJuelsPerMsg16, result.ConstructorArgs.StaticConfig.MaxFeeJuelsPerMsg) + // DestChainConfigs: output16's config for 200, plus output15's config for 300 + require.Len(t, result.DestChainConfigs, 2) + require.True(t, result.DestChainConfigs[0].DestChainConfig.IsEnabled) // from output16 + // TokenTransferFeeConfigArgs: output16's config for 400, plus output15's config for 500 + require.Len(t, result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs, 2) + require.Equal(t, uint64(400), result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs[0].DestChainSelector) + require.Equal(t, addr1, result.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs[0].TokenTransferFeeConfigs[0].Token) // from output16 + // AuthorizedCallerUpdates: merged unique entries + require.Len(t, result.AuthorizedCallerUpdates.AddedCallers, 3) + require.Contains(t, result.AuthorizedCallerUpdates.AddedCallers, addr1) + require.Contains(t, result.AuthorizedCallerUpdates.AddedCallers, addr2) + require.Contains(t, result.AuthorizedCallerUpdates.AddedCallers, addr3) + }) +} diff --git a/chains/evm/deployment/v2_0_0/sequences/sequence_fee_quoter_input_creation_test.go b/chains/evm/deployment/v2_0_0/sequences/sequence_fee_quoter_input_creation_test.go new file mode 100644 index 0000000000..bdcaf15578 --- /dev/null +++ b/chains/evm/deployment/v2_0_0/sequences/sequence_fee_quoter_input_creation_test.go @@ -0,0 +1,1167 @@ +package sequences_test + +import ( + "math/big" + "sort" + "testing" + + "github.com/Masterminds/semver/v3" + + "github.com/ethereum/go-ethereum/common" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + utils2 "github.com/smartcontractkit/chainlink-evm/pkg/utils" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + "github.com/smartcontractkit/chainlink-deployments-framework/engine/test/environment" + cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/utils/operations/contract" + routerops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_2_0/operations/router" + onrampv1_5ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/operations/onramp" + seq1_5 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_5_0/sequences" + fee_quoter_v1_6_0 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/fee_quoter" + onrampv1_6ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/onramp" + seq1_6 "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/sequences" + evmadapter "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/adapters" + fqops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/operations/fee_quoter" + "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/sequences" + "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_2_0/router" + evm_2_evm_onramp_v1_5_0 "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_5_0/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils" + dseq "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" +) + +// dummyAddressRefs is hardcoded address refs (previously from address_refs.json). +// Chain selectors must match dummyContractMetadata so metadata lookup succeeds. +var dummyAddressRefs = []datastore.AddressRef{ + {Address: "0x1111111111111111111111111111111111111111", ChainSelector: 5009297550715157269, Type: datastore.ContractType("FeeQuoter"), Version: semver.MustParse("1.6.3")}, + {Address: "0x6666666666666666666666666666666666666666", ChainSelector: 5009297550715157269, Type: datastore.ContractType("EVM2EVMOnRamp"), Version: semver.MustParse("1.5.0")}, + {Address: "0x2222222222222222222222222222222222222221", ChainSelector: 5009297550715157269, Type: datastore.ContractType("CommitStore"), Version: semver.MustParse("1.5.0")}, + {Address: "0x9999999999999999999999999999999999999999", ChainSelector: 4949039107694359620, Type: datastore.ContractType("CommitStore"), Version: semver.MustParse("1.5.0"), Qualifier: "commitstore1"}, + {Address: "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", ChainSelector: 4949039107694359620, Type: datastore.ContractType("FeeQuoter"), Version: semver.MustParse("1.6.0")}, + {Address: "0x1010101010101010101010101010101010101010", ChainSelector: 4949039107694359620, Type: datastore.ContractType("EVM2EVMOnRamp"), Version: semver.MustParse("1.5.0")}, + {Address: "0x3333333333333333333333333333333333333333", ChainSelector: 4949039107694359620, Type: datastore.ContractType("CommitStore"), Version: semver.MustParse("1.5.0"), Qualifier: "commitstore2"}, + {Address: "0x5050505050505050505050505050505050505050", ChainSelector: 15971525489660198786, Type: datastore.ContractType("EVM2EVMOnRamp"), Version: semver.MustParse("1.5.0")}, + {Address: "0x4444444444444444444444444444444444444444", ChainSelector: 15971525489660198786, Type: datastore.ContractType("CommitStore"), Version: semver.MustParse("1.5.0")}, + {Address: "0x6060606060606060606060606060606060606060", ChainSelector: 5936861837188149645, Type: datastore.ContractType("FeeQuoter"), Version: semver.MustParse("1.6.3")}, + {Address: "0x7070707070707070707070707070707070707070", ChainSelector: 5936861837188149645, Type: datastore.ContractType("EVM2EVMOnRamp"), Version: semver.MustParse("1.5.0")}, + {Address: "0x5555555555555555555555555555555555555551", ChainSelector: 5936861837188149645, Type: datastore.ContractType("CommitStore"), Version: semver.MustParse("1.5.0")}, +} + +var dummyContractMetadata = []datastore.ContractMetadata{ + { + Address: "0x1111111111111111111111111111111111111111", + ChainSelector: 5009297550715157269, + Metadata: seq1_6.FeeQuoterImportConfigSequenceOutput{ + RemoteChainCfgs: map[uint64]seq1_6.FeeQuoterImportConfigSequenceOutputPerRemoteChain{ + 15971525489660198786: { + DestChainCfg: fee_quoter_v1_6_0.DestChainConfig{ + IsEnabled: true, + MaxNumberOfTokensPerMsg: 3, + MaxDataBytes: 8000, + MaxPerMsgGasLimit: 4000000, + DestGasOverhead: 80000, + DestGasPerPayloadByteBase: 14, + DestGasPerPayloadByteHigh: 28, + DestGasPerPayloadByteThreshold: 800, + DestDataAvailabilityOverheadGas: 40000, + DestGasPerDataAvailabilityByte: 8, + DestDataAvailabilityMultiplierBps: 900, + ChainFamilySelector: utils.GetSelectorHex(15971525489660198786), + EnforceOutOfOrder: false, + DefaultTokenFeeUSDCents: 8, + DefaultTokenDestGasOverhead: 40000, + DefaultTxGasLimit: 180000, + GasMultiplierWeiPerEth: 0, + GasPriceStalenessThreshold: 10, + NetworkFeeUSDCents: 10, + }, + TokenTransferFeeCfgs: map[common.Address]fee_quoter_v1_6_0.TokenTransferFeeConfig{ + common.HexToAddress("0x2222222222222222222222222222222222222222"): { + MinFeeUSDCents: 4, + MaxFeeUSDCents: 40, + DeciBps: 90, + DestGasOverhead: 25000, + DestBytesOverhead: 80, + IsEnabled: true, + }, + }, + }, + }, + StaticCfg: fee_quoter_v1_6_0.StaticConfig{ + MaxFeeJuelsPerMsg: big.NewInt(1000000000000000000), + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + TokenPriceStalenessThreshold: 3600, + }, + PriceUpdaters: []common.Address{ + common.HexToAddress("0x4444444444444444444444444444444444444444"), + common.HexToAddress("0x5555555555555555555555555555555555555555"), + }, + }, + }, + { + Address: "0x6666666666666666666666666666666666666666", + ChainSelector: 5009297550715157269, + Metadata: seq1_5.OnRampImportConfigSequenceOutput{ + RemoteChainSelector: 4949039107694359620, + StaticConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampStaticConfig{ + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + ChainSelector: 5009297550715157269, + DestChainSelector: 4949039107694359620, + DefaultTxGasLimit: 200000, + MaxNopFeesJuels: big.NewInt(1000000000000000000), + PrevOnRamp: common.HexToAddress("0x0000000000000000000000000000000000000000"), + RmnProxy: common.HexToAddress("0x7777777777777777777777777777777777777777"), + TokenAdminRegistry: common.HexToAddress("0x8888888888888888888888888888888888888888"), + }, + DynamicConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampDynamicConfig{ + Router: common.HexToAddress("0x9999999999999999999999999999999999999999"), + MaxNumberOfTokensPerMsg: 5, + DestGasOverhead: 100000, + DestGasPerPayloadByte: 16, + DestDataAvailabilityOverheadGas: 50000, + DestGasPerDataAvailabilityByte: 10, + DestDataAvailabilityMultiplierBps: 1000, + PriceRegistry: common.HexToAddress("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + EnforceOutOfOrder: false, + }, + TokenTransferFeeConfig: map[common.Address]evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampTokenTransferFeeConfig{ + common.HexToAddress("0x2222222222222222222222222222222222222222"): { + MinFeeUSDCents: 5, + MaxFeeUSDCents: 50, + DeciBps: 100, + DestGasOverhead: 30000, + DestBytesOverhead: 100, + AggregateRateLimitEnabled: false, + IsEnabled: true, + }, + common.HexToAddress("0x3333333333333333333333333333333333333333"): { + MinFeeUSDCents: 10, + MaxFeeUSDCents: 100, + DeciBps: 200, + DestGasOverhead: 40000, + DestBytesOverhead: 200, + AggregateRateLimitEnabled: false, + IsEnabled: true, + }, + }, + }, + }, + { + Address: "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + ChainSelector: 4949039107694359620, + Metadata: seq1_6.FeeQuoterImportConfigSequenceOutput{ + RemoteChainCfgs: map[uint64]seq1_6.FeeQuoterImportConfigSequenceOutputPerRemoteChain{ + 15971525489660198786: { + DestChainCfg: fee_quoter_v1_6_0.DestChainConfig{ + IsEnabled: true, + MaxNumberOfTokensPerMsg: 3, + MaxDataBytes: 8000, + MaxPerMsgGasLimit: 4000000, + DestGasOverhead: 80000, + DestGasPerPayloadByteBase: 14, + DestGasPerPayloadByteHigh: 28, + DestGasPerPayloadByteThreshold: 800, + DestDataAvailabilityOverheadGas: 40000, + DestGasPerDataAvailabilityByte: 8, + DestDataAvailabilityMultiplierBps: 900, + ChainFamilySelector: utils.GetSelectorHex(15971525489660198786), + EnforceOutOfOrder: false, + DefaultTokenFeeUSDCents: 8, + DefaultTokenDestGasOverhead: 40000, + DefaultTxGasLimit: 180000, + GasMultiplierWeiPerEth: 0, + GasPriceStalenessThreshold: 10, + NetworkFeeUSDCents: 10, + }, + TokenTransferFeeCfgs: map[common.Address]fee_quoter_v1_6_0.TokenTransferFeeConfig{ + common.HexToAddress("0x2222222222222222222222222222222222222222"): { + MinFeeUSDCents: 4, + MaxFeeUSDCents: 40, + DeciBps: 90, + DestGasOverhead: 25000, + DestBytesOverhead: 80, + IsEnabled: true, + }, + }, + }, + }, + StaticCfg: fee_quoter_v1_6_0.StaticConfig{ + MaxFeeJuelsPerMsg: big.NewInt(1000000000000000000), + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + TokenPriceStalenessThreshold: 3600, + }, + PriceUpdaters: []common.Address{ + common.HexToAddress("0x4444444444444444444444444444444444444444"), + common.HexToAddress("0x5555555555555555555555555555555555555555"), + }, + }, + }, + { + Address: "0x1010101010101010101010101010101010101010", + ChainSelector: 4949039107694359620, + Metadata: seq1_5.OnRampImportConfigSequenceOutput{ + RemoteChainSelector: 5009297550715157269, + StaticConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampStaticConfig{ + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + ChainSelector: 4949039107694359620, + DestChainSelector: 5009297550715157269, + DefaultTxGasLimit: 200000, + MaxNopFeesJuels: big.NewInt(1000000000000000000), + PrevOnRamp: common.HexToAddress("0x0000000000000000000000000000000000000000"), + RmnProxy: common.HexToAddress("0x7777777777777777777777777777777777777777"), + TokenAdminRegistry: common.HexToAddress("0x8888888888888888888888888888888888888888"), + }, + DynamicConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampDynamicConfig{ + Router: common.HexToAddress("0x9999999999999999999999999999999999999999"), + MaxNumberOfTokensPerMsg: 5, + DestGasOverhead: 100000, + DestGasPerPayloadByte: 16, + DestDataAvailabilityOverheadGas: 50000, + DestGasPerDataAvailabilityByte: 10, + DestDataAvailabilityMultiplierBps: 1000, + PriceRegistry: common.HexToAddress("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + EnforceOutOfOrder: false, + }, + TokenTransferFeeConfig: map[common.Address]evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampTokenTransferFeeConfig{ + common.HexToAddress("0x2222222222222222222222222222222222222222"): { + MinFeeUSDCents: 5, + MaxFeeUSDCents: 50, + DeciBps: 100, + DestGasOverhead: 30000, + DestBytesOverhead: 100, + AggregateRateLimitEnabled: false, + IsEnabled: true, + }, + }, + }, + }, + { + Address: "0x5050505050505050505050505050505050505050", + ChainSelector: 15971525489660198786, + Metadata: seq1_5.OnRampImportConfigSequenceOutput{ + RemoteChainSelector: 5009297550715157269, + StaticConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampStaticConfig{ + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + ChainSelector: 15971525489660198786, + DestChainSelector: 5009297550715157269, + DefaultTxGasLimit: 200000, + MaxNopFeesJuels: big.NewInt(1000000000000000000), + PrevOnRamp: common.HexToAddress("0x0000000000000000000000000000000000000000"), + RmnProxy: common.HexToAddress("0x7777777777777777777777777777777777777777"), + TokenAdminRegistry: common.HexToAddress("0x8888888888888888888888888888888888888888"), + }, + DynamicConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampDynamicConfig{ + Router: common.HexToAddress("0x9999999999999999999999999999999999999999"), + MaxNumberOfTokensPerMsg: 5, + DestGasOverhead: 100000, + DestGasPerPayloadByte: 16, + DestDataAvailabilityOverheadGas: 50000, + DestGasPerDataAvailabilityByte: 10, + DestDataAvailabilityMultiplierBps: 1000, + PriceRegistry: common.HexToAddress("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + EnforceOutOfOrder: false, + }, + TokenTransferFeeConfig: map[common.Address]evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampTokenTransferFeeConfig{ + common.HexToAddress("0x2222222222222222222222222222222222222222"): { + MinFeeUSDCents: 5, + MaxFeeUSDCents: 50, + DeciBps: 100, + DestGasOverhead: 30000, + DestBytesOverhead: 100, + AggregateRateLimitEnabled: false, + IsEnabled: true, + }, + common.HexToAddress("0x3333333333333333333333333333333333333333"): { + MinFeeUSDCents: 10, + MaxFeeUSDCents: 100, + DeciBps: 200, + DestGasOverhead: 40000, + DestBytesOverhead: 200, + AggregateRateLimitEnabled: false, + IsEnabled: true, + }, + }, + }, + }, + { + Address: "0x6060606060606060606060606060606060606060", + ChainSelector: 5936861837188149645, + Metadata: seq1_6.FeeQuoterImportConfigSequenceOutput{ + RemoteChainCfgs: map[uint64]seq1_6.FeeQuoterImportConfigSequenceOutputPerRemoteChain{ + 5009297550715157269: { + DestChainCfg: fee_quoter_v1_6_0.DestChainConfig{ + IsEnabled: true, + MaxNumberOfTokensPerMsg: 5, + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DestGasOverhead: 100000, + DestGasPerPayloadByteBase: 16, + DestGasPerPayloadByteHigh: 32, + DestGasPerPayloadByteThreshold: 1000, + DestDataAvailabilityOverheadGas: 50000, + DestGasPerDataAvailabilityByte: 10, + DestDataAvailabilityMultiplierBps: 1000, + ChainFamilySelector: utils.GetSelectorHex(5009297550715157269), + EnforceOutOfOrder: false, + DefaultTokenFeeUSDCents: 10, + DefaultTokenDestGasOverhead: 50000, + DefaultTxGasLimit: 200000, + GasMultiplierWeiPerEth: 0, + GasPriceStalenessThreshold: 0, + NetworkFeeUSDCents: 10, + }, + TokenTransferFeeCfgs: map[common.Address]fee_quoter_v1_6_0.TokenTransferFeeConfig{}, + }, + 4949039107694359620: { + DestChainCfg: fee_quoter_v1_6_0.DestChainConfig{ + IsEnabled: true, + MaxNumberOfTokensPerMsg: 4, + MaxDataBytes: 9000, + MaxPerMsgGasLimit: 4500000, + DestGasOverhead: 90000, + DestGasPerPayloadByteBase: 15, + DestGasPerPayloadByteHigh: 30, + DestGasPerPayloadByteThreshold: 900, + DestDataAvailabilityOverheadGas: 45000, + DestGasPerDataAvailabilityByte: 9, + DestDataAvailabilityMultiplierBps: 950, + ChainFamilySelector: utils.GetSelectorHex(4949039107694359620), + EnforceOutOfOrder: false, + DefaultTokenFeeUSDCents: 9, + DefaultTokenDestGasOverhead: 45000, + DefaultTxGasLimit: 190000, + GasMultiplierWeiPerEth: 0, + GasPriceStalenessThreshold: 0, + NetworkFeeUSDCents: 10, + }, + TokenTransferFeeCfgs: map[common.Address]fee_quoter_v1_6_0.TokenTransferFeeConfig{}, + }, + }, + StaticCfg: fee_quoter_v1_6_0.StaticConfig{ + MaxFeeJuelsPerMsg: big.NewInt(1000000000000000000), + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + TokenPriceStalenessThreshold: 3600, + }, + PriceUpdaters: []common.Address{ + common.HexToAddress("0x4444444444444444444444444444444444444444"), + common.HexToAddress("0x5555555555555555555555555555555555555555"), + }, + }, + }, + { + Address: "0x7070707070707070707070707070707070707070", + ChainSelector: 5936861837188149645, + Metadata: seq1_5.OnRampImportConfigSequenceOutput{ + RemoteChainSelector: 15971525489660198786, + StaticConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampStaticConfig{ + LinkToken: common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA"), + ChainSelector: 5936861837188149645, + DestChainSelector: 15971525489660198786, + DefaultTxGasLimit: 180000, + MaxNopFeesJuels: big.NewInt(900000000000000000), + PrevOnRamp: common.HexToAddress("0x0000000000000000000000000000000000000000"), + RmnProxy: common.HexToAddress("0x7777777777777777777777777777777777777777"), + TokenAdminRegistry: common.HexToAddress("0x8888888888888888888888888888888888888888"), + }, + DynamicConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampDynamicConfig{ + Router: common.HexToAddress("0x9999999999999999999999999999999999999999"), + MaxNumberOfTokensPerMsg: 3, + DestGasOverhead: 80000, + DestGasPerPayloadByte: 14, + DestDataAvailabilityOverheadGas: 40000, + DestGasPerDataAvailabilityByte: 8, + DestDataAvailabilityMultiplierBps: 900, + PriceRegistry: common.HexToAddress("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + MaxDataBytes: 8000, + MaxPerMsgGasLimit: 4000000, + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + EnforceOutOfOrder: false, + }, + TokenTransferFeeConfig: map[common.Address]evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampTokenTransferFeeConfig{}, + }, + }, +} + +// validMaxFeeJuelsPerMsgFromMetadata returns the set of valid MaxFeeJuelsPerMsg for a chain +// by collecting values from contract metadata. The sequence merges v1.6 (FeeQuoter) and v1.5 (OnRamp) +// outputs; when the v1.6 path finds a FeeQuoter ref it uses FeeQuoter's StaticCfg.MaxFeeJuelsPerMsg, +// otherwise it may use only v1.5's ConstructorArgs which take MaxFeeJuelsPerMsg from OnRamp's +// MaxNopFeesJuels. So for chains that have both FeeQuoter and OnRamp metadata, either value is valid +// depending on ref version matching the sequence's dependency. Keys are big.Int.String() for set lookup. +func validMaxFeeJuelsPerMsgFromMetadata(chainSelector uint64, contractMetadata []datastore.ContractMetadata) map[string]bool { + valid := make(map[string]bool) + for _, meta := range contractMetadata { + if meta.ChainSelector != chainSelector { + continue + } + if fq, ok := meta.Metadata.(seq1_6.FeeQuoterImportConfigSequenceOutput); ok { + if fq.StaticCfg.MaxFeeJuelsPerMsg != nil { + valid[fq.StaticCfg.MaxFeeJuelsPerMsg.String()] = true + } + } + if onr, ok := meta.Metadata.(seq1_5.OnRampImportConfigSequenceOutput); ok { + if onr.StaticConfig.MaxNopFeesJuels != nil { + valid[onr.StaticConfig.MaxNopFeesJuels.String()] = true + } + } + } + return valid +} + +// getExpectedOutput returns hardcoded expected FeeQuoterUpdate values based on contract_metadata.json +func getExpectedOutput() map[uint64]sequences.FeeQuoterUpdate { + linkToken := common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA") + maxFeeJuels, _ := new(big.Int).SetString("1000000000000000000", 10) + + expected := make(map[uint64]sequences.FeeQuoterUpdate) + + // Chain 5009297550715157269: Has FeeQuoter v1.6.3 + OnRamp v1.5.0 + // Since no FeeQuoter v2.0.0 exists, it's a new deployment (ConstructorArgs populated) + // we will use only use RemoteChainSelector 15971525489660198786 in sequence input for 5009297550715157269 + // therefore only 15971525489660198786's DestChainConfig and TokenTransferFeeConfig will be included in expected output + expected[5009297550715157269] = sequences.FeeQuoterUpdate{ + ChainSelector: 5009297550715157269, + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken, + MaxFeeJuelsPerMsg: maxFeeJuels, + }, + PriceUpdaters: []common.Address{ + common.HexToAddress("0x4444444444444444444444444444444444444444"), + common.HexToAddress("0x5555555555555555555555555555555555555555"), + common.HexToAddress("0x2222222222222222222222222222222222222221"), + }, + DestChainConfigArgs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 15971525489660198786, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 8000, + MaxPerMsgGasLimit: 4000000, + DestGasOverhead: 80000, + DestGasPerPayloadByteBase: 14, + ChainFamilySelector: utils.GetSelectorHex(15971525489660198786), + DefaultTokenFeeUSDCents: 8, + DefaultTokenDestGasOverhead: 40000, + DefaultTxGasLimit: 180000, + NetworkFeeUSDCents: 10, + LinkFeeMultiplierPercent: 90, + }, + }, + }, + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 15971525489660198786, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + { + Token: common.HexToAddress("0x2222222222222222222222222222222222222222"), + TokenTransferFeeConfig: fqops.TokenTransferFeeConfig{ + FeeUSDCents: 4, + DestGasOverhead: 25000, + DestBytesOverhead: 80, + IsEnabled: true, + }, + }, + }, + }, + }, + }, + } + + // Chain 4949039107694359620: Has FeeQuoter v1.6.3 + OnRamp v1.5.0 + expected[4949039107694359620] = sequences.FeeQuoterUpdate{ + ChainSelector: 4949039107694359620, + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken, + MaxFeeJuelsPerMsg: maxFeeJuels, + }, + PriceUpdaters: []common.Address{ + common.HexToAddress("0x4444444444444444444444444444444444444444"), + common.HexToAddress("0x5555555555555555555555555555555555555555"), + common.HexToAddress("0x3333333333333333333333333333333333333333"), + common.HexToAddress("0x9999999999999999999999999999999999999999"), + }, + DestChainConfigArgs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 15971525489660198786, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 8000, + MaxPerMsgGasLimit: 4000000, + DestGasOverhead: 80000, + DestGasPerPayloadByteBase: 14, + ChainFamilySelector: utils.GetSelectorHex(15971525489660198786), + DefaultTokenFeeUSDCents: 8, + DefaultTokenDestGasOverhead: 40000, + DefaultTxGasLimit: 180000, + NetworkFeeUSDCents: 10, + LinkFeeMultiplierPercent: 90, + }, + }, + { + DestChainSelector: 5009297550715157269, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DestGasOverhead: 100000, + DestGasPerPayloadByteBase: 16, + ChainFamilySelector: utils.GetSelectorHex(5009297550715157269), + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + DefaultTxGasLimit: 200000, + NetworkFeeUSDCents: 10, + LinkFeeMultiplierPercent: 90, + }, + }, + }, + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 15971525489660198786, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + { + Token: common.HexToAddress("0x2222222222222222222222222222222222222222"), + TokenTransferFeeConfig: fqops.TokenTransferFeeConfig{ + FeeUSDCents: 4, + DestGasOverhead: 25000, + DestBytesOverhead: 80, + IsEnabled: true, + }, + }, + }, + }, + { + DestChainSelector: 5009297550715157269, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + { + Token: common.HexToAddress("0x2222222222222222222222222222222222222222"), + TokenTransferFeeConfig: fqops.TokenTransferFeeConfig{ + FeeUSDCents: 5, + DestGasOverhead: 30000, + DestBytesOverhead: 100, + IsEnabled: true, + }, + }, + }, + }, + }, + }, + } + + // Chain 15971525489660198786: Only has OnRamp v1.5.0 + maxFeeJuels159, _ := new(big.Int).SetString("1000000000000000000", 10) + expected[15971525489660198786] = sequences.FeeQuoterUpdate{ + ChainSelector: 15971525489660198786, + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken, + MaxFeeJuelsPerMsg: maxFeeJuels159, + }, + DestChainConfigArgs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 5009297550715157269, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DestGasOverhead: 100000, + DestGasPerPayloadByteBase: 16, + ChainFamilySelector: utils.GetSelectorHex(5009297550715157269), + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + DefaultTxGasLimit: 200000, + NetworkFeeUSDCents: 10, + LinkFeeMultiplierPercent: 90, + }, + }, + }, + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 5009297550715157269, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{ + { + Token: common.HexToAddress("0x2222222222222222222222222222222222222222"), + TokenTransferFeeConfig: fqops.TokenTransferFeeConfig{ + FeeUSDCents: 5, + DestGasOverhead: 30000, + DestBytesOverhead: 100, + IsEnabled: true, + }, + }, + { + Token: common.HexToAddress("0x3333333333333333333333333333333333333333"), + TokenTransferFeeConfig: fqops.TokenTransferFeeConfig{ + FeeUSDCents: 10, + DestGasOverhead: 40000, + DestBytesOverhead: 200, + IsEnabled: true, + }, + }, + }, + }, + }, + PriceUpdaters: []common.Address{ + common.HexToAddress("0x4444444444444444444444444444444444444444"), + }, + }, + } + + // Chain 5936861837188149645: Has FeeQuoter v1.6.3 + OnRamp v1.5.0 + expected[5936861837188149645] = sequences.FeeQuoterUpdate{ + ChainSelector: 5936861837188149645, + ConstructorArgs: fqops.ConstructorArgs{ + StaticConfig: fqops.StaticConfig{ + LinkToken: linkToken, + MaxFeeJuelsPerMsg: maxFeeJuels, + }, + PriceUpdaters: []common.Address{ + common.HexToAddress("0x4444444444444444444444444444444444444444"), + common.HexToAddress("0x5555555555555555555555555555555555555555"), + common.HexToAddress("0x5555555555555555555555555555555555555551"), + }, + DestChainConfigArgs: []fqops.DestChainConfigArgs{ + { + DestChainSelector: 5009297550715157269, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DestGasOverhead: 100000, + DestGasPerPayloadByteBase: 16, + ChainFamilySelector: utils.GetSelectorHex(5009297550715157269), + DefaultTokenFeeUSDCents: 10, + DefaultTokenDestGasOverhead: 50000, + DefaultTxGasLimit: 200000, + NetworkFeeUSDCents: 10, + LinkFeeMultiplierPercent: 90, + }, + }, + { + DestChainSelector: 4949039107694359620, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 9000, + MaxPerMsgGasLimit: 4500000, + DestGasOverhead: 90000, + DestGasPerPayloadByteBase: 15, + ChainFamilySelector: utils.GetSelectorHex(4949039107694359620), + DefaultTokenFeeUSDCents: 9, + DefaultTokenDestGasOverhead: 45000, + DefaultTxGasLimit: 190000, + NetworkFeeUSDCents: 10, + LinkFeeMultiplierPercent: 90, + }, + }, + { + DestChainSelector: 15971525489660198786, + DestChainConfig: fqops.DestChainConfig{ + IsEnabled: true, + MaxDataBytes: 8000, + MaxPerMsgGasLimit: 4000000, + DestGasOverhead: 80000, + DestGasPerPayloadByteBase: 14, + ChainFamilySelector: utils.GetSelectorHex(15971525489660198786), + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + DefaultTxGasLimit: 180000, + NetworkFeeUSDCents: 10, + LinkFeeMultiplierPercent: 90, + }, + }, + }, + TokenTransferFeeConfigArgs: []fqops.TokenTransferFeeConfigArgs{ + { + DestChainSelector: 5009297550715157269, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{}, + }, + { + DestChainSelector: 4949039107694359620, + TokenTransferFeeConfigs: []fqops.TokenTransferFeeConfigSingleTokenArgs{}, + }, + }, + }, + } + + return expected +} + +func TestSequenceFeeQuoterInputCreation(t *testing.T) { + contractMetadata := dummyContractMetadata + addressRefs := dummyAddressRefs + + // Collect unique chain selectors from address refs + chainSelectors := make(map[uint64]bool) + for _, ref := range addressRefs { + chainSelectors[ref.ChainSelector] = true + } + + // Convert map keys to slice and sort for deterministic test order (avoids flakiness from map iteration) + chainSelectorList := make([]uint64, 0, len(chainSelectors)) + for selector := range chainSelectors { + chainSelectorList = append(chainSelectorList, selector) + } + sort.Slice(chainSelectorList, func(i, j int) bool { return chainSelectorList[i] < chainSelectorList[j] }) + + // Create environment with simulated EVM chains + e, err := environment.New(t.Context(), + environment.WithEVMSimulated(t, chainSelectorList), + ) + require.NoError(t, err, "Failed to create environment") + require.NotNil(t, e, "Environment should be created") + + // Load address refs into a new datastore + // Note: The environment's datastore is sealed, so we'll use our own datastore + // and pass the data directly in the input to the sequence + ds := datastore.NewMemoryDataStore() + for _, ref := range addressRefs { + err := ds.Addresses().Add(ref) + require.NoError(t, err, "Failed to add address ref %+v to datastore", ref) + } + + // Load contract metadata into the datastore + err = dseq.WriteMetadataToDatastore(ds, dseq.Metadata{ + Contracts: contractMetadata, + }) + require.NoError(t, err, "Failed to write contract metadata to datastore") + + // deploy a router in each chain + for _, chainSelector := range chainSelectorList { + chain := e.BlockChains.EVMChains()[chainSelector] + dRep, err2 := cldf_ops.ExecuteOperation(e.OperationsBundle, routerops.Deploy, chain, contract.DeployInput[routerops.ConstructorArgs]{ + ChainSelector: chainSelector, + TypeAndVersion: cldf.NewTypeAndVersion(routerops.ContractType, *routerops.Version), + Args: routerops.ConstructorArgs{ + WrappedNative: utils2.RandomAddress(), // not relevant to this test, just needs to be populated with a valid address + RMNProxy: utils2.RandomAddress(), // not relevant to this test, just needs to be populated with a valid address + }, + }) + require.NoError(t, err2, "Failed to deploy router on chain %d", chainSelector) + require.NoError(t, ds.Addresses().Add(datastore.AddressRef{ + ChainSelector: chainSelector, + Type: datastore.ContractType(routerops.ContractType), + Version: routerops.Version, + Address: dRep.Output.Address, + })) + } + // Seal the datastore for use in the test + e.DataStore = ds.Seal() + deployOnRamps(t, e, 5009297550715157269, map[uint64]*semver.Version{ + 4949039107694359620: semver.MustParse("1.5.0"), + 15971525489660198786: semver.MustParse("1.6.0"), + }) + deployOnRamps(t, e, 4949039107694359620, map[uint64]*semver.Version{ + 5009297550715157269: semver.MustParse("1.5.0"), + 15971525489660198786: semver.MustParse("1.6.0"), + }) + deployOnRamps(t, e, 15971525489660198786, map[uint64]*semver.Version{ + 5009297550715157269: semver.MustParse("1.5.0"), + }) + deployOnRamps(t, e, 5936861837188149645, map[uint64]*semver.Version{ + 15971525489660198786: semver.MustParse("1.5.0"), + 5009297550715157269: semver.MustParse("1.6.0"), + 4949039107694359620: semver.MustParse("1.6.0"), + }) + // Get the FeeQuoterUpdater adapter (use concrete type so report.Output is sequences.FeeQuoterUpdate) + fquUpdater := evmadapter.FeeQuoterUpdater[sequences.FeeQuoterUpdate]{} + + // Test the sequence for each chain selector that has a FeeQuoter + for _, chainSelector := range chainSelectorList { + chain, ok := e.BlockChains.EVMChains()[chainSelector] + require.True(t, ok, "Chain with selector %d should exist", chainSelector) + + // Build input from original slices so Version/Type match exactly (sealed datastore + // can alter refs and break GetAddressRef lookup for FeeQuoter 1.6.0). + existingAddresses := e.DataStore.Addresses().Filter(datastore.AddressRefByChainSelector(chainSelector)) + contractMeta := make([]datastore.ContractMetadata, 0) + for _, meta := range contractMetadata { + if meta.ChainSelector == chainSelector { + contractMeta = append(contractMeta, meta) + } + } + sort.Slice(existingAddresses, func(i, j int) bool { + if existingAddresses[i].Type != existingAddresses[j].Type { + return string(existingAddresses[i].Type) < string(existingAddresses[j].Type) + } + return existingAddresses[i].Address < existingAddresses[j].Address + }) + sort.Slice(contractMeta, func(i, j int) bool { + return contractMeta[i].Address < contractMeta[j].Address + }) + + // Create input for SequenceFeeQuoterInputCreation + input := deploy.FeeQuoterUpdateInput{ + ChainSelector: chainSelector, + ExistingAddresses: existingAddresses, + ContractMeta: contractMeta, + } + + // Get expected output (hardcoded based on contract_metadata.json) + expectedMap := getExpectedOutput() + expected, hasExpected := expectedMap[chainSelector] + require.True(t, hasExpected, "Expected output should exist for chain %d", chainSelector) + + // to test selective remote Chain selectors + if chainSelector == 5009297550715157269 { + input.RemoteChainSelectors = []uint64{15971525489660198786} + } + input.PreviousVersions = []*semver.Version{ + semver.MustParse("1.5.0"), + semver.MustParse("1.6.0"), + } + // Execute the sequence + report, err := cldf_ops.ExecuteSequence( + e.OperationsBundle, + fquUpdater.SequenceFeeQuoterInputCreation(), + e.BlockChains, + input, + ) + + // Verify the sequence executed successfully + require.NoError(t, err, "SequenceFeeQuoterInputCreation should not error for chain %d", chainSelector) + require.NotNil(t, report, "Report should not be nil for chain %d", chainSelector) + + // Verify the output is not empty + output := report.Output + isEmpty, err := output.IsEmpty() + require.NoError(t, err, "IsEmpty check should not error") + require.False(t, isEmpty, "Output should not be empty for chain %d", chainSelector) + + // Verify basic output structure + require.Equal(t, chainSelector, output.ChainSelector, "Chain selector should match input") + require.Equal(t, existingAddresses, output.ExistingAddresses, "Existing addresses should match input") + + // Verify that the output has meaningful data + // At least one of these should be populated: + // - ConstructorArgs + // - DestChainConfigs + // - TokenTransferFeeConfigUpdates + // - AuthorizedCallerUpdates + hasData := !sequences.IsConstructorArgsEmpty(output.ConstructorArgs) || + len(output.DestChainConfigs) > 0 || + len(output.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs) > 0 || + len(output.AuthorizedCallerUpdates.AddedCallers) > 0 || + len(output.AuthorizedCallerUpdates.RemovedCallers) > 0 + + require.True(t, hasData, "Output should have at least some configuration data for chain %d", chainSelector) + // Assert against expected values + if !sequences.IsConstructorArgsEmpty(expected.ConstructorArgs) { + require.False(t, sequences.IsConstructorArgsEmpty(output.ConstructorArgs), "ConstructorArgs should be present for new deployment on chain %d", chainSelector) + require.Equal(t, expected.ConstructorArgs.StaticConfig.LinkToken, output.ConstructorArgs.StaticConfig.LinkToken, + "LinkToken should match expected value on chain %d", chainSelector) + // MaxFeeJuelsPerMsg must be one of the values present in contract metadata for this chain + // (FeeQuoter StaticCfg or OnRamp MaxNopFeesJuels); the sequence uses one or the other depending + // on whether the v1.6 path finds a FeeQuoter ref. + validMaxFee := validMaxFeeJuelsPerMsgFromMetadata(chainSelector, contractMetadata) + require.NotEmpty(t, validMaxFee, "contract metadata for chain %d should define at least one MaxFeeJuelsPerMsg source", chainSelector) + require.True(t, validMaxFee[output.ConstructorArgs.StaticConfig.MaxFeeJuelsPerMsg.String()], + "MaxFeeJuelsPerMsg should be one of the values from contract metadata (FeeQuoter or OnRamp) on chain %d", chainSelector) + // sort PriceUpdaters for deterministic comparison since sequence may merge from multiple sources + sort.Slice(output.ConstructorArgs.PriceUpdaters, func(i, j int) bool { + return output.ConstructorArgs.PriceUpdaters[i].Hex() < output.ConstructorArgs.PriceUpdaters[j].Hex() + }) + expected.ConstructorArgs.PriceUpdaters = append(expected.ConstructorArgs.PriceUpdaters, chain.DeployerKey.From) + sort.Slice(expected.ConstructorArgs.PriceUpdaters, func(i, j int) bool { + return expected.ConstructorArgs.PriceUpdaters[i].Hex() < expected.ConstructorArgs.PriceUpdaters[j].Hex() + }) + require.ElementsMatch(t, expected.ConstructorArgs.PriceUpdaters, output.ConstructorArgs.PriceUpdaters, + "PriceUpdaters should match expected value on chain %d", chainSelector) + } else { + // For existing deployments, ConstructorArgs should be empty + require.True(t, sequences.IsConstructorArgsEmpty(output.ConstructorArgs), "ConstructorArgs should be empty for existing deployment on chain %d", chainSelector) + } + + // Assert specific values based on the sequence logic in feequoterupdater.go + // The sequence merges outputs from CreateFeeQuoterUpdateInputFromV16x and CreateFeeQuoterUpdateInputFromV150 + + // Verify DestChainConfigs against expected values + // Build a map of expected dest chain configs for easier lookup + expectedDestChainConfigsMap := make(map[uint64]fqops.DestChainConfigArgs) + for _, cfg := range expected.DestChainConfigs { + expectedDestChainConfigsMap[cfg.DestChainSelector] = cfg + } + require.Len(t, output.DestChainConfigs, len(expectedDestChainConfigsMap), + "Number of DestChainConfigs should match expected value on chain %d", chainSelector) + + for _, destChainCfg := range output.DestChainConfigs { + if expectedCfg, exists := expectedDestChainConfigsMap[destChainCfg.DestChainSelector]; exists { + require.Equal(t, expectedCfg, destChainCfg, "DestChainConfig should match expected value for "+ + "DestChainSelector %d on chain %d", destChainCfg.DestChainSelector, chainSelector) + } + } + for _, cfg := range expected.ConstructorArgs.DestChainConfigArgs { + expectedDestChainConfigsMap[cfg.DestChainSelector] = cfg + } + require.Len(t, output.ConstructorArgs.DestChainConfigArgs, len(expectedDestChainConfigsMap), + "Number of Constructor DestChainConfigArgs should match expected value for chain %d", chainSelector) + + for _, destChainCfg := range output.ConstructorArgs.DestChainConfigArgs { + if expectedCfg, exists := expectedDestChainConfigsMap[destChainCfg.DestChainSelector]; exists { + require.Equal(t, expectedCfg, destChainCfg, "Constructor DestChainConfig should match expected value for "+ + "DestChainSelector %d on chain %d", destChainCfg.DestChainSelector, chainSelector) + } + } + + require.Len(t, output.ConstructorArgs.TokenTransferFeeConfigArgs, len(expected.ConstructorArgs.TokenTransferFeeConfigArgs), + "Number of TokenTransferFeeConfigArgs should match expected value for chain %d", chainSelector) + require.Len(t, output.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs, len(expected.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs), + "Number of TokenTransferFeeConfigUpdates should match expected value for chain %d", chainSelector) + for _, tokenTransferFeeConfig := range output.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs { + found := false + for _, expectedCfg := range expected.TokenTransferFeeConfigUpdates.TokenTransferFeeConfigArgs { + if tokenTransferFeeConfig.DestChainSelector == expectedCfg.DestChainSelector { + require.ElementsMatch(t, expectedCfg.TokenTransferFeeConfigs, tokenTransferFeeConfig.TokenTransferFeeConfigs, + "TokenTransferFeeConfigs should match expected value for DestChainSelector %d on chain %d", + tokenTransferFeeConfig.DestChainSelector, chainSelector) + found = true + break + } + } + require.True(t, found, "Unexpected TokenTransferFeeConfig for DestChainSelector %d on chain %d", + tokenTransferFeeConfig.DestChainSelector, chainSelector) + } + + for _, tokenTransferFeeConfig := range output.ConstructorArgs.TokenTransferFeeConfigArgs { + found := false + for _, expectedCfg := range expected.ConstructorArgs.TokenTransferFeeConfigArgs { + if tokenTransferFeeConfig.DestChainSelector == expectedCfg.DestChainSelector { + require.ElementsMatch(t, expectedCfg.TokenTransferFeeConfigs, tokenTransferFeeConfig.TokenTransferFeeConfigs, + "Constructor TokenTransferFeeConfigs should match expected value for DestChainSelector %d on chain %d", + tokenTransferFeeConfig.DestChainSelector, chainSelector) + found = true + break + } + } + require.True(t, found, "Unexpected Constructor TokenTransferFeeConfig for DestChainSelector %d on chain %d", + tokenTransferFeeConfig.DestChainSelector, chainSelector) + } + + // Verify AuthorizedCallerUpdates if present (for existing deployments) + require.ElementsMatch(t, expected.AuthorizedCallerUpdates.AddedCallers, output.AuthorizedCallerUpdates.AddedCallers, + "AuthorizedCallerUpdates.AddedCallers should match expected value on chain %d", chainSelector) + require.ElementsMatch(t, expected.AuthorizedCallerUpdates.RemovedCallers, output.AuthorizedCallerUpdates.RemovedCallers, + "AuthorizedCallerUpdates.RemovedCallers should match expected value on chain %d", chainSelector) + + t.Logf("Successfully executed SequenceFeeQuoterInputCreation for chain %d", chainSelector) + } +} + +// TestHandleEmptyGasPriceStalenessThreshold verifies HandleEmptyGasPriceStalenessThreshold behavior for +// chains with zero GasPriceStalenessThreshold (e.g. Aptos/Sui that require manual gas price). +// GasPricesPerRemoteChain uses string values (parsed as base-10 big.Int). +func TestHandleEmptyGasPriceStalenessThreshold(t *testing.T) { + aptosSelector := uint64(743186221051783445) + suiSelector := uint64(9762610643973837292) + + // EVM chain is not in gasPriceMandatoryForChainFamily -> expect error + t.Run("EVM_chain_returns_error", func(t *testing.T) { + evmChainSelector := uint64(5009297550715157269) + input := deploy.FeeQuoterUpdateInput{ + ChainSelector: 1, + AdditionalConfig: &deploy.AdditionalFeeQuoterConfig{ + GasPricesPerRemoteChain: map[uint64]string{evmChainSelector: "1000000000"}, + }, + } + priceUpdates, err := sequences.HandleEmptyGasPriceStalenessThreshold(evmChainSelector, input) + require.NoError(t, err) + require.Empty(t, priceUpdates, "Expected no gas price updates for EVM chain since staleness threshold is not empty") + }) + + // Chain in gasPriceMandatoryForChainFamily but AdditionalConfig is nil -> expect error + t.Run("Aptos_or_Sui_with_nil_AdditionalConfig_returns_error", func(t *testing.T) { + input := deploy.FeeQuoterUpdateInput{ChainSelector: 1} + _, err := sequences.HandleEmptyGasPriceStalenessThreshold(aptosSelector, input) + require.Error(t, err) + require.Contains(t, err.Error(), "please provide gas price for this remote chain in the input additional config") + }) + + // Chain in gasPriceMandatoryForChainFamily but GasPricesPerRemoteChain is nil -> expect error + t.Run("Aptos_or_Sui_with_nil_GasPricesPerRemoteChain_returns_error", func(t *testing.T) { + input := deploy.FeeQuoterUpdateInput{ + ChainSelector: 1, + AdditionalConfig: &deploy.AdditionalFeeQuoterConfig{}, + } + _, err := sequences.HandleEmptyGasPriceStalenessThreshold(aptosSelector, input) + require.Error(t, err) + require.Contains(t, err.Error(), "please provide gas price for this remote chain in the input additional config") + }) + + // Chain in gasPriceMandatoryForChainFamily but remote chain not in map -> expect error + t.Run("Aptos_or_Sui_with_missing_remote_chain_in_map_returns_error", func(t *testing.T) { + input := deploy.FeeQuoterUpdateInput{ + ChainSelector: 1, + AdditionalConfig: &deploy.AdditionalFeeQuoterConfig{ + GasPricesPerRemoteChain: map[uint64]string{999: "1000000000"}, + }, + } + _, err := sequences.HandleEmptyGasPriceStalenessThreshold(aptosSelector, input) + require.Error(t, err) + require.Contains(t, err.Error(), "please provide gas price for this remote chain in the input additional config") + }) + + // Invalid gas price string (not a number) -> expect error + t.Run("invalid_gas_price_string_returns_error", func(t *testing.T) { + input := deploy.FeeQuoterUpdateInput{ + ChainSelector: 1, + AdditionalConfig: &deploy.AdditionalFeeQuoterConfig{ + GasPricesPerRemoteChain: map[uint64]string{aptosSelector: "not-a-number"}, + }, + } + _, err := sequences.HandleEmptyGasPriceStalenessThreshold(aptosSelector, input) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid gas price") + require.Contains(t, err.Error(), "in input additional config") + }) + + // Chain in gasPriceMandatoryForChainFamily with valid gas price string -> success + t.Run("Aptos_and_Sui_with_gas_price_returns_updates", func(t *testing.T) { + gasPriceStr := "2000000000" + input := deploy.FeeQuoterUpdateInput{ + ChainSelector: 1, + AdditionalConfig: &deploy.AdditionalFeeQuoterConfig{ + GasPricesPerRemoteChain: map[uint64]string{ + aptosSelector: gasPriceStr, + suiSelector: gasPriceStr, + }, + }, + } + output, err := sequences.HandleEmptyGasPriceStalenessThreshold(aptosSelector, input) + require.NoError(t, err) + require.Len(t, output.GasPriceUpdates, 1) + require.Equal(t, aptosSelector, output.GasPriceUpdates[0].DestChainSelector) + require.Equal(t, 0, big.NewInt(2e9).Cmp(output.GasPriceUpdates[0].UsdPerUnitGas), "UsdPerUnitGas should match parsed value") + output, err = sequences.HandleEmptyGasPriceStalenessThreshold(suiSelector, input) + require.NoError(t, err) + require.Len(t, output.GasPriceUpdates, 1) + require.Equal(t, suiSelector, output.GasPriceUpdates[0].DestChainSelector) + require.Equal(t, 0, big.NewInt(2e9).Cmp(output.GasPriceUpdates[0].UsdPerUnitGas), "UsdPerUnitGas should match parsed value") + + }) +} + +// placeholder addresses used for OnRamp deploy in tests (contracts are not validated on simulated chain beyond non-zero) +var ( + linkToken = common.HexToAddress("0x514910771AF9Ca656af840dff83E8264EcF986CA") + rmnProxyPlaceholder = common.HexToAddress("0x7777777777777777777777777777777777777777") + tokenAdminRegistryPlaceholder = common.HexToAddress("0x8888888888888888888888888888888888888888") + priceRegistryPlaceholder = common.HexToAddress("0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") +) + +func deployOnRamps(t *testing.T, e *cldf.Environment, chainSelector uint64, remoteChains map[uint64]*semver.Version) { + chain := e.BlockChains.EVMChains()[chainSelector] + routerOnRamps := make([]router.RouterOnRamp, 0) + routerRef := e.DataStore.Addresses().Filter( + datastore.AddressRefByChainSelector(chainSelector), + datastore.AddressRefByType(datastore.ContractType(routerops.ContractType)), + datastore.AddressRefByVersion(routerops.Version)) + require.Len(t, routerRef, 1, "There should be exactly one router deployed for chain %d", chainSelector) + routerAddr := common.HexToAddress(routerRef[0].Address) + + for remoteChainSelector, version := range remoteChains { + var rampAddr datastore.AddressRef + switch version.String() { + case "1.5.0": + out, err := cldf_ops.ExecuteOperation(e.OperationsBundle, onrampv1_5ops.DeployOnRamp, chain, contract.DeployInput[onrampv1_5ops.ConstructorArgs]{ + ChainSelector: chainSelector, + TypeAndVersion: cldf.NewTypeAndVersion(onrampv1_5ops.ContractType, *onrampv1_5ops.Version), + Args: onrampv1_5ops.ConstructorArgs{ + StaticConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampStaticConfig{ + LinkToken: linkToken, + ChainSelector: chainSelector, + DestChainSelector: remoteChainSelector, + DefaultTxGasLimit: 200000, + MaxNopFeesJuels: big.NewInt(1000000000000000000), + PrevOnRamp: common.Address{}, + RmnProxy: rmnProxyPlaceholder, + TokenAdminRegistry: tokenAdminRegistryPlaceholder, + }, + DynamicConfig: evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampDynamicConfig{ + Router: routerAddr, + MaxNumberOfTokensPerMsg: 5, + DestGasOverhead: 100000, + DestGasPerPayloadByte: 16, + DestDataAvailabilityOverheadGas: 50000, + DestGasPerDataAvailabilityByte: 10, + DestDataAvailabilityMultiplierBps: 1000, + PriceRegistry: priceRegistryPlaceholder, + MaxDataBytes: 10000, + MaxPerMsgGasLimit: 5000000, + DefaultTokenFeeUSDCents: 0, + DefaultTokenDestGasOverhead: 0, + EnforceOutOfOrder: false, + }, + RateLimiterConfig: evm_2_evm_onramp_v1_5_0.RateLimiterConfig{ + IsEnabled: false, + Capacity: big.NewInt(0), + Rate: big.NewInt(0), + }, + FeeTokenConfigs: []evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampFeeTokenConfigArgs{}, + TokenTransferFeeConfigArgs: []evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampTokenTransferFeeConfigArgs{}, + NopsAndWeights: []evm_2_evm_onramp_v1_5_0.EVM2EVMOnRampNopAndWeight{}, + }, + }) + require.NoError(t, err) + rampAddr = out.Output + case "1.6.0": + out, err := cldf_ops.ExecuteOperation(e.OperationsBundle, onrampv1_6ops.Deploy, chain, contract.DeployInput[onrampv1_6ops.ConstructorArgs]{ + ChainSelector: chainSelector, + TypeAndVersion: cldf.NewTypeAndVersion(onrampv1_6ops.ContractType, *onrampv1_6ops.Version), + Args: onrampv1_6ops.ConstructorArgs{ + StaticConfig: onrampv1_6ops.StaticConfig{ + ChainSelector: chainSelector, + RmnRemote: rmnProxyPlaceholder, + NonceManager: utils2.RandomAddress(), + TokenAdminRegistry: tokenAdminRegistryPlaceholder, + }, + DynamicConfig: onrampv1_6ops.DynamicConfig{ + FeeQuoter: utils2.RandomAddress(), + ReentrancyGuardEntered: false, + MessageInterceptor: common.Address{}, + FeeAggregator: chain.DeployerKey.From, + AllowlistAdmin: chain.DeployerKey.From, + }, + DestChainConfigArgs: []onrampv1_6ops.DestChainConfigArgs{ + { + DestChainSelector: remoteChainSelector, + Router: routerAddr, + AllowlistEnabled: false, + }, + }, + }, + }) + require.NoError(t, err) + rampAddr = out.Output + } + routerOnRamps = append(routerOnRamps, router.RouterOnRamp{ + DestChainSelector: remoteChainSelector, + OnRamp: common.HexToAddress(rampAddr.Address), + }) + } + + _, err := cldf_ops.ExecuteOperation(e.OperationsBundle, routerops.ApplyRampUpdates, chain, contract.FunctionInput[routerops.ApplyRampsUpdatesArgs]{ + ChainSelector: chainSelector, + Address: routerAddr, + Args: routerops.ApplyRampsUpdatesArgs{ + OnRampUpdates: routerOnRamps, + OffRampAdds: []router.RouterOffRamp{}, + OffRampRemoves: nil, + }, + }) + require.NoError(t, err, "Failed to apply ramp updates for router on chain %d", chainSelector) +} diff --git a/chains/evm/gobindings/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/chains/evm/gobindings/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 5b0f188ecd..f49e9ce6cb 100644 --- a/chains/evm/gobindings/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/chains/evm/gobindings/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,4 @@ -GETH_VERSION: 1.17.0 +GETH_VERSION: 1.17.1 advanced_pool_hooks: ../solc/ccip/AdvancedPoolHooks/AdvancedPoolHooks.sol/AdvancedPoolHooks.abi.json ../solc/ccip/AdvancedPoolHooks/AdvancedPoolHooks.sol/AdvancedPoolHooks.bin f1c8260d2adb1b0eb01af60956d3b2c615641ae68b1e2fb9c59f4c5c216b2e37 advanced_pool_hooks_extractor: ../solc/ccip/AdvancedPoolHooksExtractor/AdvancedPoolHooksExtractor.sol/AdvancedPoolHooksExtractor.abi.json ../solc/ccip/AdvancedPoolHooksExtractor/AdvancedPoolHooksExtractor.sol/AdvancedPoolHooksExtractor.bin b830a2462c65673e018fd0ec625dccebef701779c458800f93875d5c179ed49e burn_from_mint_token_pool: ../solc/ccip/BurnFromMintTokenPool/BurnFromMintTokenPool.sol/BurnFromMintTokenPool.abi.json ../solc/ccip/BurnFromMintTokenPool/BurnFromMintTokenPool.sol/BurnFromMintTokenPool.bin 903ecb55c90e819eca28e48a56522bba91792d8607159511c0491ee115d5b926 diff --git a/chains/solana/deployment/docs/_category_.yaml b/chains/solana/deployment/docs/_category_.yaml new file mode 100644 index 0000000000..3b43de19d2 --- /dev/null +++ b/chains/solana/deployment/docs/_category_.yaml @@ -0,0 +1,2 @@ +label: "Solana Implementation" +position: 3 diff --git a/chains/solana/deployment/docs/adapter.md b/chains/solana/deployment/docs/adapter.md new file mode 100644 index 0000000000..ddf31f665f --- /dev/null +++ b/chains/solana/deployment/docs/adapter.md @@ -0,0 +1,195 @@ +--- +title: "SolanaAdapter Reference" +sidebar_label: "Adapter" +sidebar_position: 2 +--- + +# SolanaAdapter Reference + +The `SolanaAdapter` implements the shared deployment interfaces for the Solana Virtual Machine (SVM). Unlike the stateless EVM adapter, the Solana adapter carries state for timelock addresses. + +**Source:** [v1_6_0/sequences/adapter.go](../v1_6_0/sequences/adapter.go) + +--- + +## Struct Definition + +```go +type SolanaAdapter struct { + timelockAddr map[uint64]solana.PublicKey +} +``` + +The `timelockAddr` map caches resolved timelock public keys by chain selector, avoiding repeated datastore lookups. + +--- + +## Registration + +The Solana adapter registers itself with **5 shared registries** via two `init()` functions: + +### Main Registration (`v1_6_0/sequences/adapter.go`) + +**Source:** [v1_6_0/sequences/adapter.go](../v1_6_0/sequences/adapter.go) + +```go +func init() { + v := semver.MustParse("1.6.0") + laneapi.GetLaneAdapterRegistry().RegisterLaneAdapter(chain_selectors.FamilySolana, v, &SolanaAdapter{}) + deployapi.GetRegistry().RegisterDeployer(chain_selectors.FamilySolana, v, &SolanaAdapter{}) + deployapi.GetTransferOwnershipRegistry().RegisterAdapter(chain_selectors.FamilySolana, v, &SolanaAdapter{}) + mcmsreaderapi.GetRegistry().RegisterMCMSReader(chain_selectors.FamilySolana, &SolanaAdapter{}) + tokensapi.GetTokenAdapterRegistry().RegisterTokenAdapter(chain_selectors.FamilySolana, v, &SolanaAdapter{}) +} +``` + +### Curse Adapter Registration (`v1_6_0/adapters/init.go`) + +**Source:** [v1_6_0/adapters/init.go](../v1_6_0/adapters/init.go) + +```go +func init() { + curseRegistry := fastcurse.GetCurseRegistry() + curseRegistry.RegisterNewCurse(fastcurse.CurseRegistryInput{ + CursingFamily: chainsel.FamilySolana, + CursingVersion: semver.MustParse("1.6.0"), + CurseAdapter: NewCurseAdapter(), + CurseSubjectAdapter: NewCurseAdapter(), + }) +} +``` + +### Registration Summary + +| Registry | Interface | Key | +|----------|-----------|-----| +| `LaneAdapterRegistry` | `LaneAdapter` | `solana-1.6.0` | +| `DeployerRegistry` | `Deployer` | `solana-1.6.0` | +| `TransferOwnershipAdapterRegistry` | `TransferOwnershipAdapter` | `solana-1.6.0` | +| `MCMSReaderRegistry` | `MCMSReader` | `solana` (no version) | +| `TokenAdapterRegistry` | `TokenAdapter` | `solana-1.6.0` | +| `CurseRegistry` | `CurseAdapter` + `CurseSubjectAdapter` | `solana-1.6.0` | + +--- + +## Interface Implementations + +### Deployer Interface + +| Method | Implementation | +|--------|---------------| +| `DeployChainContracts()` | Returns `DeployChainContracts` sequence (deploys LINK, Router, FeeQuoter, OffRamp, RMNRemote, BurnMint/LockRelease token pools, TestReceiver; initializes all; extends lookup table) | +| `DeployMCMS()` | Deploys Access Controller, MCM, and Timelock programs; initializes access controller roles, MCM configs, and timelock | +| `FinalizeDeployMCMS()` | Configures MCM (set_config), sets up timelock roles (Proposer, Executor, Canceller, Bypasser) | +| `SetOCR3Config()` | Returns `SetOCR3Config` sequence (sets OCR3 config on OffRamp) | +| `GrantAdminRoleToTimelock()` | Returns `nil` (not implemented for Solana) | +| `UpdateMCMSConfig()` | Updates config of specified MCMS contracts | + +### LaneAdapter Interface + +| Method | Implementation | +|--------|---------------| +| `ConfigureLaneLegAsSource()` | Adds destination chain config to FeeQuoter, adds destination chain to Router | +| `ConfigureLaneLegAsDest()` | Adds OffRamp to Router for remote chain, adds source chain config to OffRamp | +| `GetOnRampAddress()` | **Delegates to `GetRouterAddress()`** -- on Solana, the Router serves as the OnRamp | +| `GetOffRampAddress()` | Looks up OffRamp from DataStore | +| `GetFQAddress()` | Looks up FeeQuoter from DataStore | +| `GetRouterAddress()` | Looks up Router from DataStore | +| `GetRMNRemoteAddress()` | Looks up RMNRemote from DataStore | + +### TokenAdapter Interface + +| Method | Implementation | +|--------|---------------| +| `ConfigureTokenForTransfersSequence()` | Registers token in admin registry, accepts admin, sets pool on Router, configures remote chains with rate limits | +| `AddressRefToBytes()` | Converts base58 address to `[]byte` via `solana.MustPublicKeyFromBase58` | +| `DeriveTokenAddress()` | Not implemented (returns error) | +| `DeriveTokenDecimals()` | Reads decimals from on-chain mint account data | +| `DeriveTokenPoolCounterpart()` | Derives PDA using `TokenPoolConfigAddress(tokenMint, tokenPool)` | +| `ManualRegistration()` | Registers token admin registry, initializes token pool, optionally creates token multisig with customer mint authorities | +| `SetTokenPoolRateLimits()` | Sets inbound/outbound rate limits for BurnMint or LockRelease pools | +| `DeployToken()` | Deploys SPL/SPL2022 token, creates ATAs for senders, optionally uploads metadata | +| `DeployTokenVerify()` | No-op (returns nil) | +| `DeployTokenPoolForToken()` | Initializes pool account (BurnMint or LockRelease), creates pool signer ATA, sets mint authority for BurnMint pools | +| `UpdateAuthorities()` | Transfers token pool ownership to timelock signer PDA, updates rate limit admin | + +### TransferOwnershipAdapter Interface + +| Method | Implementation | +|--------|---------------| +| `InitializeTimelockAddress()` | No-op (returns nil) | +| `SequenceTransferOwnershipViaMCMS()` | Dispatches per contract type: Router, OffRamp, FeeQuoter, RMNRemote, AccessController, RBACTimelock | +| `ShouldAcceptOwnershipWithTransferOwnership()` | Returns `true` when current owner equals the chain's deployer key | +| `SequenceAcceptOwnership()` | Dispatches accept-ownership per contract type (same types as transfer) | + +### MCMSReader Interface + +| Method | Implementation | +|--------|---------------| +| `GetChainMetadata()` | Resolves MCM contract address based on timelock action (Schedule/Cancel/Bypass), gets op count from inspector, builds Solana MCMS chain metadata with access controller accounts | +| `GetTimelockRef()` | Looks up RBACTimelock from DataStore | +| `GetMCMSRef()` | Looks up MCM program from DataStore | + +--- + +## Solana-Specific Patterns + +### Router = OnRamp + +On Solana, the Router program functions as both the Router and OnRamp. `GetOnRampAddress()` delegates directly to `GetRouterAddress()`: + +```go +func (a *SolanaAdapter) GetOnRampAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) { + return a.GetRouterAddress(ds, chainSelector) +} +``` + +### Two-Phase MCMS Deployment + +Unlike EVM where `FinalizeDeployMCMS()` is a no-op, Solana requires: + +1. **Phase 1 (`DeployMCMS`)**: Deploy programs + initialize (Access Controller accounts, MCM init, Timelock init) +2. **Phase 2 (`FinalizeDeployMCMS`)**: Configure MCM (set_config for Proposer/Canceller/Bypasser) + set up timelock roles + +### PDA-Based Address Derivation + +Solana uses Program Derived Addresses (PDAs) extensively: + +- `FindConfigPDA(routerProgram)` -- Router config +- `FindOfframpConfigPDA(offRampProgram)` -- OffRamp config +- `FindFqConfigPDA(feeQuoterProgram)` -- FeeQuoter config +- `FindRMNRemoteConfigPDA(rmnRemoteProgram)` -- RMN Remote config +- `state.GetTimelockSignerPDA(timelockID, seed)` -- Timelock signer +- `state.GetMCMSignerPDA(mcmID, seed)` -- MCM signer +- `tokens.TokenPoolConfigAddress(tokenMint, tokenPool)` -- Token pool config +- `tokens.TokenPoolSignerAddress(tokenMint, tokenPool)` -- Token pool signer + +### Base58 Address Handling + +All Solana addresses use base58 encoding. The adapter converts between `datastore.AddressRef` (string addresses) and `solana.PublicKey`: + +```go +func (a *SolanaAdapter) AddressRefToBytes(ref datastore.AddressRef) ([]byte, error) { + return solana.MustPublicKeyFromBase58(ref.Address).Bytes(), nil +} +``` + +### Solana-Specific Contract Types + +Defined in `utils/common.go`: + +| Constant | Type | +|----------|------| +| `TimelockProgramType` | `"RBACTimelockProgram"` | +| `McmProgramType` | `"ManyChainMultiSigProgram"` | +| `AccessControllerProgramType` | `"AccessControllerProgram"` | +| `ProposerSeed` | `"ProposerSeed"` | +| `CancellerSeed` | `"CancellerSeed"` | +| `BypasserSeed` | `"BypasserSeed"` | +| `RBACTimelockSeed` | `"RBACTimelockSeed"` | +| `ProposerAccessControllerAccount` | `"ProposerAccessControllerAccount"` | +| `ExecutorAccessControllerAccount` | `"ExecutorAccessControllerAccount"` | +| `CancellerAccessControllerAccount` | `"CancellerAccessControllerAccount"` | +| `BypasserAccessControllerAccount` | `"BypasserAccessControllerAccount"` | +| `SPLTokens` | `"SPLTokens"` | +| `SPL2022Tokens` | `"SPL2022Tokens"` | diff --git a/chains/solana/deployment/docs/index.md b/chains/solana/deployment/docs/index.md new file mode 100644 index 0000000000..733acd961f --- /dev/null +++ b/chains/solana/deployment/docs/index.md @@ -0,0 +1,73 @@ +--- +title: "Solana Implementation" +sidebar_label: "Overview" +sidebar_position: 1 +--- + +# Solana Deployment Implementation + +This documentation covers the Solana-specific implementation of the CCIP Deployment Tooling API. The Solana adapter handles the unique requirements of the Solana Virtual Machine (SVM), including account-based program deployment, PDA-derived addresses, and two-phase MCMS initialization. + +For the shared interfaces this implementation fulfills, see [Interfaces Reference](../../../../deployment/docs/interfaces.md). For the shared types, see [Types Reference](../../../../deployment/docs/types.md). + +--- + +## Package Layout + +``` +chains/solana/deployment/ +├── utils/ +│ ├── common.go # Contract types, PDA helpers, MCMS builders +│ ├── deploy.go # MaybeDeployContract, artifact download +│ ├── mcms.go # GetAllMCMS helper +│ ├── utils.go # Lookup tables, token utilities, program data +│ ├── upgrade_authority.go # Upgrade authority management +│ ├── datastore.go # Address format conversions (base58) +│ ├── sequences.go # Shared sequence utilities +│ └── artifact_versions.go # Program artifact version mappings +├── v1_6_0/ +│ ├── adapters/ +│ │ └── init.go # CurseAdapter registration +│ ├── operations/ +│ │ ├── router/ # Router program operations +│ │ ├── offramp/ # OffRamp program operations +│ │ ├── fee_quoter/ # FeeQuoter program operations +│ │ ├── tokens/ # Token deployment operations +│ │ ├── token_pools/ # Token pool operations +│ │ ├── mcms/ # MCMS operations +│ │ ├── rmn_remote/ # RMN Remote operations +│ │ └── test_receiver/ # Test receiver operations +│ └── sequences/ +│ ├── adapter.go # SolanaAdapter struct + init() registration +│ ├── deploy_chain_contracts.go # Full chain deployment sequence +│ ├── connect_chains.go # Lane configuration sequences +│ ├── mcms.go # MCMS deploy + finalize sequences +│ ├── ocr.go # OCR3 configuration +│ ├── fee_quoter.go # Fee configuration sequences +│ ├── tokens.go # Token deployment + configuration +│ └── transfer_ownership.go # Ownership transfer sequences +└── idl/ # Anchor IDL definitions +``` + +## Key Differences from EVM + +| Aspect | EVM | Solana | +|--------|-----|--------| +| **Address format** | Hex (20 bytes) | Base58 (32 bytes, `solana.PublicKey`) | +| **Adapter state** | Stateless empty struct | Stateful: `timelockAddr map[uint64]solana.PublicKey` | +| **Router = OnRamp** | Separate OnRamp contract | Router serves as OnRamp (`GetOnRampAddress` delegates to `GetRouterAddress`) | +| **Contract deployment** | Single-step `CREATE`/`CREATE2` | Program deployment with `DeployProgram` + separate initialization | +| **Address derivation** | Deployed address known at creation | PDAs derived from seeds (`FindConfigPDA`, `FindOfframpConfigPDA`, etc.) | +| **MCMS deployment** | Single-phase (EVM returns no-op for finalize) | Two-phase: `DeployMCMS` deploys + initializes, `FinalizeDeployMCMS` configures + sets up roles | +| **Token standards** | ERC-20 only | SPL Tokens + SPL 2022 Tokens | +| **Token pool deployment** | New contract deployed per pool | Token pools are program accounts (initialized, not deployed) | +| **Lookup tables** | Not needed | Address lookup tables required for OffRamp (extended during deployment) | +| **Ownership acceptance** | Atomic with transfer when caller is deployer | `ShouldAcceptOwnershipWithTransferOwnership` returns true when current owner is deployer key | +| **MCMS contract types** | Single MCM, Timelock | Access Controller + MCM + Timelock with PDA seeds (Proposer, Canceller, Bypasser, RBACTimelock) | +| **Token configuration** | Register via TokenAdminRegistry contract | Register via Router's TokenAdminRegistry + initialize pool with Router/RMN references | +| **Associated Token Accounts** | Not applicable | ATAs must be created for token pool signers before use | + +## Documentation + +- [SolanaAdapter Reference](adapter.md) -- adapter struct, interface implementations, registration +- [Operations and Sequences Reference](operations-and-sequences.md) -- all operations by program, all sequences, utilities diff --git a/chains/solana/deployment/docs/operations-and-sequences.md b/chains/solana/deployment/docs/operations-and-sequences.md new file mode 100644 index 0000000000..0c44facc41 --- /dev/null +++ b/chains/solana/deployment/docs/operations-and-sequences.md @@ -0,0 +1,397 @@ +--- +title: "Solana Operations and Sequences Reference" +sidebar_label: "Operations & Sequences" +sidebar_position: 3 +--- + +# Solana Operations and Sequences Reference + +This document covers all Solana-specific operations (atomic on-chain interactions) and sequences (composed workflows) for the CCIP deployment tooling. + +For the shared interfaces these implement, see [Interfaces Reference](../../../../deployment/docs/interfaces.md). For the adapter that exposes these, see [SolanaAdapter Reference](adapter.md). + +--- + +## Table of Contents + +- [Operations by Program](#operations-by-program) +- [Sequences](#sequences) +- [Utilities](#utilities) + +--- + +## Operations by Program + +Solana operations are organized by the on-chain program they interact with. Each operation directory contains deploy, initialize, and state-modifying operations specific to that program. + +**Source:** [v1_6_0/operations/](../v1_6_0/operations/) + +### Router Operations + +**Source:** [v1_6_0/operations/router/](../v1_6_0/operations/router/) + +| Operation | Description | +|-----------|-------------| +| `Deploy` | Deploys the Router program using `MaybeDeployContract` | +| `Initialize` | Initializes the Router with FeeQuoter, LINK token, and RMN Remote references | +| `ConnectChains` | Adds a destination chain config to the Router (source-side lane setup) | +| `AddOffRamp` | Registers an OffRamp for a remote chain on the Router (dest-side lane setup) | +| `RegisterTokenAdminRegistry` | Registers a token in the admin registry via the Router | +| `AcceptTokenAdminRegistry` | Accepts token admin registration | +| `SetPool` | Associates a token pool with a token on the Router and FeeQuoter | +| `TransferOwnership` | Proposes ownership transfer to a new owner | +| `AcceptOwnership` | Accepts a proposed ownership transfer | + +### OffRamp Operations + +**Source:** [v1_6_0/operations/offramp/](../v1_6_0/operations/offramp/) + +| Operation | Description | +|-----------|-------------| +| `Deploy` | Deploys the OffRamp program | +| `Initialize` | Initializes with FeeQuoter, Router, and RMN Remote references | +| `InitializeConfig` | Sets OffRamp config (e.g., `EnableExecutionAfter` threshold) | +| `ConnectChains` | Adds a source chain config to the OffRamp (dest-side lane setup) | +| `SetOcr3` | Sets OCR3 configuration on the OffRamp | +| `TransferOwnership` | Proposes ownership transfer | +| `AcceptOwnership` | Accepts ownership transfer | + +### FeeQuoter Operations + +**Source:** [v1_6_0/operations/fee_quoter/](../v1_6_0/operations/fee_quoter/) + +| Operation | Description | +|-----------|-------------| +| `Deploy` | Deploys the FeeQuoter program | +| `Initialize` | Initializes with max fee, Router, OffRamp, and LINK token | +| `AddPriceUpdater` | Adds the OffRamp as a price updater | +| `ConnectChains` | Adds a destination chain config on the FeeQuoter (source-side lane setup) | +| `SetTokenTransferFeeConfig` | Sets per-token transfer fee configuration for a destination chain | +| `TransferOwnership` | Proposes ownership transfer | +| `AcceptOwnership` | Accepts ownership transfer | + +### Token Operations + +**Source:** [v1_6_0/operations/tokens/](../v1_6_0/operations/tokens/) + +| Operation | Description | +|-----------|-------------| +| `DeployLINK` | Deploys a LINK token (SPL token with configurable decimals and private key) | +| `DeploySolanaToken` | Deploys an SPL or SPL2022 token with optional ATAs and pre-minting | +| `UpsertTokenMetadata` | Uploads or updates token metadata on-chain | +| `CreateTokenMultisig` | Creates a multisig authority for token operations (used for customer mint authorities) | + +### Token Pool Operations + +**Source:** [v1_6_0/operations/token_pools/](../v1_6_0/operations/token_pools/) + +| Operation | Description | +|-----------|-------------| +| `DeployBurnMint` | Deploys BurnMint token pool program | +| `DeployLockRelease` | Deploys LockRelease token pool program | +| `InitializeBurnMint` | Initializes a BurnMint pool account with Router and RMN references | +| `InitializeLockRelease` | Initializes a LockRelease pool account with Router and RMN references | +| `UpsertRemoteChainConfigBurnMint` | Configures remote chain on a BurnMint pool | +| `UpsertRemoteChainConfigLockRelease` | Configures remote chain on a LockRelease pool | +| `UpsertRateLimitsBurnMint` | Sets rate limits on a BurnMint pool | +| `UpsertRateLimitsLockRelease` | Sets rate limits on a LockRelease pool | +| `TransferOwnershipBurnMint` | Proposes ownership transfer for BurnMint pool | +| `TransferOwnershipLockRelease` | Proposes ownership transfer for LockRelease pool | +| `AcceptOwnershipBurnMint` | Accepts ownership for BurnMint pool | +| `AcceptOwnershipLockRelease` | Accepts ownership for LockRelease pool | +| `UpdateRateLimitAdminBurnMint` | Updates the rate limit admin on a BurnMint pool | +| `UpdateRateLimitAdminLockRelease` | Updates the rate limit admin on a LockRelease pool | + +### MCMS Operations + +**Source:** [v1_6_0/operations/mcms/](../v1_6_0/operations/mcms/) + +| Operation | Description | +|-----------|-------------| +| `AccessControllerDeploy` | Deploys Access Controller program | +| `McmDeploy` | Deploys MCM (ManyChainMultiSig) program | +| `TimelockDeploy` | Deploys Timelock program | +| `InitAccessControllerOp` | Initializes an access controller account for a specific role | +| `InitMCMOp` | Initializes MCM for a specific config type (Proposer, Canceller, Bypasser) | +| `ConfigureMCMOp` | Reconfigures MCM after deployment (used in `FinalizeDeployMCMS`) | +| `InitTimelockOp` | Initializes the Timelock with minimum delay | +| `AddAccessOp` | Grants access roles on Timelock (Proposer, Executor, Canceller, Bypasser) | +| `TransferOwnershipOp` | Proposes ownership transfer for an ownable MCMS contract | +| `AcceptOwnershipOp` | Accepts ownership for an ownable MCMS contract | + +### RMN Remote Operations + +**Source:** [v1_6_0/operations/rmn_remote/](../v1_6_0/operations/rmn_remote/) + +| Operation | Description | +|-----------|-------------| +| `Deploy` | Deploys the RMN Remote program | +| `Initialize` | Initializes the RMN Remote | +| `TransferOwnership` | Proposes ownership transfer | +| `AcceptOwnership` | Accepts ownership transfer | + +### Test Receiver Operations + +**Source:** [v1_6_0/operations/test_receiver/](../v1_6_0/operations/test_receiver/) + +| Operation | Description | +|-----------|-------------| +| `Deploy` | Deploys the Test Receiver program | +| `Initialize` | Initializes with Router reference | + +--- + +## Sequences + +Sequences compose multiple operations into complete workflows. + +**Source:** [v1_6_0/sequences/](../v1_6_0/sequences/) + +### Deploy Chain Contracts + +**Source:** [v1_6_0/sequences/deploy_chain_contracts.go](../v1_6_0/sequences/deploy_chain_contracts.go) + +**Variable:** `DeployChainContracts` +**Input:** `ContractDeploymentConfigPerChainWithAddress` +**Output:** `OnChainOutput` + +Deploys and initializes all CCIP infrastructure on a Solana chain: + +1. Deploy LINK token (SPL token with configurable private key/decimals) +2. Deploy Router program +3. Deploy FeeQuoter program +4. Deploy OffRamp program +5. Deploy RMN Remote program +6. Deploy BurnMint Token Pool program +7. Deploy LockRelease Token Pool program +8. Initialize FeeQuoter (with max fee, Router, OffRamp, LINK references; add OffRamp as price updater) +9. Initialize Router (with FeeQuoter, LINK, RMN Remote references) +10. Initialize OffRamp (with FeeQuoter, Router, RMN Remote references; set config with execution threshold) +11. Initialize RMN Remote +12. **Extend OffRamp lookup table** with PDAs for all deployed programs (OffRamp config, FeeQuoter config, Router config, RMN Remote config, token pool addresses) +13. Deploy and initialize Test Receiver + +### Lane Configuration + +**Source:** [v1_6_0/sequences/connect_chains.go](../v1_6_0/sequences/connect_chains.go) + +#### ConfigureLaneLegAsSource + +Configures this chain as the source end of a lane. + +**Input:** `UpdateLanesInput` + +**Steps:** +1. Add destination chain config to FeeQuoter (translates shared `FeeQuoterDestChainConfig` to Solana-specific `fee_quoter.DestChainConfig`) +2. Add destination chain to Router (with allowlist configuration) + +#### ConfigureLaneLegAsDest + +Configures this chain as the destination end of a lane. + +**Input:** `UpdateLanesInput` + +**Steps:** +1. Add OffRamp to Router for the remote chain +2. Add source chain config to OffRamp (with source OnRamp address, enabled state, RMN verification flag) + +### MCMS Deployment + +**Source:** [v1_6_0/sequences/mcms.go](../v1_6_0/sequences/mcms.go) + +#### DeployMCMS (Phase 1) + +Deploys and initializes MCMS infrastructure: + +1. Deploy Access Controller program +2. Deploy MCM program +3. Deploy Timelock program +4. Initialize Access Controller accounts (Proposer, Executor, Canceller, Bypasser) +5. Initialize MCM for each config type (Proposer, Canceller, Bypasser) +6. Initialize Timelock with minimum delay + +#### FinalizeDeployMCMS (Phase 2) + +Completes MCMS setup after deployment: + +1. Configure MCM (set_config for Proposer, Canceller, Bypasser -- updates signer/quorum config) +2. Set up Timelock roles: + - Proposer role: MCM proposer signer PDA + - Executor role: Deployer key + - Canceller role: Canceller PDA + Proposer PDA + Bypasser PDA + - Bypasser role: Bypasser PDA + +#### UpdateMCMSConfig + +Updates config of specified MCMS contracts. Applies the same MCM config to Canceller, Bypasser, and Proposer for each specified MCM contract. + +#### GrantAdminRoleToTimelock + +Not implemented for Solana (returns `nil`). + +### OCR3 Configuration + +**Source:** [v1_6_0/sequences/ocr.go](../v1_6_0/sequences/ocr.go) + +**Variable:** `SetOCR3Config` + +Sets OCR3 configuration on the OffRamp. Resolves the OffRamp address from the DataStore and delegates to `offrampops.SetOcr3`. + +### Fee Configuration + +**Source:** [v1_6_0/sequences/fee_quoter.go](../v1_6_0/sequences/fee_quoter.go) + +#### SetTokenTransferFeeConfig + +**Variable:** `SetTokenTransferFeeConfig` +**Input:** `FeeQuoterSetTokenTransferFeeConfigSequenceInput` + +Sets per-token transfer fee configs on the FeeQuoter. Iterates over remote chain configs and applies fee config for each token/destination pair. + +### Token Sequences + +**Source:** [v1_6_0/sequences/tokens.go](../v1_6_0/sequences/tokens.go) + +#### DeployToken + +Deploys an SPL or SPL2022 token: +1. Checks if token already exists in DataStore (skips deployment if found) +2. Creates token with optional private key, ATAs for senders, pre-mint amount, and freeze authority disable +3. Optionally uploads token metadata + +#### DeployTokenPoolForToken + +Initializes a token pool account for an existing token: +1. Initializes BurnMint or LockRelease pool with Router and RMN Remote references +2. Creates associated token account (ATA) for the pool signer PDA +3. For BurnMint pools: sets mint authority to pool signer PDA (if deployer owns it) + +#### ConfigureTokenForTransfersSequence + +Full token configuration workflow: +1. Register token in admin registry via Router +2. Accept token admin registration +3. Set token pool on Router (associates pool with token on Router and FeeQuoter) +4. For each remote chain: + - Upsert remote chain config on token pool (BurnMint or LockRelease variant) + - Set inbound/outbound rate limits + +#### ManualRegistration + +For externally-owned tokens where mint authority is unavailable: +1. Register token admin registry (optionally with external admin) +2. Initialize token pool (BurnMint or LockRelease) +3. Transfer token pool ownership to proposed owner +4. Optionally create token multisig with customer mint authorities + pool signer PDA + +#### SetTokenPoolRateLimits + +Sets rate limits on a BurnMint or LockRelease token pool for a specific remote chain. + +#### UpdateAuthorities + +Transfers token pool ownership to the timelock signer PDA: +1. Update rate limit admin to timelock signer +2. Transfer ownership to timelock signer +3. Accept ownership (immediate acceptance since deployer is current owner) + +### Ownership Transfer + +**Source:** [v1_6_0/sequences/transfer_ownership.go](../v1_6_0/sequences/transfer_ownership.go) + +#### SequenceTransferOwnershipViaMCMS + +Dispatches ownership transfer based on contract type: +- **Router, OffRamp, FeeQuoter, RMN Remote**: Direct per-program transfer operation +- **AccessController type**: Transfers all MCMS contracts including access controller accounts +- **RBACTimelock type**: Transfers MCMS contracts (Timelock, Proposer, Canceller, Bypasser MCM accounts) + +#### SequenceAcceptOwnership + +Mirrors `SequenceTransferOwnershipViaMCMS` but calls accept-ownership operations for each contract type. + +--- + +## Utilities + +**Source:** [utils/](../utils/) + +### Contract Deployment (`deploy.go`) + +| Function | Description | +|----------|-------------| +| `MaybeDeployContract` | Checks DataStore for existing contract; deploys via `chain.DeployProgram` only if not found | +| `DownloadSolanaCCIPProgramArtifacts` | Downloads pre-built program artifacts from GitHub releases | + +### MCMS Helpers (`common.go`) + +| Function | Description | +|----------|-------------| +| `BuildMCMSBatchOperation` | Converts Solana instructions into MCMS `BatchOperation` transactions | +| `GetTimelockSignerPDA` | Resolves the timelock signer PDA from DataStore refs | +| `GetMCMSignerPDA` | Resolves an MCM signer PDA for a given signer type | +| `FundSolanaAccounts` | Airdrops SOL to accounts (testnet utility) | +| `FundFromDeployerKey` | Transfers SOL from deployer to accounts | +| `FundFromAddressIxs` | Creates SOL transfer instructions | + +### MCMS Ref Resolution (`mcms.go`) + +| Function | Description | +|----------|-------------| +| `GetAllMCMS` | Returns all MCMS-related address refs for a chain (access controller + accounts + timelock + MCM accounts) | + +### Program Utilities (`utils.go`) + +| Function | Description | +|----------|-------------| +| `ExtendLookupTable` | Extends an OffRamp address lookup table with new entries (deduplicates) | +| `GetTokenProgramID` | Maps `SPLTokens`/`SPL2022Tokens` contract types to Solana program IDs | +| `GetSolProgramSize` | Gets the byte size of a deployed program | +| `GetSolProgramData` | Reads program data account (data type + address) | +| `GetTokenDecimals` | Reads token decimals from on-chain mint account | +| `GetTokenMintAuthority` | Reads the current mint authority of a token | +| `MintTokens` | Mints tokens to specified addresses via their ATAs | +| `DisableFreezeAuthority` | Permanently disables freeze authority on token mints | + +### DataStore Utilities (`datastore.go`) + +Provides Solana-specific address format conversion functions, including `ToByteArray` for converting DataStore address refs to `[]byte` via base58 decoding. + +### Upgrade Authority (`upgrade_authority.go`) + +Manages Solana program upgrade authority for production deployments. + +--- + +## Operation Pattern + +Solana operations follow the same framework as EVM but interact with Solana chains: + +```go +var MyOperation = operations.NewOperation( + "my-operation", + semver.MustParse("1.6.0"), + "Description of what this operation does", + func(b operations.Bundle, chain cldf_solana.Chain, input MyInput) (MyOutput, error) { + // Build Solana instruction + ixn, err := program.NewMyInstruction(input.Param, /* accounts */).ValidateAndBuild() + if err != nil { + return MyOutput{}, err + } + + // Execute on-chain + if err := chain.Confirm([]solana.Instruction{ixn}); err != nil { + return MyOutput{}, err + } + + return MyOutput{/* ... */}, nil + }, +) +``` + +Key differences from EVM operations: +- Uses `cldf_solana.Chain` instead of EVM chain type +- Builds Solana `Instruction` objects (not EVM transactions) +- Executes via `chain.Confirm(instructions)` (not contract bindings) +- No automatic MCMS fallback -- MCMS batch ops are built explicitly via `BuildMCMSBatchOperation` +- Deploy operations use `MaybeDeployContract` which calls `chain.DeployProgram` diff --git a/chains/solana/deployment/go.mod b/chains/solana/deployment/go.mod index 44a28aa645..a265b9651f 100644 --- a/chains/solana/deployment/go.mod +++ b/chains/solana/deployment/go.mod @@ -18,16 +18,16 @@ replace ( require ( github.com/Masterminds/semver/v3 v3.4.0 - github.com/ethereum/go-ethereum v1.17.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/gagliardetto/binary v0.8.0 github.com/gagliardetto/solana-go v1.13.0 github.com/rs/zerolog v1.34.0 github.com/smartcontractkit/chain-selectors v1.0.97 github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260129103204-4c8453dd8139 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d - github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20251014191100-bad58388f0c9 + github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260312233953-f588f8dc6d7c github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-00010101000000-000000000000 - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 github.com/smartcontractkit/chainlink-deployments-framework v0.80.2 github.com/smartcontractkit/mcms v0.36.0 github.com/stretchr/testify v1.11.1 @@ -36,6 +36,7 @@ require ( require ( github.com/creachadair/jrpc2 v1.2.0 // indirect github.com/creachadair/mds v0.13.4 // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b // indirect github.com/smartcontractkit/chainlink-ton v0.0.0-20260213025045-83535910e2c0 // indirect github.com/stellar/go-stellar-sdk v0.1.0 // indirect github.com/stellar/go-xdr v0.0.0-20231122183749-b53fb00bcac2 // indirect @@ -81,10 +82,10 @@ require ( github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dchest/siphash v1.2.3 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/emicklei/dot v1.6.2 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/fatih/color v1.18.0 // indirect github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b // indirect @@ -188,7 +189,7 @@ require ( github.com/smartcontractkit/chainlink-evm v0.3.3 // indirect github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3544aab8f3 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 // indirect @@ -199,7 +200,7 @@ require ( github.com/spf13/cast v1.10.0 // indirect github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect - github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -219,7 +220,7 @@ require ( go.mongodb.org/mongo-driver v1.17.2 // indirect 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/otel v1.39.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect @@ -230,28 +231,28 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/zap v1.27.1 // indirect golang.org/x/crypto v0.48.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/chains/solana/deployment/go.sum b/chains/solana/deployment/go.sum index 5a97421bfb..951e8b407e 100644 --- a/chains/solana/deployment/go.sum +++ b/chains/solana/deployment/go.sum @@ -239,8 +239,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -273,12 +273,12 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= @@ -862,10 +862,10 @@ github.com/smartcontractkit/chain-selectors v1.0.97 h1:ECOin+SkJv2MUrfqTUu28J0ku github.com/smartcontractkit/chain-selectors v1.0.97/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 h1:vGdeMwHO3ow88HvxfhA4DDPYNY0X9jmdux7L83UF/W8= github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2/go.mod h1:iteU0WORHkArACVh/HoY/1bipV4TcNcJdTmom9uIT0E= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20251014191100-bad58388f0c9 h1:cMzoreLtepEiB5pfYrvreXNoZideVRzQQKNr5Io5J7M= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20251014191100-bad58388f0c9/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260312233953-f588f8dc6d7c h1:zHOzhm2TaGQKzPSlooyBkyT18xEZDO/PuEFf61cGsuU= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260312233953-f588f8dc6d7c/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 h1:eui+u6ge2RYW01F/DeXWrc5UOqc+8+lyPoi9TIAmMgo= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= 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-deployments-framework v0.80.2 h1:M944dbKDHJiqqGOfiaeQw9nUk/uuci8ggUXSgfSzW5Q= @@ -876,12 +876,14 @@ github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3544aab8f3/go.mod h1:3Lsp38qxen9PABVF+O5eocveQev+hyo9HLAgRodBD4Q= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 h1:JU1JUrkzdAUHsOYdS9DENPkJfmrxweFRPRSztad6oPM= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2/go.mod h1:+pRGfDej1r7cHMs1dYmuyPuOZzYB9Q+PKu0FvZOYlmw= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 h1:03tbcwjyIEjvHba1IWOj1sfThwebm2XNzyFHSuZtlWc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 h1:xHPmFDhff7QpeFxKsZfk+24j4AlnQiFjjRh5O87Peu4= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b h1:36knUpKHHAZ86K4FGWXtx8i/EQftGdk2bqCoEu/Cha8= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 h1:KyPROV+v7P8VdiU7JhVuGLcDlEBsURSpQmSCgNBTY+s= @@ -948,8 +950,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -1025,8 +1027,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -1047,20 +1049,20 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1126,8 +1128,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1323,8 +1325,8 @@ golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1384,8 +1386,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1441,8 +1443,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/chains/solana/deployment/utils/utils.go b/chains/solana/deployment/utils/utils.go index e8ec288b20..302f573074 100644 --- a/chains/solana/deployment/utils/utils.go +++ b/chains/solana/deployment/utils/utils.go @@ -49,7 +49,8 @@ func GetSolProgramSize(chain cldf_solana.Chain, programID solana.PublicKey) (int func GetSolProgramData(client *solrpc.Client, programID solana.PublicKey) (struct { DataType uint32 Address solana.PublicKey -}, error) { +}, error, +) { var programData struct { DataType uint32 Address solana.PublicKey @@ -126,6 +127,19 @@ func GetTokenProgramID(programName cldf_deployment.ContractType) (solana.PublicK return programID, nil } +func GetTokenProgramType(programID solana.PublicKey) (cldf_deployment.ContractType, error) { + tokenPrograms := map[solana.PublicKey]cldf_deployment.ContractType{ + solana.TokenProgramID: SPLTokens, + solana.Token2022ProgramID: SPL2022Tokens, + } + + programName, ok := tokenPrograms[programID] + if !ok { + return "", fmt.Errorf("invalid token program ID: %s. Must be one of: %s, %s", programID, solana.TokenProgramID, solana.Token2022ProgramID) + } + return programName, nil +} + func MintTokens(chain cldf_solana.Chain, tokenProgramID, mint solana.PublicKey, amountToAddress map[string]uint64) error { for toAddress, amount := range amountToAddress { toAddressBase58 := solana.MustPublicKeyFromBase58(toAddress) @@ -183,3 +197,24 @@ func GetTokenDecimals(chain cldf_solana.Chain, tokenMint solana.PublicKey) (uint } return mintData.Decimals, nil } + +func FetchTokenProgramID(ctx context.Context, chain cldf_solana.Chain, tokenPubKey solana.PublicKey) (solana.PublicKey, error) { + tokenAcctInfo, err := chain.Client.GetAccountInfo(ctx, tokenPubKey) + if err != nil { + return solana.PublicKey{}, fmt.Errorf("failed to get account info for token %s: %w", tokenPubKey.String(), err) + } + + // Best-effort safeguard: if this truly is a token, then we should be able to fetch its decimals + _, err = GetTokenDecimals(chain, tokenPubKey) + if err != nil { + return solana.PublicKey{}, fmt.Errorf("failed to get token decimals for token %s: %w", tokenPubKey.String(), err) + } + + // Safeguard: make sure that the token program ID is one we actually support + programID := tokenAcctInfo.Value.Owner + if _, err = GetTokenProgramType(programID); err != nil { + return solana.PublicKey{}, fmt.Errorf("account %s is owned by %s, which is not a supported token program: %w", tokenPubKey.String(), programID.String(), err) + } + + return programID, nil +} diff --git a/chains/solana/deployment/v1_6_0/changesets/rmn_changesets.go b/chains/solana/deployment/v1_6_0/changesets/rmn_changesets.go new file mode 100644 index 0000000000..7738294f9e --- /dev/null +++ b/chains/solana/deployment/v1_6_0/changesets/rmn_changesets.go @@ -0,0 +1,69 @@ +package changesets + +import ( + "fmt" + + "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/sequences" + _ "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/sequences" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/changesets" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" +) + +type RMNRemoteSetEventAuthoritiesChangesetInput struct { + ChainSelector uint64 `json:"chainSelector"` + MCMS mcms.Input `json:"mcms"` +} + +func RMNRemoteSetEventAuthoritiesChangeset() cldf.ChangeSetV2[RMNRemoteSetEventAuthoritiesChangesetInput] { + return cldf.CreateChangeSet(rmnSetEventAuthoritiesApply, rmnSetEventAuthoritiesVerify) +} + +func rmnSetEventAuthoritiesVerify(env cldf.Environment, input RMNRemoteSetEventAuthoritiesChangesetInput) error { + if err := cldf.IsValidChainSelector(input.ChainSelector); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", input.ChainSelector, err) + } + if !env.BlockChains.Exists(input.ChainSelector) { + return fmt.Errorf("chain with selector %d does not exist", input.ChainSelector) + } + + if err := input.MCMS.Validate(); err != nil { + return fmt.Errorf("invalid MCMS configuration: %w", err) + } + + return nil +} + +func rmnSetEventAuthoritiesApply(e cldf.Environment, input RMNRemoteSetEventAuthoritiesChangesetInput) (cldf.ChangesetOutput, error) { + reports := make([]cldf_ops.Report[any, any], 0) + + // Prepare sequence input + seqInput := sequences.RMNRemoteSetEventAuthoritiesSequenceInput{ + DataStore: e.DataStore, + Selector: input.ChainSelector, + } + + report, err := cldf_ops.ExecuteSequence(e.OperationsBundle, sequences.SetRMNRemoteEventAuthorities, e.BlockChains, seqInput) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to set rmn event authorities: %w", err) + } + + reports = append(reports, report.ExecutionReports...) + + // Create the datastore with the addresses from the report + ds := datastore.NewMemoryDataStore() + for _, addr := range report.Output.Addresses { + if err := ds.Addresses().Add(addr); err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to add address to datastore: %w", err) + } + } + + return changesets. + NewOutputBuilder(e, changesets.GetRegistry()). + WithReports(reports). + WithDataStore(ds). + WithBatchOps(report.Output.BatchOps). + Build(input.MCMS) +} diff --git a/chains/solana/deployment/v1_6_0/operations/fee_quoter/fee_quoter.go b/chains/solana/deployment/v1_6_0/operations/fee_quoter/fee_quoter.go index 801c235a16..5c309f865c 100644 --- a/chains/solana/deployment/v1_6_0/operations/fee_quoter/fee_quoter.go +++ b/chains/solana/deployment/v1_6_0/operations/fee_quoter/fee_quoter.go @@ -185,6 +185,52 @@ var ConnectChains = operations.NewOperation( }, ) +type DisableDestChainParams struct { + FeeQuoter solana.PublicKey + RemoteChainSelector uint64 +} + +var DisableDestChain = operations.NewOperation( + "fee-quoter:disable-dest-chain", + Version, + "Disables a destination chain on the FeeQuoter, preventing sending to that chain", + func(b operations.Bundle, chain cldf_solana.Chain, input DisableDestChainParams) (sequences.OnChainOutput, error) { + fee_quoter.SetProgramID(input.FeeQuoter) + authority := GetAuthority(chain, input.FeeQuoter) + feeQuoterConfigPDA, _, _ := state.FindFqConfigPDA(input.FeeQuoter) + fqDestChainPDA, _, _ := state.FindFqDestChainPDA(input.RemoteChainSelector, input.FeeQuoter) + + instruction, err := fee_quoter.NewDisableDestChainInstruction( + input.RemoteChainSelector, + feeQuoterConfigPDA, + fqDestChainPDA, + authority, + ).ValidateAndBuild() + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to build disable dest chain instruction: %w", err) + } + + if authority != chain.DeployerKey.PublicKey() { + batches, err := utils.BuildMCMSBatchOperation( + chain.Selector, + []solana.Instruction{instruction}, + input.FeeQuoter.String(), + ContractType.String(), + ) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to create MCMS batch for disable dest chain: %w", err) + } + return sequences.OnChainOutput{BatchOps: []types.BatchOperation{batches}}, nil + } + + err = chain.Confirm([]solana.Instruction{instruction}) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to confirm disable dest chain: %w", err) + } + return sequences.OnChainOutput{}, nil + }, +) + var TransferOwnership = operations.NewOperation( "fee-quoter:transfer-ownership", Version, diff --git a/chains/solana/deployment/v1_6_0/operations/offramp/offramp.go b/chains/solana/deployment/v1_6_0/operations/offramp/offramp.go index 26bf4138c5..42942422f0 100644 --- a/chains/solana/deployment/v1_6_0/operations/offramp/offramp.go +++ b/chains/solana/deployment/v1_6_0/operations/offramp/offramp.go @@ -242,6 +242,52 @@ var ConnectChains = operations.NewOperation( }, ) +type DisableSourceChainParams struct { + OffRamp solana.PublicKey + RemoteChainSelector uint64 +} + +var DisableSourceChain = operations.NewOperation( + "off-ramp:disable-source-chain", + Version, + "Disables a source chain on the OffRamp, preventing receiving from that chain", + func(b operations.Bundle, chain cldf_solana.Chain, input DisableSourceChainParams) (sequences.OnChainOutput, error) { + ccip_offramp.SetProgramID(input.OffRamp) + authority := GetAuthority(chain, input.OffRamp) + offRampConfigPDA, _, _ := state.FindOfframpConfigPDA(input.OffRamp) + offRampSourceChainPDA, _, _ := state.FindOfframpSourceChainPDA(input.RemoteChainSelector, input.OffRamp) + + instruction, err := ccip_offramp.NewDisableSourceChainSelectorInstruction( + input.RemoteChainSelector, + offRampSourceChainPDA, + offRampConfigPDA, + authority, + ).ValidateAndBuild() + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to build disable source chain instruction: %w", err) + } + + if authority != chain.DeployerKey.PublicKey() { + batches, err := utils.BuildMCMSBatchOperation( + chain.Selector, + []solana.Instruction{instruction}, + input.OffRamp.String(), + ContractType.String(), + ) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to create MCMS batch for disable source chain: %w", err) + } + return sequences.OnChainOutput{BatchOps: []types.BatchOperation{batches}}, nil + } + + err = chain.Confirm([]solana.Instruction{instruction}) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to confirm disable source chain: %w", err) + } + return sequences.OnChainOutput{}, nil + }, +) + var SetOcr3 = operations.NewOperation( "off-ramp:set-ocr3", Version, diff --git a/chains/solana/deployment/v1_6_0/operations/rmn_remote/rmn_remote.go b/chains/solana/deployment/v1_6_0/operations/rmn_remote/rmn_remote.go index 5bfe5ab44f..fa3fd59902 100644 --- a/chains/solana/deployment/v1_6_0/operations/rmn_remote/rmn_remote.go +++ b/chains/solana/deployment/v1_6_0/operations/rmn_remote/rmn_remote.go @@ -15,14 +15,17 @@ import ( "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/utils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/v0_1_1/rmn_remote" + rmn161 "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/v1_6_1/rmn_remote" "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" api "github.com/smartcontractkit/chainlink-ccip/deployment/fastcurse" "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" ) -var ContractType cldf_deployment.ContractType = "RMNRemote" -var ProgramName = "rmn_remote" -var Version *semver.Version = semver.MustParse("1.6.0") +var ( + ContractType cldf_deployment.ContractType = "RMNRemote" + ProgramName = "rmn_remote" + Version *semver.Version = semver.MustParse("1.6.0") +) type CurseInput struct { Subjects []api.Subject @@ -256,6 +259,53 @@ var Uncurse = operations.NewOperation( }, ) +type EventAuthoritiesInput struct { + EventAuthorities []solana.PublicKey + RMNRemote solana.PublicKey + RMNRemoteConfigPDA solana.PublicKey +} + +var SetEventAuthorities = operations.NewOperation( + "rmn-remote:set-event-authorities", + Version, + "Sets the event authorities list on the RMNRemote contract", + func(b operations.Bundle, chain cldf_solana.Chain, input EventAuthoritiesInput) (sequences.OnChainOutput, error) { + rmn161.SetProgramID(input.RMNRemote) + + authority := GetAuthority(chain, input.RMNRemote) + + ixn, err := rmn161.NewSetEventAuthoritiesInstruction( + input.EventAuthorities, + input.RMNRemoteConfigPDA, + authority, + solana.SystemProgramID, + ).ValidateAndBuild() + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to build set event authorities instruction: %w", err) + } + + batches := make([]types.BatchOperation, 0) + if authority != chain.DeployerKey.PublicKey() { + b, err := utils.BuildMCMSBatchOperation( + chain.Selector, + []solana.Instruction{ixn}, + input.RMNRemote.String(), + ContractType.String(), + ) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to execute or create batch: %w", err) + } + batches = append(batches, b) + } else { + err := chain.Confirm([]solana.Instruction{ixn}) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to confirm set-event-authorities instruction: %w", err) + } + } + return sequences.OnChainOutput{BatchOps: batches}, nil + }, +) + func GetAuthority(chain cldf_solana.Chain, program solana.PublicKey) solana.PublicKey { programData := rmn_remote.Config{} rmnRemoteConfigPDA, _, _ := state.FindRMNRemoteConfigPDA(program) diff --git a/chains/solana/deployment/v1_6_0/operations/router/router.go b/chains/solana/deployment/v1_6_0/operations/router/router.go index 0edd5e1d4c..404c200fec 100644 --- a/chains/solana/deployment/v1_6_0/operations/router/router.go +++ b/chains/solana/deployment/v1_6_0/operations/router/router.go @@ -430,6 +430,15 @@ var RegisterTokenAdminRegistry = operations.NewOperation( return TokenAdminRegistryOut{}, nil } pendingAdmin = tokenAdminRegistryAccount.PendingAdministrator + // there is already an admin registered, we need to transfer + if !tokenAdminRegistryAccount.Administrator.IsZero() { + out, err := operations.ExecuteOperation(b, TransferTokenAdminRegistry, chain, input) + if err != nil { + return TokenAdminRegistryOut{}, fmt.Errorf("failed to transfer token admin registry: %w", err) + } + b.Logger.Infof("Token admin registry transfer initiated. Pending admin: %s", out.Output.PendingSigner.String()) + return out.Output, nil + } } // this is the key that will need to accept the admin registration pendingSigner := input.Admin @@ -635,7 +644,7 @@ var TransferTokenAdminRegistry = operations.NewOperation( "router:transfer-token-admin-registry", Version, "Transfers a Token Admin Registry with the Router 1.6.0 contract", - func(b operations.Bundle, chain cldf_solana.Chain, input TokenAdminRegistryParams) (sequences.OnChainOutput, error) { + func(b operations.Bundle, chain cldf_solana.Chain, input TokenAdminRegistryParams) (TokenAdminRegistryOut, error) { ccip_router.SetProgramID(input.Router) routerConfigPDA, _, _ := state.FindConfigPDA(input.Router) tokenAdminRegistryPDA, _, _ := state.FindTokenAdminRegistryPDA(input.TokenMint, input.Router) @@ -643,6 +652,12 @@ var TransferTokenAdminRegistry = operations.NewOperation( var tokenAdminRegistryAccount ccip_common.TokenAdminRegistry if err := chain.GetAccountDataBorshInto(b.GetContext(), tokenAdminRegistryPDA, &tokenAdminRegistryAccount); err == nil { currentAdmin = tokenAdminRegistryAccount.Administrator + if currentAdmin.IsZero() { + return TokenAdminRegistryOut{}, fmt.Errorf("no admin found for token admin registry") + } + b.Logger.Infof("Current token admin registry account: %+v", tokenAdminRegistryAccount) + } else { + return TokenAdminRegistryOut{}, fmt.Errorf("failed to fetch token admin registry account: %w", err) } // we can only sign as either the deployer or timelock // ccip admin should be timelock @@ -651,6 +666,10 @@ var TransferTokenAdminRegistry = operations.NewOperation( if input.Admin.IsZero() { input.Admin = ccipAdmin } + if currentAdmin == input.Admin { + b.Logger.Info("Token admin registry already registered with the given admin:", tokenAdminRegistryAccount) + return TokenAdminRegistryOut{}, nil + } // sign as the current admin to transfer tempIx, err := ccip_router.NewTransferAdminRoleTokenAdminRegistryInstruction( input.Admin, @@ -660,11 +679,11 @@ var TransferTokenAdminRegistry = operations.NewOperation( currentAdmin, ).ValidateAndBuild() if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to generate instructions: %w", err) + return TokenAdminRegistryOut{}, fmt.Errorf("failed to generate instructions: %w", err) } ixData, err := tempIx.Data() if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to extract data payload router accept admin role token admin registry instruction: %w", err) + return TokenAdminRegistryOut{}, fmt.Errorf("failed to extract data payload router accept admin role token admin registry instruction: %w", err) } ixn := solana.NewInstruction(input.Router, tempIx.Accounts(), ixData) // now we need a proposal if the admin is not the deployer @@ -676,19 +695,24 @@ var TransferTokenAdminRegistry = operations.NewOperation( ContractType.String(), ) if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to execute or create batch: %w", err) + return TokenAdminRegistryOut{}, fmt.Errorf("failed to execute or create batch: %w", err) } - return sequences.OnChainOutput{ - BatchOps: []types.BatchOperation{batches}, + return TokenAdminRegistryOut{ + PendingSigner: input.Admin, + OnChainOutput: sequences.OnChainOutput{ + BatchOps: []types.BatchOperation{batches}, + }, }, nil } err = chain.Confirm([]solana.Instruction{ixn}) if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to confirm register token admin registry: %w", err) + return TokenAdminRegistryOut{}, fmt.Errorf("failed to confirm register token admin registry: %w", err) } - return sequences.OnChainOutput{}, nil + return TokenAdminRegistryOut{ + PendingSigner: input.Admin, + }, nil }, ) diff --git a/chains/solana/deployment/v1_6_0/sequences/disable_remote_chain.go b/chains/solana/deployment/v1_6_0/sequences/disable_remote_chain.go new file mode 100644 index 0000000000..87ab822010 --- /dev/null +++ b/chains/solana/deployment/v1_6_0/sequences/disable_remote_chain.go @@ -0,0 +1,63 @@ +package sequences + +import ( + "fmt" + + "github.com/Masterminds/semver/v3" + "github.com/gagliardetto/solana-go" + + fqops "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/operations/fee_quoter" + offrampops "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/operations/offramp" + "github.com/smartcontractkit/chainlink-ccip/deployment/lanes" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" + cldf_chain "github.com/smartcontractkit/chainlink-deployments-framework/chain" + cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" +) + +var DisableRemoteChainSequence = cldf_ops.NewSequence( + "DisableRemoteChain", + semver.MustParse("1.6.0"), + "Disables both sending to and receiving from a remote chain on a Solana chain", + func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, input lanes.DisableRemoteChainInput) (sequences.OnChainOutput, error) { + var result sequences.OnChainOutput + b.Logger.Infof("SVM Disabling remote chain %d on chain %d", input.RemoteChainSelector, input.LocalChainSelector) + + feeQuoterAddress := solana.PublicKeyFromBytes(input.FeeQuoter) + offRampAddress := solana.PublicKeyFromBytes(input.OffRamp) + + fqOut, err := cldf_ops.ExecuteOperation( + b, + fqops.DisableDestChain, + chains.SolanaChains()[input.LocalChainSelector], + fqops.DisableDestChainParams{ + FeeQuoter: feeQuoterAddress, + RemoteChainSelector: input.RemoteChainSelector, + }, + ) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to disable dest chain on FeeQuoter: %w", err) + } + result.BatchOps = append(result.BatchOps, fqOut.Output.BatchOps...) + + offrampOut, err := cldf_ops.ExecuteOperation( + b, + offrampops.DisableSourceChain, + chains.SolanaChains()[input.LocalChainSelector], + offrampops.DisableSourceChainParams{ + OffRamp: offRampAddress, + RemoteChainSelector: input.RemoteChainSelector, + }, + ) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to disable source chain on OffRamp: %w", err) + } + result.BatchOps = append(result.BatchOps, offrampOut.Output.BatchOps...) + + b.Logger.Infof("Remote chain %d disabled on Solana chain %d", input.RemoteChainSelector, input.LocalChainSelector) + return result, nil + }, +) + +func (a *SolanaAdapter) DisableRemoteChain() *cldf_ops.Sequence[lanes.DisableRemoteChainInput, sequences.OnChainOutput, cldf_chain.BlockChains] { + return DisableRemoteChainSequence +} diff --git a/chains/solana/deployment/v1_6_0/sequences/mcms.go b/chains/solana/deployment/v1_6_0/sequences/mcms.go index 45d6a5a165..6cf97e7a94 100644 --- a/chains/solana/deployment/v1_6_0/sequences/mcms.go +++ b/chains/solana/deployment/v1_6_0/sequences/mcms.go @@ -360,6 +360,46 @@ func (a *SolanaAdapter) GrantAdminRoleToTimelock() *operations.Sequence[deployop return nil } +func (a *SolanaAdapter) UpdateMCMSConfig() *operations.Sequence[deployops.UpdateMCMSConfigInputPerChainWithSelector, sequences.OnChainOutput, chain.BlockChains] { + return operations.NewSequence( + "update-mcms-config", + semver.MustParse("1.0.0"), + "Updates Config of specified MCMS Contracts", + func(b operations.Bundle, chains cldf_chain.BlockChains, in ccipapi.UpdateMCMSConfigInputPerChainWithSelector) (output sequences.OnChainOutput, err error) { + chain, ok := chains.SolanaChains()[in.ChainSelector] + if !ok { + return sequences.OnChainOutput{}, fmt.Errorf("chain with selector %d not found in environment", in.ChainSelector) + } + + // Set config for each inputted contract + for _, contract := range in.MCMContracts { + deps := mcmsops.Deps{ + Chain: chain, + ExistingAddresses: in.ExistingAddresses, + Qualifier: contract.Qualifier, + } + configureOpInput := ccipapi.MCMSDeploymentConfigPerChain{ + Canceller: in.MCMConfig, + Bypasser: in.MCMConfig, + Proposer: in.MCMConfig, + Qualifier: &contract.Qualifier, + ContractVersion: contract.Version.String(), + } + id, _, _ := mcms_solana.ParseContractAddress(contract.Address) + configureOpOutput, err := configureMCM(b, deps, configureOpInput, id) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to configure MCMs: %w", err) + } + + output.Addresses = append(output.Addresses, configureOpOutput.NewAddresses...) + output.BatchOps = append(output.BatchOps, configureOpOutput.BatchOps...) + } + + return output, nil + }, + ) +} + func getRefsAsOwnable( refs []cldf_datastore.AddressRef, transferAccessController bool) []mcmsops.OwnableContract { diff --git a/chains/solana/deployment/v1_6_0/sequences/set_rmn_event_authorities.go b/chains/solana/deployment/v1_6_0/sequences/set_rmn_event_authorities.go new file mode 100644 index 0000000000..b52b881e95 --- /dev/null +++ b/chains/solana/deployment/v1_6_0/sequences/set_rmn_event_authorities.go @@ -0,0 +1,66 @@ +package sequences + +import ( + "fmt" + + "github.com/Masterminds/semver/v3" + "github.com/gagliardetto/solana-go" + rmnops "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/operations/rmn_remote" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" + cldf_chain "github.com/smartcontractkit/chainlink-deployments-framework/chain" + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" +) + +type RMNRemoteSetEventAuthoritiesSequenceInput struct { + DataStore datastore.DataStore + Selector uint64 +} + +var SetRMNRemoteEventAuthorities = cldf_ops.NewSequence( + "rmn-remote:set-event-authorities", + semver.MustParse("1.6.1"), + "Sets event authorities on the RMNRemote 1.6.1 contract", + func(b cldf_ops.Bundle, chains cldf_chain.BlockChains, input RMNRemoteSetEventAuthoritiesSequenceInput) (sequences.OnChainOutput, error) { + chain, ok := chains.SolanaChains()[input.Selector] + if !ok { + return sequences.OnChainOutput{}, fmt.Errorf("solana chain with selector %d not found", input.Selector) + } + + rmnAddrBytes, err := (&SolanaAdapter{}).GetRMNRemoteAddress(input.DataStore, input.Selector) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to get RMNRemote address: %w", err) + } + rmnPubkey := solana.PublicKeyFromBytes(rmnAddrBytes) + + routerAddrBytes, err := (&SolanaAdapter{}).GetRouterAddress(input.DataStore, input.Selector) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to get Router address: %w", err) + } + routerPubkey := solana.PublicKeyFromBytes(routerAddrBytes) + + rmnConfigPDA, _, err := state.FindRMNRemoteConfigPDA(rmnPubkey) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to find RMNRemote config PDA: %w", err) + } + routerSignerPDA, _, err := state.FindFeeBillingSignerPDA(routerPubkey) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to find Router Signer PDA: %w", err) + } + + out, err := cldf_ops.ExecuteOperation(b, + rmnops.SetEventAuthorities, + chain, + rmnops.EventAuthoritiesInput{ + EventAuthorities: []solana.PublicKey{routerSignerPDA}, + RMNRemote: rmnPubkey, + RMNRemoteConfigPDA: rmnConfigPDA, + }, + ) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to set event authorities: %w", err) + } + return out.Output, nil + }, +) diff --git a/chains/solana/deployment/v1_6_0/sequences/tokens.go b/chains/solana/deployment/v1_6_0/sequences/tokens.go index c20d920cc3..189110bd5e 100644 --- a/chains/solana/deployment/v1_6_0/sequences/tokens.go +++ b/chains/solana/deployment/v1_6_0/sequences/tokens.go @@ -1,6 +1,7 @@ package sequences import ( + "errors" "fmt" "github.com/gagliardetto/solana-go" @@ -201,10 +202,46 @@ func (a *SolanaAdapter) ManualRegistration() *cldf_ops.Sequence[tokenapi.ManualR if !ok { return sequences.OnChainOutput{}, fmt.Errorf("chain with selector %d not defined", input.ChainSelector) } - tokenRef, tokenProgramId, err := getTokenMintAndTokenProgram(input.ExistingDataStore, input.TokenRef, chain) - if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to get token and token program address using the specified reference (%+v): %w", input.TokenRef, err) + + // NOTE: we only need token metadata if we're creating a token multisig. If we + // don't need to create one, then we can avoid sending extraneous RPC calls to + // the chain. + needTokenMultisig := input.SVMExtraArgs != nil && len(input.SVMExtraArgs.CustomerMintAuthorities) > 0 + tokenSymb := input.TokenRef.Qualifier + + // NOTE: we attempt to resolve the token from the datastore first to avoid RPC calls. + // If the token does not exist there, then we will try to infer all the info from the + // chain *if* the token ref includes an address. Otherwise, we don't have enough info + // to proceed. + var tokenProg solana.PublicKey + var tokenMint solana.PublicKey + if tokRef, tokProgramID, err := getTokenMintAndTokenProgram(input.ExistingDataStore, input.TokenRef, chain); err != nil { + b.Logger.Warnf("Failed to get token mint and program ID from datastore for ref (%s) (err = %v). Falling back to on-chain lookup if possible.", datastore_utils.SprintRef(input.TokenRef), err) + if input.TokenRef.Address == "" { + return sequences.OnChainOutput{}, errors.New("token information could not be resolved from datastore and token reference does not include an address to attempt on-chain resolution") + } + + tokenAddr := solana.MustPublicKeyFromBase58(input.TokenRef.Address) + tokProgramID, err := utils.FetchTokenProgramID(b.GetContext(), chain, tokenAddr) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to get token program ID for token mint '%s': %w", tokenAddr.String(), err) + } + + tokenProg = tokProgramID + tokenMint = tokenAddr + if needTokenMultisig && tokenSymb == "" { + if tokenMeta, _, err := tokens.GetTokenMetadata(b.GetContext(), chain.Client, tokenAddr); err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to get token metadata for token mint '%s': %w", tokenAddr.String(), err) + } else { + tokenSymb = tokenMeta.Data.Symbol + } + } + } else { + tokenMint = solana.MustPublicKeyFromBase58(tokRef.Address) + tokenSymb = tokRef.Qualifier + tokenProg = tokProgramID } + routerAddr, err := a.GetRouterAddress(input.ExistingDataStore, chain.Selector) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to get router address: %w", err) @@ -222,7 +259,7 @@ func (a *SolanaAdapter) ManualRegistration() *cldf_ops.Sequence[tokenapi.ManualR rtarOut, err := operations.ExecuteOperation(b, routerops.RegisterTokenAdminRegistry, chains.SolanaChains()[chain.Selector], routerops.TokenAdminRegistryParams{ Router: solana.PublicKeyFromBytes(routerAddr), - TokenMint: solana.MustPublicKeyFromBase58(tokenRef.Address), + TokenMint: tokenMint, Admin: tokenAdmin, ExistingAddresses: input.ExistingDataStore.Addresses().Filter(), }) @@ -240,9 +277,8 @@ func (a *SolanaAdapter) ManualRegistration() *cldf_ops.Sequence[tokenapi.ManualR if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to find token pool address using the specified reference (%+v): %w", input.TokenPoolRef, err) } - tokenMint := solana.MustPublicKeyFromBase58(tokenRef.Address) - tokenPool := solana.MustPublicKeyFromBase58(tokenPoolRef.Address) + tokenPool := solana.MustPublicKeyFromBase58(tokenPoolRef.Address) if input.SVMExtraArgs == nil || !input.SVMExtraArgs.SkipTokenPoolInit { initTPOp := tokenpoolops.InitializeBurnMint transferOwnershipTPOp := tokenpoolops.TransferOwnershipBurnMint @@ -263,7 +299,7 @@ func (a *SolanaAdapter) ManualRegistration() *cldf_ops.Sequence[tokenapi.ManualR initTPOut, err := operations.ExecuteOperation(b, initTPOp, chains.SolanaChains()[chain.Selector], tokenpoolops.Params{ TokenPool: tokenPool, TokenMint: tokenMint, - TokenProgramID: tokenProgramId, + TokenProgramID: tokenProg, Router: solana.PublicKeyFromBytes(routerAddr), RMNRemote: solana.PublicKeyFromBytes(rmnRemoteAddr), }) @@ -288,7 +324,7 @@ func (a *SolanaAdapter) ManualRegistration() *cldf_ops.Sequence[tokenapi.ManualR /// Create Token Multisig /// ///////////////////////////// - if input.SVMExtraArgs != nil && len(input.SVMExtraArgs.CustomerMintAuthorities) > 0 { + if needTokenMultisig { // The multisig will be used as the mint authority (or owner) depending on the pool flow. // We include the TokenPoolSigner PDA as one of the multisig signers so the Token Pool Program // can "sign" via PDA seeds when it needs to act (PDA signing). @@ -304,10 +340,10 @@ func (a *SolanaAdapter) ManualRegistration() *cldf_ops.Sequence[tokenapi.ManualR } createTokenMultisigOutput, err := operations.ExecuteOperation(b, tokensops.CreateTokenMultisig, chains.SolanaChains()[chain.Selector], tokensops.TokenMultisigParams{ - TokenProgram: tokenProgramId, + TokenProgram: tokenProg, Signers: signers, TokenMint: tokenMint, - TokenSymbol: tokenRef.Qualifier, + TokenSymbol: tokenSymb, }) if err != nil { return sequences.OnChainOutput{}, fmt.Errorf("failed to create token multisig on-chain: %w", err) @@ -316,6 +352,41 @@ func (a *SolanaAdapter) ManualRegistration() *cldf_ops.Sequence[tokenapi.ManualR result.BatchOps = append(result.BatchOps, createTokenMultisigOutput.Output.BatchOps...) } + // Manual registration can be used on tokens that aren't in the datastore + // (e.g. a customer deployed the token themselves). If we come across one + // of these tokens, then it will only be added to the datastore if (1) we + // have enough info to rebuild the full ref, and (2) it truly is the case + // that the ref does not already exist in the datastore. + if tokenSymb != "" && !tokenMint.IsZero() && !tokenProg.IsZero() { + tokenType, err := utils.GetTokenProgramType(tokenProg) + if err != nil { + return sequences.OnChainOutput{}, fmt.Errorf("failed to get token program type: %w", err) + } + + tokenRef := datastore.AddressRef{ + ChainSelector: input.ChainSelector, + Qualifier: tokenSymb, + Address: tokenMint.String(), + Version: tokensops.Version, + Labels: datastore.NewLabelSet(), + Type: datastore.ContractType(tokenType.String()), + } + + results := input.ExistingDataStore.Addresses().Filter( + datastore.AddressRefByChainSelector(tokenRef.ChainSelector), + datastore.AddressRefByQualifier(tokenRef.Qualifier), + datastore.AddressRefByVersion(tokenRef.Version), + datastore.AddressRefByAddress(tokenRef.Address), + datastore.AddressRefByType(tokenRef.Type), + ) + if len(results) == 0 { + b.Logger.Infof("No existing datastore entry found for ref (%s). Adding to results.", datastore_utils.SprintRef(tokenRef)) + result.Addresses = append(result.Addresses, tokenRef) + } else { + b.Logger.Infof("Datastore entry already exists for ref (%s). Not adding to results.", datastore_utils.SprintRef(tokenRef)) + } + } + return result, nil }) } @@ -550,18 +621,9 @@ func (a *SolanaAdapter) DeployTokenPoolForToken() *cldf_ops.Sequence[tokenapi.De } func getTokenMintAndTokenProgram(store datastore.DataStore, tokenRef datastore.AddressRef, chain cldf_solana.Chain) (datastore.AddressRef, solana.PublicKey, error) { - ref := datastore.AddressRef{ - ChainSelector: chain.Selector, - } - if tokenRef.Address != "" { - ref.Address = tokenRef.Address - } - if tokenRef.Qualifier != "" { - ref.Qualifier = tokenRef.Qualifier - } - tokenAddr, err := datastore_utils.FindAndFormatRef(store, ref, chain.Selector, datastore_utils.FullRef) + tokenAddr, err := datastore_utils.FindAndFormatRef(store, tokenRef, chain.Selector, datastore_utils.FullRef) if err != nil { - return datastore.AddressRef{}, solana.PublicKey{}, fmt.Errorf("failed to find token address for ref '%+v': %w", ref, err) + return datastore.AddressRef{}, solana.PublicKey{}, fmt.Errorf("failed to find token address for ref '%+v': %w", tokenRef, err) } tokenProgramId, err := utils.GetTokenProgramID(deployment.ContractType(tokenAddr.Type)) if err != nil { diff --git a/chains/solana/deployment/v1_6_0/testadapter/test_adapter.go b/chains/solana/deployment/v1_6_0/testadapter/test_adapter.go index 9cdd6752f6..dae6f11b35 100644 --- a/chains/solana/deployment/v1_6_0/testadapter/test_adapter.go +++ b/chains/solana/deployment/v1_6_0/testadapter/test_adapter.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "maps" + "math" "math/big" "slices" "strconv" @@ -27,6 +28,7 @@ import ( solutils "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/utils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/latest/ccip_common" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/latest/ccip_offramp" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/latest/test_ccip_receiver" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/v0_1_0/ccip_router" solccip "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/ccip" solcommon "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" @@ -333,6 +335,33 @@ func (a *SVMAdapter) CCIPReceiver() []byte { return receiver.Bytes() } +func (a *SVMAdapter) SetReceiverRejectAll(ctx context.Context, rejectAll bool) error { + receiverProgram, err := a.getAddress("TestReceiver") + if err != nil { + return err + } + receiverTargetAccountPDA, _, _ := solana.FindProgramAddress([][]byte{[]byte("counter")}, receiverProgram) + // Set reject all flag in receiver to force reverts + deployer := a.Chain.DeployerKey + ix, err := test_ccip_receiver.NewSetRejectAllInstruction(true, receiverTargetAccountPDA, deployer.PublicKey()).ValidateAndBuild() + if err != nil { + return err + } + ixData, err := ix.Data() + if err != nil { + return err + } + rejectAllIx := solana.NewInstruction(receiverProgram, ix.Accounts(), ixData) + result, err := solcommon.SendAndConfirm(ctx, a.Client, []solana.Instruction{rejectAllIx}, *deployer, solconfig.DefaultCommitment) + if err != nil { + return fmt.Errorf("failed to send and confirm transaction: %w", err) + } + if result.Meta.Err != nil { + return fmt.Errorf("failed to send and confirm transaction: %v", result.Meta.Err) + } + return nil +} + func (a *SVMAdapter) NativeFeeToken() string { return solana.SolMint.String() } @@ -349,13 +378,32 @@ func (a *SVMAdapter) GetExtraArgs(receiver []byte, sourceFamily string, opts ... switch sourceFamily { case chain_selectors.FamilyEVM: - return ccipcommon.SerializeClientSVMExtraArgsV1(msg_hasher163.ClientSVMExtraArgsV1{ + extraArgs := msg_hasher163.ClientSVMExtraArgsV1{ AccountIsWritableBitmap: solccip.GenerateBitMapForIndexes([]int{0, 1}), Accounts: accounts, TokenReceiver: receiverProgram, ComputeUnits: 100_000, AllowOutOfOrderExecution: true, - }) + } + for _, opt := range opts { + switch opt.Name { + case testadapters.ExtraArgGasLimit: + unitsBig := opt.Value.(*big.Int) + if !unitsBig.IsUint64() { + return nil, fmt.Errorf("ComputeUnits is larger than uint32: %d", unitsBig) + } + units := unitsBig.Uint64() + if units > math.MaxUint32 { + return nil, fmt.Errorf("ComputeUnits is larger than uint32: %d", units) + } + extraArgs.ComputeUnits = uint32(units) + case testadapters.ExtraArgOOO: + extraArgs.AllowOutOfOrderExecution = opt.Value.(bool) + default: + // unsupported arg + } + } + return ccipcommon.SerializeClientSVMExtraArgsV1(extraArgs) case chain_selectors.FamilySolana: panic("unimplemented GetExtraArgs(solana->solana)") default: diff --git a/chains/solana/go.mod b/chains/solana/go.mod index dc9779894b..2a2df3ea0e 100644 --- a/chains/solana/go.mod +++ b/chains/solana/go.mod @@ -22,6 +22,7 @@ require ( require ( github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.1 // indirect github.com/cloudevents/sdk-go/v2 v2.16.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -48,9 +49,9 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/text v0.33.0 // indirect @@ -84,13 +85,13 @@ require ( github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect go.mongodb.org/mongo-driver v1.12.2 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.27.1 // indirect - golang.org/x/sys v0.40.0 // indirect + golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/time v0.12.0 // indirect google.golang.org/protobuf v1.36.11 // indirect diff --git a/chains/solana/go.sum b/chains/solana/go.sum index 60fd136f14..e187dea083 100644 --- a/chains/solana/go.sum +++ b/chains/solana/go.sum @@ -61,6 +61,8 @@ github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7 github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -395,8 +397,8 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -419,18 +421,18 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwW go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -588,8 +590,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 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-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/deployment/deploy/feequoterupdater.go b/deployment/deploy/feequoterupdater.go index 8ba2ea4466..fe9486fc79 100644 --- a/deployment/deploy/feequoterupdater.go +++ b/deployment/deploy/feequoterupdater.go @@ -2,6 +2,7 @@ package deploy import ( "fmt" + "math/big" "sync" "github.com/Masterminds/semver/v3" @@ -14,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-ccip/deployment/utils" "github.com/smartcontractkit/chainlink-ccip/deployment/utils/changesets" + datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore" "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" "github.com/smartcontractkit/chainlink-ccip/deployment/utils/sequences" ) @@ -30,13 +32,29 @@ type UpdateFeeQuoterInput struct { type UpdateFeeQuoterInputPerChain struct { FeeQuoterVersion *semver.Version + FeeQuoterConfig *AdditionalFeeQuoterConfig RampsVersion *semver.Version + // RemoteChainSelectors is used to determine which remote chains to pull config for when populating config for the FeeQuoter + // if RemoteChainSelectors is empty, it will pull all remote chain configs using 1.5.0 and 1.6.0 config importer + RemoteChainSelectors []uint64 +} + +type AdditionalFeeQuoterConfig struct { + GasPricesPerRemoteChain map[uint64]string // uses string values (parsed as base-10 big.Int). } type FeeQuoterUpdateInput struct { ChainSelector uint64 ExistingAddresses []datastore.AddressRef - ContractMeta []datastore.ContractMetadata + // PreviousVersions lists the supported config-importer / lane versions that + // should be consulted when deriving the FeeQuoter configuration for this chain. + // It does NOT refer to previous FeeQuoter contract deployment versions. + PreviousVersions []*semver.Version + RemoteChainSelectors []uint64 + AdditionalConfig *AdditionalFeeQuoterConfig + ContractMeta []datastore.ContractMetadata + // TimelockAddress is the address of the CCIP timelock contract to be added as a price updater on the fee quoter. + TimelockAddress string } type SourceChainConfig struct { @@ -106,7 +124,6 @@ func (r *FQAndRampUpdaterRegistry) RegisterConfigImporterVersionResolver(family } func (r *FQAndRampUpdaterRegistry) GetConfigImporter(chainsel uint64, version *semver.Version) (ConfigImporter, bool) { - // Get the chain family from the chain selector family, err := chain_selectors.GetSelectorFamily(chainsel) if err != nil { return nil, false @@ -119,7 +136,6 @@ func (r *FQAndRampUpdaterRegistry) GetConfigImporter(chainsel uint64, version *s } func (r *FQAndRampUpdaterRegistry) GetFeeQuoterUpdater(chainsel uint64, version *semver.Version) (FeeQuoterUpdater[any], bool) { - // Get the chain family from the chain selector family, err := chain_selectors.GetSelectorFamily(chainsel) if err != nil { return nil, false @@ -132,7 +148,6 @@ func (r *FQAndRampUpdaterRegistry) GetFeeQuoterUpdater(chainsel uint64, version } func (r *FQAndRampUpdaterRegistry) GetRampUpdater(chainsel uint64, version *semver.Version) (RampUpdater, bool) { - // Get the chain family from the chain selector family, err := chain_selectors.GetSelectorFamily(chainsel) if err != nil { return nil, false @@ -145,7 +160,6 @@ func (r *FQAndRampUpdaterRegistry) GetRampUpdater(chainsel uint64, version *semv } func (r *FQAndRampUpdaterRegistry) GetConfigImporterVersionResolver(chainsel uint64) (LaneVersionResolver, bool) { - // Get the chain family from the chain selector family, err := chain_selectors.GetSelectorFamily(chainsel) if err != nil { return nil, false @@ -173,10 +187,13 @@ func GetFQAndRampUpdaterRegistry() *FQAndRampUpdaterRegistry { } // UpdateFeeQuoterChangeset creates a changeset that updates FeeQuoter contracts on specified chains. +// This can support either upgrading to a 2.0 or higher version of the FeeQuoter, which allows re-configuration of the existing contract, or deploying a new FeeQuoter and updating Ramps to point to it. // It first optionally populates configuration values, then creates FeeQuoterUpdateInput, // deploys or updates the FeeQuoter contract, and finally updates the Ramps contracts to use the new FeeQuoter address. -func UpdateFeeQuoterChangeset(fquRegistry *FQAndRampUpdaterRegistry, mcmsRegistry *changesets.MCMSReaderRegistry) cldf.ChangeSetV2[UpdateFeeQuoterInput] { - return cldf.CreateChangeSet(updateFeeQuoterApply(fquRegistry, mcmsRegistry), updateFeeQuoterVerify()) +// This also supports downgrading the FQ contract to a prior version (i.e. a rollback) where only the ramps +// are updated and the existing FQ is not touched. This would be triggered when specifying a FQ version < 2.0.0, which do not support re-configuration, and an existing FQ address is found in the datastore for the chain. +func UpdateFeeQuoterChangeset() cldf.ChangeSetV2[UpdateFeeQuoterInput] { + return cldf.CreateChangeSet(updateFeeQuoterApply(), updateFeeQuoterVerify()) } func updateFeeQuoterVerify() func(cldf.Environment, UpdateFeeQuoterInput) error { @@ -188,88 +205,166 @@ func updateFeeQuoterVerify() func(cldf.Environment, UpdateFeeQuoterInput) error if perChainInput.FeeQuoterVersion == nil { return fmt.Errorf("fee quoter version is required for chain selector %d", chainSel) } - if perChainInput.RampsVersion == nil { - return fmt.Errorf("ramps version is required for chain selector %d", chainSel) + if perChainInput.FeeQuoterConfig != nil { + for remoteChainSel := range perChainInput.FeeQuoterConfig.GasPricesPerRemoteChain { + _, ok := new(big.Int).SetString(perChainInput.FeeQuoterConfig.GasPricesPerRemoteChain[remoteChainSel], 10) + if !ok { + return fmt.Errorf("invalid gas price %s for remote chain selector %d in fee quoter config for chain selector %d", perChainInput.FeeQuoterConfig.GasPricesPerRemoteChain[remoteChainSel], remoteChainSel, chainSel) + } + } + } + _, err := datastore_utils.FindAndFormatRef(e.DataStore, datastore.AddressRef{ + ChainSelector: chainSel, + Type: datastore.ContractType(utils.FeeQuoter), + Version: perChainInput.FeeQuoterVersion, + }, chainSel, datastore_utils.FullRef) + if err != nil { + // errors are alright if we don't expect to find the ref + // but we only support deploying/updating fee quoters with versions >= 2.0.0 + supportedVersion := semver.MustParse("2.0.0") + if perChainInput.FeeQuoterVersion.LessThan(supportedVersion) { + return fmt.Errorf("fee quoter address not found for chain selector %d and version %s: %w", chainSel, perChainInput.FeeQuoterVersion.String(), err) + } } } return nil } } -func updateFeeQuoterApply(fquRegistry *FQAndRampUpdaterRegistry, mcmsRegistry *changesets.MCMSReaderRegistry) func(cldf.Environment, UpdateFeeQuoterInput) (cldf.ChangesetOutput, error) { +func updateFeeQuoterApply() func(cldf.Environment, UpdateFeeQuoterInput) (cldf.ChangesetOutput, error) { return func(e cldf.Environment, input UpdateFeeQuoterInput) (cldf.ChangesetOutput, error) { batchOps := make([]mcms_types.BatchOperation, 0) reports := make([]cldf_ops.Report[any, any], 0) addressRefs := make([]datastore.AddressRef, 0) contractMetadata := make([]datastore.ContractMetadata, 0) + fquRegistry := GetFQAndRampUpdaterRegistry() + mcmsRegistry := changesets.GetRegistry() for chainSel, perChainInput := range input.Chains { - fquUpdater, ok := fquRegistry.GetFeeQuoterUpdater(chainSel, perChainInput.FeeQuoterVersion) - if !ok { - return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("FeeQuoterUpdater", chainSel, perChainInput.FeeQuoterVersion) - } - rampUpdater, ok := fquRegistry.GetRampUpdater(chainSel, perChainInput.RampsVersion) - if !ok { - return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("RampUpdater", chainSel, perChainInput.RampsVersion) - } - versionResolver, ok := fquRegistry.GetConfigImporterVersionResolver(chainSel) - if !ok { - return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("ConfigImporterVersionResolver", chainSel, nil) - } - // Resolve the config importer version to use for this chain - _, configImporterVersions, err := versionResolver.DeriveLaneVersionsForChain(e, chainSel) - if err != nil { - return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve config importer version for chain %d: %w", chainSel, err) + var feeQuoterAddrRef datastore.AddressRef + feeQuoterAddrRefs := e.DataStore.Addresses().Filter( + datastore.AddressRefByChainSelector(chainSel), + datastore.AddressRefByType(datastore.ContractType(utils.FeeQuoter)), + datastore.AddressRefByVersion(perChainInput.FeeQuoterVersion), + ) + if len(feeQuoterAddrRefs) > 0 { + feeQuoterAddrRef = feeQuoterAddrRefs[0] + e.Logger.Infof("Found existing FeeQuoter address %s for chain selector %d and version %s", + feeQuoterAddrRef.Address, chainSel, perChainInput.FeeQuoterVersion.String()) } - contractMeta := make([]datastore.ContractMetadata, 0) - for _, version := range configImporterVersions { - configImporter, ok := fquRegistry.GetConfigImporter(chainSel, version) + isNewFeeQuoterDeployment := len(feeQuoterAddrRefs) == 0 + if perChainInput.FeeQuoterVersion.GreaterThanEqual(semver.MustParse("2.0.0")) { + e.Logger.Infof("No existing FeeQuoter address found for chain selector %d and version %s, proceeding with deployment and upgrade", chainSel, perChainInput.FeeQuoterVersion.String()) + fquUpdater, ok := fquRegistry.GetFeeQuoterUpdater(chainSel, perChainInput.FeeQuoterVersion) if !ok { - return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("ConfigImporter", chainSel, version) + return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("FeeQuoterUpdater", chainSel, perChainInput.FeeQuoterVersion) } - populateCfgOutput, err := PopulateMetaDataFromConfigImporter(e, configImporter, chainSel) + + versionResolver, ok := fquRegistry.GetConfigImporterVersionResolver(chainSel) + if !ok { + return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("ConfigImporterVersionResolver", chainSel, nil) + } + _, configImporterVersions, err := versionResolver.DeriveLaneVersionsForChain(e, chainSel) if err != nil { - return cldf.ChangesetOutput{}, fmt.Errorf("failed to populate metadata from config importer for chain %d: %w", chainSel, err) + return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve config importer version for chain %d: %w", chainSel, err) } - if len(populateCfgOutput.Metadata.Contracts) == 0 { - return cldf.ChangesetOutput{}, fmt.Errorf("no contract metadata returned from populate config on chain %d", chainSel) + contractMeta := make([]datastore.ContractMetadata, 0) + for _, version := range configImporterVersions { + configImporter, ok := fquRegistry.GetConfigImporter(chainSel, version) + if !ok { + return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("ConfigImporter", chainSel, version) + } + err := configImporter.InitializeAdapter(e, chainSel) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to initialize config importer for chain %d: %w", chainSel, err) + } + supportedTokensPerRemoteChain, err := configImporter.SupportedTokensPerRemoteChain(e, chainSel) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get supported tokens per remote chain for chain %d: %w", chainSel, err) + } + connectedChains, err := configImporter.ConnectedChains(e, chainSel) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get connected chains for chain %d: %w", chainSel, err) + } + populateConfigReport, err := cldf_ops.ExecuteSequence(e.OperationsBundle, configImporter.SequenceImportConfig(), e.BlockChains, ImportConfigPerChainInput{ + ChainSelector: chainSel, + RemoteChains: connectedChains, + TokensPerRemoteChain: supportedTokensPerRemoteChain, + }) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to populate config for FeeQuoter on chain %d: %w", chainSel, err) + } + if len(populateConfigReport.Output.Metadata.Contracts) == 0 { + return cldf.ChangesetOutput{}, fmt.Errorf("no contract metadata returned from populate config for FeeQuoter on chain %d", chainSel) + } + + contractMeta = append(contractMeta, populateConfigReport.Output.Metadata.Contracts...) + contractMetadata = append(contractMetadata, populateConfigReport.Output.Metadata.Contracts...) } - contractMeta = append(contractMeta, populateCfgOutput.Metadata.Contracts...) - contractMetadata = append(contractMetadata, populateCfgOutput.Metadata.Contracts...) - } - // Create FeeQuoterUpdateInput - reportFQInputCreation, err := cldf_ops.ExecuteSequence(e.OperationsBundle, fquUpdater.SequenceFeeQuoterInputCreation(), e.BlockChains, FeeQuoterUpdateInput{ - ChainSelector: chainSel, - ExistingAddresses: e.DataStore.Addresses().Filter(datastore.AddressRefByChainSelector(chainSel)), - ContractMeta: contractMeta, - }) - if err != nil { - return cldf.ChangesetOutput{}, fmt.Errorf("failed to create FeeQuoterUpdateInput for chain %d: %w", chainSel, err) - } - // Deploy or update FeeQuoter - reportFQUpdate, err := cldf_ops.ExecuteSequence(e.OperationsBundle, fquUpdater.SequenceDeployOrUpdateFeeQuoter(), e.BlockChains, reportFQInputCreation.Output) - if err != nil { - return cldf.ChangesetOutput{}, fmt.Errorf("failed to deploy or update FeeQuoter for chain %d: %w", chainSel, err) + timelockAddr := "" + family, err := chain_selectors.GetSelectorFamily(chainSel) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get chain family for selector %d: %w", chainSel, err) + } + mcmsReader, ok := mcmsRegistry.GetMCMSReader(family) + if !ok { + return cldf.ChangesetOutput{}, fmt.Errorf("no MCMS reader registered for chain family '%s'", family) + } + timelockRef, err := mcmsReader.GetTimelockRef(e, chainSel, input.MCMS) + if err != nil { + e.Logger.Warnf("Could not resolve timelock ref for chain %d, skipping timelock as price updater: %v", chainSel, err) + } else { + timelockAddr = timelockRef.Address + } + + reportFQInputCreation, err := cldf_ops.ExecuteSequence(e.OperationsBundle, fquUpdater.SequenceFeeQuoterInputCreation(), e.BlockChains, FeeQuoterUpdateInput{ + ChainSelector: chainSel, + ExistingAddresses: e.DataStore.Addresses().Filter(datastore.AddressRefByChainSelector(chainSel)), + ContractMeta: contractMeta, + RemoteChainSelectors: perChainInput.RemoteChainSelectors, + TimelockAddress: timelockAddr, + AdditionalConfig: perChainInput.FeeQuoterConfig, + PreviousVersions: configImporterVersions, + }) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to create FeeQuoterUpdateInput for chain %d: %w", chainSel, err) + } + reportFQUpdate, err := cldf_ops.ExecuteSequence(e.OperationsBundle, fquUpdater.SequenceDeployOrUpdateFeeQuoter(), e.BlockChains, reportFQInputCreation.Output) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to deploy or update FeeQuoter for chain %d: %w", chainSel, err) + } + batchOps = append(batchOps, reportFQUpdate.Output.BatchOps...) + addressRefs = append(addressRefs, reportFQUpdate.Output.Addresses...) + reports = append(reports, reportFQUpdate.ExecutionReports...) + if len(reportFQUpdate.Output.Addresses) == 0 { + return cldf.ChangesetOutput{}, fmt.Errorf("no FeeQuoter address returned for chain %d", chainSel) + } + feeQuoterAddrRef = reportFQUpdate.Output.Addresses[len(reportFQUpdate.Output.Addresses)-1] + + if isNewFeeQuoterDeployment && timelockAddr != "" { + fqTransferBatches, fqTransferReports, err := TransferToTimelock(chainSel, &e, input.MCMS, []datastore.AddressRef{feeQuoterAddrRef}) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to transfer ownership to timelock for chain %d: %w", chainSel, err) + } + batchOps = append(batchOps, fqTransferBatches...) + reports = append(reports, fqTransferReports...) } - batchOps = append(batchOps, reportFQUpdate.Output.BatchOps...) - addressRefs = append(addressRefs, reportFQUpdate.Output.Addresses...) - reports = append(reports, reportFQUpdate.ExecutionReports...) - if len(reportFQUpdate.Output.Addresses) == 0 { - return cldf.ChangesetOutput{}, fmt.Errorf("no FeeQuoter address returned for chain %d", chainSel) } - // Update Ramps with new FeeQuoter address - // fetch the address refs - feeQuoterAddrRef := reportFQUpdate.Output.Addresses[len(reportFQUpdate.Output.Addresses)-1] if perChainInput.RampsVersion != nil { + if feeQuoterAddrRef.Address == "" { + return cldf.ChangesetOutput{}, fmt.Errorf("fee quoter address ref is required to update ramps for chain %d", chainSel) + } + rampUpdater, ok := fquRegistry.GetRampUpdater(chainSel, perChainInput.RampsVersion) + if !ok { + return cldf.ChangesetOutput{}, utils.ErrNoAdapterForSelectorRegistered("RampUpdater", chainSel, perChainInput.RampsVersion) + } rampsInput := UpdateRampsInput{ ChainSelector: chainSel, FeeQuoterAddress: feeQuoterAddrRef, } - // Resolve Ramps input resolvedRampsInput, err := rampUpdater.ResolveRampsInput(e, rampsInput) if err != nil { return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve ramps input for chain %d: %w", chainSel, err) } - // Execute Ramps update sequence reportRampsUpdate, err := cldf_ops.ExecuteSequence(e.OperationsBundle, rampUpdater.SequenceUpdateRampsWithFeeQuoter(), e.BlockChains, resolvedRampsInput) if err != nil { return cldf.ChangesetOutput{}, fmt.Errorf("failed to update ramps with FeeQuoter for chain %d: %w", chainSel, err) @@ -279,7 +374,6 @@ func updateFeeQuoterApply(fquRegistry *FQAndRampUpdaterRegistry, mcmsRegistry *c reports = append(reports, reportRampsUpdate.ExecutionReports...) } } - // Prepare datastore with all address refs ds := datastore.NewMemoryDataStore() for _, addrRef := range addressRefs { if err := ds.Addresses().Add(addrRef); err != nil { diff --git a/deployment/deploy/mcms.go b/deployment/deploy/mcms.go index 0c6c543ebc..a8606eeddd 100644 --- a/deployment/deploy/mcms.go +++ b/deployment/deploy/mcms.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math/big" + "slices" "github.com/Masterminds/semver/v3" "github.com/ethereum/go-ethereum/common" @@ -11,7 +12,6 @@ import ( "github.com/smartcontractkit/chainlink-deployments-framework/datastore" cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" - mcms_types "github.com/smartcontractkit/mcms/types" mcmstypes "github.com/smartcontractkit/mcms/types" "github.com/smartcontractkit/chainlink-ccip/deployment/utils" @@ -62,6 +62,119 @@ type GrantAdminRoleToTimelockConfig struct { AdapterVersion *semver.Version `json:"adapterVersion"` } +type UpdateMCMSConfigInputPerChainWithSelector struct { + UpdateMCMSConfigInputPerChain + ChainSelector uint64 + ExistingAddresses []datastore.AddressRef // needed for Solana +} + +type UpdateMCMSConfigInputPerChain struct { + MCMConfig mcmstypes.Config + MCMContracts []datastore.AddressRef +} + +type UpdateMCMSConfigInput struct { + Chains map[uint64]UpdateMCMSConfigInputPerChain `json:"chains"` + AdapterVersion *semver.Version `json:"adapterVersion"` + MCMS mcms.Input `json:"mcms"` +} + +func UpdateMCMSConfig(deployerReg *DeployerRegistry, mcmsRegistry *changesets.MCMSReaderRegistry) cldf.ChangeSetV2[UpdateMCMSConfigInput] { + return cldf.CreateChangeSet( + updateMCMSConfigApply(deployerReg, mcmsRegistry), + updateMCMSConfigVerify(deployerReg, mcmsRegistry), + ) +} + +func updateMCMSConfigVerify(_ *DeployerRegistry, _ *changesets.MCMSReaderRegistry) func(cldf.Environment, UpdateMCMSConfigInput) error { + return func(e cldf.Environment, cfg UpdateMCMSConfigInput) error { + if cfg.AdapterVersion == nil { + return errors.New("adapter version is required") + } + + // validate mcms input + if err := cfg.MCMS.Validate(); err != nil { + return err + } + + validTypes := []string{utils.BypasserManyChainMultisig.String(), utils.ProposerManyChainMultisig.String(), + utils.CancellerManyChainMultisig.String()} + + // validate each contract + for _, chainCfg := range cfg.Chains { + for _, contract := range chainCfg.MCMContracts { + if !slices.Contains(validTypes, contract.Type.String()) { + return errors.New("type of contract needs to be mcms") + } + if len(contract.Qualifier) == 0 { + return errors.New("mcms contract qualifier cannot be empty") + } + if len(contract.Version.String()) == 0 { + return errors.New("mcms contract version cannot be empty") + } + } + } + + return nil + } +} + +func updateMCMSConfigApply(d *DeployerRegistry, mcmsRegistry *changesets.MCMSReaderRegistry) func(cldf.Environment, UpdateMCMSConfigInput) (cldf.ChangesetOutput, error) { + return func(e cldf.Environment, cfg UpdateMCMSConfigInput) (cldf.ChangesetOutput, error) { + batchOps := make([]mcmstypes.BatchOperation, 0) + reports := make([]cldf_ops.Report[any, any], 0) + for selector, chainCfg := range cfg.Chains { + family, err := chain_selectors.GetSelectorFamily(selector) + if err != nil { + return cldf.ChangesetOutput{}, err + } + deployer, exists := d.GetDeployer(family, cfg.AdapterVersion) + if !exists { + return cldf.ChangesetOutput{}, fmt.Errorf("no deployer registered for chain family %s and version %s", family, cfg.AdapterVersion.String()) + } + + // If partial refs are provided, resolve to full refs + mcmsContracts := []datastore.AddressRef{} + for _, contract := range chainCfg.MCMContracts { + mcmsQualifier := contract.Qualifier + mcmsRef, err := datastore_utils.FindAndFormatRef(e.DataStore, datastore.AddressRef{ + ChainSelector: selector, + Type: contract.Type, + Version: contract.Version, + Qualifier: mcmsQualifier, + }, selector, datastore_utils.FullRef) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to find mcms ref with qualifier %s on chain with selector %d", mcmsQualifier, selector) + } + + mcmsContracts = append(mcmsContracts, mcmsRef) + } + + // find existing addresses for this chain from the env + existingAddrs := d.ExistingAddressesForChain(e, selector) + + // Call the set mcms config sequence + seqCfg := UpdateMCMSConfigInputPerChainWithSelector{ + UpdateMCMSConfigInputPerChain: chainCfg, + ChainSelector: selector, + ExistingAddresses: existingAddrs, + } + + report, err := cldf_ops.ExecuteSequence(e.OperationsBundle, deployer.UpdateMCMSConfig(), e.BlockChains, seqCfg) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to Update MCMS Config on chain with selector %d: %w", selector, err) + } + batchOps = append(batchOps, report.Output.BatchOps...) + reports = append(reports, report.ExecutionReports...) + } + + return changesets.NewOutputBuilder(e, mcmsRegistry). + WithReports(reports). + WithBatchOps(batchOps). + Build(cfg.MCMS) + } +} + func GrantAdminRoleToTimelock(deployerReg *DeployerRegistry, mcmsRegistry *changesets.MCMSReaderRegistry) cldf.ChangeSetV2[GrantAdminRoleToTimelockConfig] { return cldf.CreateChangeSet( grantAdminRoleToTimelockApply(deployerReg, mcmsRegistry), @@ -184,7 +297,7 @@ func deployMCMSApply( finalize bool) func(cldf.Environment, MCMSDeploymentConfig) (cldf.ChangesetOutput, error) { return func(e cldf.Environment, cfg MCMSDeploymentConfig) (cldf.ChangesetOutput, error) { reports := make([]cldf_ops.Report[any, any], 0) - batchOps := make([]mcms_types.BatchOperation, 0) + batchOps := make([]mcmstypes.BatchOperation, 0) ds := datastore.NewMemoryDataStore() for selector, mcmsCfg := range cfg.Chains { family, err := chain_selectors.GetSelectorFamily(selector) diff --git a/deployment/deploy/product.go b/deployment/deploy/product.go index f15047ad2d..c6f087fe03 100644 --- a/deployment/deploy/product.go +++ b/deployment/deploy/product.go @@ -33,6 +33,7 @@ type Deployer interface { FinalizeDeployMCMS() *cldf_ops.Sequence[MCMSDeploymentConfigPerChainWithAddress, sequences.OnChainOutput, cldf_chain.BlockChains] SetOCR3Config() *cldf_ops.Sequence[SetOCR3ConfigInput, sequences.OnChainOutput, cldf_chain.BlockChains] GrantAdminRoleToTimelock() *cldf_ops.Sequence[GrantAdminRoleToTimelockConfigPerChainWithSelector, sequences.OnChainOutput, cldf_chain.BlockChains] + UpdateMCMSConfig() *cldf_ops.Sequence[UpdateMCMSConfigInputPerChainWithSelector, sequences.OnChainOutput, cldf_chain.BlockChains] } type DeployerRegistry struct { diff --git a/deployment/deploy/transfer_ownership.go b/deployment/deploy/transfer_ownership.go index b52071bc73..53ba9dd8c5 100644 --- a/deployment/deploy/transfer_ownership.go +++ b/deployment/deploy/transfer_ownership.go @@ -1,7 +1,10 @@ package deploy import ( + "fmt" + "github.com/Masterminds/semver/v3" + chain_selectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" @@ -86,10 +89,6 @@ func transferOwnershipApply(cr *TransferOwnershipAdapterRegistry, mcmsRegistry * if err != nil { return cldf.ChangesetOutput{}, err } - err = adapter.InitializeTimelockAddress(e, input.MCMS) - if err != nil { - return cldf.ChangesetOutput{}, err - } // if partial refs are provided, resolve to full refs for i, contractRef := range perChainInputs.ContractRef { fullRef, err := datastore_utils.FindAndFormatRef(e.DataStore, contractRef, perChainInputs.ChainSelector, datastore_utils.FullRef) @@ -98,24 +97,12 @@ func transferOwnershipApply(cr *TransferOwnershipAdapterRegistry, mcmsRegistry * } perChainInputs.ContractRef[i] = fullRef } - report, err := cldf_ops.ExecuteSequence(e.OperationsBundle, adapter.SequenceTransferOwnershipViaMCMS(), e.BlockChains, perChainInputs) + chainBatchOps, chainReports, err := transferAndAcceptOwnership(e, adapter, perChainInputs, input.MCMS) if err != nil { return cldf.ChangesetOutput{}, err } - batchOps = append(batchOps, report.Output.BatchOps...) - reports = append(reports, report.ExecutionReports...) - needAcceptOwnership, err := adapter.ShouldAcceptOwnershipWithTransferOwnership(e, perChainInputs) - if err != nil { - return cldf.ChangesetOutput{}, err - } - if needAcceptOwnership { - report, err = cldf_ops.ExecuteSequence(e.OperationsBundle, adapter.SequenceAcceptOwnership(), e.BlockChains, perChainInputs) - if err != nil { - return cldf.ChangesetOutput{}, err - } - batchOps = append(batchOps, report.Output.BatchOps...) - reports = append(reports, report.ExecutionReports...) - } + batchOps = append(batchOps, chainBatchOps...) + reports = append(reports, chainReports...) } return changesets.NewOutputBuilder(e, mcmsRegistry). WithReports(reports). @@ -132,3 +119,69 @@ func transferOwnershipVerify(cr *TransferOwnershipAdapterRegistry, mcmsRegistry return nil } } + +func TransferToTimelock(chainSel uint64, e *cldf.Environment, mcmsInput mcms.Input, addressRefs []datastore.AddressRef) ([]mcms_types.BatchOperation, []cldf_ops.Report[any, any], error) { + mcmsRegistry := changesets.GetRegistry() + transferOwnershipReg := GetTransferOwnershipRegistry() + family, err := chain_selectors.GetSelectorFamily(chainSel) + if err != nil { + return nil, nil, fmt.Errorf("failed to get chain family for selector %d: %w", chainSel, err) + } + mcmsReader, ok := mcmsRegistry.GetMCMSReader(family) + if !ok { + return nil, nil, fmt.Errorf("no MCMS reader registered for chain family '%s'", family) + } + timelockRef, err := mcmsReader.GetTimelockRef(*e, chainSel, mcmsInput) + if err != nil { + return nil, nil, fmt.Errorf("failed to get timelock ref for chain %d: %w", chainSel, err) + } + + adapter, err := transferOwnershipReg.GetAdapterByChainSelector(chainSel, MCMSVersion) + if err != nil { + return nil, nil, fmt.Errorf("failed to get transfer ownership adapter for chain %d: %w", chainSel, err) + } + + ownershipInput := TransferOwnershipPerChainInput{ + ChainSelector: chainSel, + ContractRef: addressRefs, + ProposedOwner: timelockRef.Address, + } + return transferAndAcceptOwnership(*e, adapter, ownershipInput, mcmsInput) +} + +// transferAndAcceptOwnership executes the transfer ownership sequence via MCMS and, +// if needed, the accept ownership sequence for the given contracts on a single chain. +func transferAndAcceptOwnership( + e cldf.Environment, + adapter TransferOwnershipAdapter, + input TransferOwnershipPerChainInput, + mcmsInput mcms.Input, +) ([]mcms_types.BatchOperation, []cldf_ops.Report[any, any], error) { + batchOps := make([]mcms_types.BatchOperation, 0) + reports := make([]cldf_ops.Report[any, any], 0) + + if err := adapter.InitializeTimelockAddress(e, mcmsInput); err != nil { + return nil, nil, fmt.Errorf("failed to initialize timelock address for chain %d: %w", input.ChainSelector, err) + } + + transferReport, err := cldf_ops.ExecuteSequence(e.OperationsBundle, adapter.SequenceTransferOwnershipViaMCMS(), e.BlockChains, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to transfer ownership on chain %d: %w", input.ChainSelector, err) + } + batchOps = append(batchOps, transferReport.Output.BatchOps...) + reports = append(reports, transferReport.ExecutionReports...) + + needAccept, err := adapter.ShouldAcceptOwnershipWithTransferOwnership(e, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to check accept ownership on chain %d: %w", input.ChainSelector, err) + } + if needAccept { + acceptReport, err := cldf_ops.ExecuteSequence(e.OperationsBundle, adapter.SequenceAcceptOwnership(), e.BlockChains, input) + if err != nil { + return nil, nil, fmt.Errorf("failed to accept ownership on chain %d: %w", input.ChainSelector, err) + } + batchOps = append(batchOps, acceptReport.Output.BatchOps...) + reports = append(reports, acceptReport.ExecutionReports...) + } + return batchOps, reports, nil +} diff --git a/deployment/docs/_category_.yaml b/deployment/docs/_category_.yaml new file mode 100644 index 0000000000..29bf607e90 --- /dev/null +++ b/deployment/docs/_category_.yaml @@ -0,0 +1,2 @@ +label: "CCIP Deployment Tooling" +position: 1 diff --git a/deployment/docs/architecture.md b/deployment/docs/architecture.md new file mode 100644 index 0000000000..6858138b18 --- /dev/null +++ b/deployment/docs/architecture.md @@ -0,0 +1,220 @@ +--- +title: "Architecture Guide" +sidebar_label: "Architecture" +sidebar_position: 2 +--- + +# Architecture Guide + +This document describes the design principles, patterns, and data flow of the CCIP Deployment Tooling API. + +## Design Principles + +1. **Chain Agnosticism**: Changesets operate without knowledge of chain-specific details. All chain-specific logic is encapsulated in adapters. +2. **Version-Aware Adapters**: Different contract versions have different adapters registering under the same interface. This allows multiple versions to coexist. +3. **Singleton Registries**: All adapter registries are global singletons, initialized once via Go `init()` functions and accessed thread-safely. +4. **Stateful Retry via Operations**: Operations produce reports that enable resuming long-running sequences from the point of failure. +5. **MCMS-First Governance**: All write operations can be routed through Multi-Chain Multi-Sig (MCMS) proposals when the deployer key is not the contract owner. + +## Adapter-Registry Pattern + +The core architectural pattern is a registry of chain-family adapters keyed by `chainFamily-version`: + +``` +Changeset (entry point) + | + v +Registry.GetAdapter(chainFamily, version) + | + v +Adapter (implements interface) + | + v +Sequence (ordered operations) + | + v +Operations (single side-effects) +``` + +### How Registries Work + +Each registry is a singleton created via `sync.Once`: + +```go +var ( + singletonRegistry *DeployerRegistry + once sync.Once +) + +func GetRegistry() *DeployerRegistry { + once.Do(func() { + singletonRegistry = newDeployerRegistry() + }) + return singletonRegistry +} +``` + +Adapters register themselves in Go `init()` functions, which run automatically when the package is imported: + +```go +func init() { + v := semver.MustParse("1.6.0") + deploy.GetRegistry().RegisterDeployer(chain_selectors.FamilyEVM, v, &EVMAdapter{}) + lanes.GetLaneAdapterRegistry().RegisterLaneAdapter(chain_selectors.FamilyEVM, v, &EVMAdapter{}) + // ... more registrations +} +``` + +Registry keys are constructed using `utils.NewRegistererID(chainFamily, version)`, which produces strings like `"evm-1.6.0"` or `"solana-1.6.0"`. The `MCMSReaderRegistry` is the exception -- it uses only `chainFamily` as the key (one reader per family, not per version). + +All registries use `sync.Mutex` for thread-safe concurrent access. + +### Available Registries + +| Registry | Key Format | Singleton Accessor | Source | +|----------|------------|-------------------|--------| +| `DeployerRegistry` | `chainFamily-version` | `deploy.GetRegistry()` | `deploy/product.go` | +| `LaneAdapterRegistry` | `chainFamily-version` | `lanes.GetLaneAdapterRegistry()` | `lanes/product.go` | +| `TokenAdapterRegistry` | `chainFamily-version` | `tokens.GetTokenAdapterRegistry()` | `tokens/product.go` | +| `FeeAdapterRegistry` | `chainFamily-version` | `fees.GetRegistry()` | `fees/product.go` | +| `MCMSReaderRegistry` | `chainFamily` | `changesets.GetRegistry()` | `utils/changesets/output.go` | +| `TransferOwnershipAdapterRegistry` | `chainFamily-version` | `deploy.GetTransferOwnershipRegistry()` | `deploy/product.go` | +| `CurseRegistry` | `chainFamily-version` | `fastcurse.GetCurseRegistry()` | `fastcurse/product.go` | + +## The Three-Level Hierarchy + +### Operations + +An Operation is the most granular executable action -- a single contract deployment, transaction, or read call. Each operation has **at most one side effect**. + +Operations produce reports that serialize their inputs and outputs, enabling stateful retries. If a sequence fails partway through, it can resume from the last successful operation. + +Chain-specific implementations define operations differently: +- **EVM**: Uses `contract.NewRead`, `contract.NewWrite`, `contract.NewDeploy` helpers that wrap gethwrapper methods +- **Solana**: Uses `operations.NewOperation` with chain-specific transaction building + +### Sequences + +A Sequence is an ordered collection of operations that represents a complete workflow (e.g., "deploy all CCIP contracts on a chain" or "configure a lane between two chains"). + +Key properties: +- Accept a **serializable input** and return `OnChainOutput` +- Depend on minimal infrastructure (typically just `cldf_chain.BlockChains`) +- Target a **single chain** for simplicity +- Can be composed using `RunAndMergeSequence` to aggregate outputs + +```go +type OnChainOutput struct { + Addresses []datastore.AddressRef // Deployed/managed contracts + Metadata Metadata // Contract, chain, and env metadata + BatchOps []mcms_types.BatchOperation // Operations for MCMS proposals +} +``` + +### Changesets + +A Changeset wraps a sequence with deployment environment context. It handles: +1. Reading addresses from a DataStore +2. Passing resolved addresses into sequences as input +3. Producing MCMS proposals from sequence output using `OutputBuilder` +4. Writing new addresses and metadata back to the DataStore + +Changesets follow the `cldf.ChangeSetV2[Config]` pattern from chainlink-deployments-framework, with a `verify` function (validates config) and an `apply` function (executes the logic). + +## Cross-Chain Dispatch Flow + +When a changeset needs to operate across multiple chains, it follows this dispatch pattern: + +1. The changeset receives a config with `map[uint64]PerChainConfig` (chain selector -> per-chain config) +2. For each chain selector, it resolves the chain family using `chain_selectors.GetSelectorFamily(chainSelector)` +3. It looks up the correct adapter from the registry using the family and version +4. It delegates to that adapter's sequence, passing chain-specific input +5. It aggregates outputs from all chains into a single `ChangesetOutput` + +Example flow from `DeployContracts`: + +```go +for chainSelector, chainConfig := range config.Chains { + family, _ := chain_selectors.GetSelectorFamily(chainSelector) + deployer, _ := registry.GetDeployer(family, chainConfig.Version) + // Execute the deployer's sequence for this chain + seq := deployer.DeployChainContracts() + report, _ := operations.ExecuteSequence(bundle, seq, blockchains, input) +} +``` + +## DataStore Integration + +The DataStore is the central persistence layer for deployment state. It stores contract addresses, metadata, and environment information. + +### AddressRef + +`datastore.AddressRef` is the universal contract pointer: + +```go +type AddressRef struct { + ChainSelector uint64 + Address string + Type ContractType + Version *semver.Version + Qualifier string +} +``` + +### Flow + +1. **Read**: Changesets read existing addresses from the DataStore to resolve dependencies (e.g., finding the Router address to pass to OffRamp deployment) +2. **Write**: Sequence outputs (`OnChainOutput.Addresses`) are written back to the DataStore after execution +3. **Metadata**: Contract, chain, and environment metadata is upserted via `WriteMetadataToDatastore` + +## MCMS Proposal Construction + +The `OutputBuilder` is a builder pattern for constructing changeset outputs that include MCMS (Multi-Chain Multi-Sig) proposals. + +### Flow + +1. Sequences return `BatchOperation` objects for write operations that require MCMS approval +2. The `OutputBuilder` collects all batch operations across sequences +3. On `Build(mcms.Input)`, it: + - Resolves timelock addresses per chain via `MCMSReader.GetTimelockRef` + - Fetches chain metadata via `MCMSReader.GetChainMetadata` + - Constructs a `TimelockProposal` containing all batch operations +4. The proposal is included in the `ChangesetOutput.MCMSTimelockProposals` + +```go +output, err := changesets.NewOutputBuilder(env, mcmsRegistry). + WithReports(reports). + WithBatchOps(batchOps). + WithDataStore(ds). + Build(mcmsInput) +``` + +The `MCMSReader` interface is what allows this to work across chain families -- each family implements its own way of resolving timelock addresses and chain metadata. + +## Versioning Strategy + +### Contract Versions + +Multiple contract versions coexist in the system: +- **Version constants**: `Version_1_0_0`, `Version_1_5_0`, `Version_1_5_1`, `Version_1_6_0`, `Version_1_6_1` (defined in `utils/common.go`) +- **Adapter registration**: Each version registers its own adapter (e.g., EVM has adapters from v1_0_0 through v1_6_5) + +### Token Adapter Versioning + +Token adapters have a special versioning requirement: any token pool version must be connectable to any other. This means there is one `TokenAdapterRegistry` (not version-scoped), and each chain-family-version combination registers separately. + +### Lane Adapter Versioning + +Lane adapters are version-scoped -- a v1.6.0 lane and a v1.7.0 lane may have different configuration requirements. Separate registries can exist per major API version. + +### Chain Family Selectors + +Each chain family has a unique 4-byte selector used on-chain: + +| Family | Selector | Constant | +|--------|----------|----------| +| EVM | `0x2812d52c` | `EVMFamilySelector` | +| SVM (Solana) | `0x1e10bdc4` | `SVMFamilySelector` | +| Aptos | `0xac77ffec` | `AptosFamilySelector` | +| TVM (TON) | `0x647e2ba9` | `TVMFamilySelector` | +| Sui | `0xc4e05953` | `SuiFamilySelector` | diff --git a/deployment/docs/changesets.md b/deployment/docs/changesets.md new file mode 100644 index 0000000000..1b45f698fe --- /dev/null +++ b/deployment/docs/changesets.md @@ -0,0 +1,441 @@ +--- +title: "Changesets Reference" +sidebar_label: "Changesets" +sidebar_position: 5 +--- + +# Changesets Reference + +Changesets are the top-level entry points for the CCIP Deployment Tooling API. Each changeset accepts a configuration input, resolves the appropriate chain-family adapters, executes sequences, and returns a `ChangesetOutput` that may include DataStore updates and MCMS proposals. + +For the types used by these changesets, see [Types Reference](types.md). For the adapter interfaces they delegate to, see [Interfaces Reference](interfaces.md). + +--- + +## Table of Contents + +- [Contract Deployment](#contract-deployment) +- [MCMS Deployment](#mcms-deployment) +- [Lane Configuration](#lane-configuration) +- [OCR3 Configuration](#ocr3-configuration) +- [Token Configuration](#token-configuration) +- [Fee Configuration](#fee-configuration) +- [Ownership Management](#ownership-management) +- [RMN Curse Operations](#rmn-curse-operations) +- [Lane Migration](#lane-migration) + +--- + +## Contract Deployment + +### DeployContracts + +Deploys all CCIP infrastructure contracts (Router, OnRamp, OffRamp, FeeQuoter, RMNRemote, etc.) on one or more chains. + +**Source:** [deploy/contracts.go](../deploy/contracts.go) + +**Constructor:** +```go +func DeployContracts(deployerReg *DeployerRegistry) cldf.ChangeSetV2[ContractDeploymentConfig] +``` + +**Input:** [`ContractDeploymentConfig`](types.md#contractdeploymentconfig) + +**Behavior:** +1. For each chain in `Chains`, resolves the chain family from the selector +2. Looks up the `Deployer` adapter from the `DeployerRegistry` using family + version +3. Executes `deployer.DeployChainContracts()` sequence +4. Collects deployed addresses into a DataStore +5. Returns output via `OutputBuilder` + +**Example:** +```go +changeset := deploy.DeployContracts(deploy.GetRegistry()) +output, err := changeset.Apply(env, deploy.ContractDeploymentConfig{ + Chains: map[uint64]deploy.ContractDeploymentConfigPerChain{ + chainSelector: { + Version: semver.MustParse("1.6.0"), + MaxFeeJuelsPerMsg: big.NewInt(1e18), + TokenPriceStalenessThreshold: 86400, + LinkPremiumMultiplier: 9e17, + NativeTokenPremiumMultiplier: 18e17, + PermissionLessExecutionThresholdSeconds: 86400, + }, + }, +}) +``` + +--- + +## MCMS Deployment + +### DeployMCMS + +Deploys Multi-Chain Multi-Sig governance contracts (AccessController, MCM, Timelock) on one or more chains. + +**Source:** [deploy/mcms.go](../deploy/mcms.go) + +**Constructor:** +```go +func DeployMCMS(deployerReg *DeployerRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[MCMSDeploymentConfig] +``` + +**Input:** [`MCMSDeploymentConfig`](types.md#mcmsdeploymentconfig) + +**Behavior:** +1. For each chain, resolves family and looks up `Deployer` adapter +2. Executes `deployer.DeployMCMS()` sequence +3. Collects deployed MCMS contract addresses into a DataStore + +### FinalizeDeployMCMS + +Finalizes MCMS deployment. Required for chains that need a two-phase deployment (e.g., Solana timelock initialization). + +**Constructor:** +```go +func FinalizeDeployMCMS(deployerReg *DeployerRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[MCMSDeploymentConfig] +``` + +Same input as `DeployMCMS`. Executes `deployer.FinalizeDeployMCMS()` instead. + +### GrantAdminRoleToTimelock + +Grants admin role from one timelock to another. Used when setting up multi-tiered MCMS governance. + +**Constructor:** +```go +func GrantAdminRoleToTimelock(deployerReg *DeployerRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[GrantAdminRoleToTimelockConfig] +``` + +**Input:** [`GrantAdminRoleToTimelockConfig`](types.md#grantadminroletotimelockconfig) + +**Validation:** +- Both timelock refs must have type `RBACTimelock` +- Both must have non-empty qualifier and version + +--- + +## Lane Configuration + +### ConnectChains + +Configures bidirectional lanes between chains. For each lane, configures both the source-side and destination-side on each chain. + +**Source:** [lanes/connect_chains.go](../lanes/connect_chains.go) + +**Constructor:** +```go +func ConnectChains(laneRegistry *LaneAdapterRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[ConnectChainsConfig] +``` + +**Input:** [`ConnectChainsConfig`](types.md#connectchainsconfig) + +**Behavior:** +For each `LaneConfig` in the input: +1. Resolves `LaneAdapter` for both ChainA and ChainB families +2. Populates contract addresses (OnRamp, OffRamp, Router, FeeQuoter) from the DataStore via the adapter's `Get*Address` methods +3. Executes `ConfigureLaneLegAsSource()` and `ConfigureLaneLegAsDest()` for both directions: + - A as source, B as dest + - B as source, A as dest + +**Example:** +```go +changeset := lanes.ConnectChains(lanes.GetLaneAdapterRegistry(), changesets.GetRegistry()) +output, err := changeset.Apply(env, lanes.ConnectChainsConfig{ + Lanes: []lanes.LaneConfig{ + { + ChainA: lanes.ChainDefinition{Selector: evmSel, GasPrice: big.NewInt(2e12)}, + ChainB: lanes.ChainDefinition{Selector: solSel, GasPrice: big.NewInt(2e12)}, + Version: semver.MustParse("1.6.0"), + }, + }, + MCMS: mcms.Input{TimelockAction: mcms_types.TimelockActionSchedule}, +}) +``` + +--- + +## OCR3 Configuration + +### SetOCR3Config + +Sets OCR3 configuration on remote chain OffRamps. Reads the OCR3 config from CCIPHome on the home chain and applies it to the specified remote chains. + +**Source:** [deploy/set_ocr3_config.go](../deploy/set_ocr3_config.go) + +**Constructor:** +```go +func SetOCR3Config(deployerReg *DeployerRegistry, mcmsReg *MCMSReaderRegistry) cldf.ChangeSetV2[SetOCR3ConfigArgs] +``` + +**Input:** [`SetOCR3ConfigArgs`](types.md#setocr3configargs) + +**Behavior:** +1. For each remote chain selector, resolves the `Deployer` adapter (always version 1.6.0) +2. Reads OCR3 config from CCIPHome on the home chain +3. Builds `OCR3ConfigArgs` with signers, transmitters, and config digests +4. Executes `deployer.SetOCR3Config()` on each remote chain + +--- + +## Token Configuration + +### TokenExpansion + +Comprehensive changeset that deploys tokens, token pools, configures them for cross-chain transfers, and transfers ownership to the timelock. + +**Source:** [tokens/token_expansion.go](../tokens/token_expansion.go) + +**Constructor:** +```go +func TokenExpansion() cldf.ChangeSetV2[TokenExpansionInput] +``` + +**Input:** [`TokenExpansionInput`](types.md#tokenexpansioninput) + +**Behavior:** +1. **Deploy tokens** (if `DeployTokenInput` is non-nil): Executes `adapter.DeployToken()` +2. **Deploy token pools** (if `DeployTokenPoolInput` is non-nil): Executes `adapter.DeployTokenPoolForToken()` +3. **Configure for transfers** (if `TokenTransferConfig` is non-nil): Calls `processTokenConfigForChain()` which executes `adapter.ConfigureTokenForTransfersSequence()` +4. **Update authorities** (unless `SkipOwnershipTransfer` is true): Executes `adapter.UpdateAuthorities()` to transfer ownership to the timelock + +### ConfigureTokensForTransfers + +Configures already-deployed tokens and token pools for cross-chain transfers. Does not deploy new contracts. + +**Source:** [tokens/configure_tokens_for_transfers.go](../tokens/configure_tokens_for_transfers.go) + +**Constructor:** +```go +func ConfigureTokensForTransfers(tokenRegistry *TokenAdapterRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[ConfigureTokensForTransfersConfig] +``` + +**Input:** [`ConfigureTokensForTransfersConfig`](types.md#configuretokensfortransfersconfig) + +**Behavior:** +For each token configuration: +1. Resolves the `TokenAdapter` for the chain family +2. Resolves remote chain addresses and decimals +3. Derives inbound rate limiter config from counterpart's outbound config +4. Executes `adapter.ConfigureTokenForTransfersSequence()` + +### ManualRegistration + +Registers customer tokens with the token admin registry. Used when the customer has already deployed the token and no longer has mint authority. + +**Source:** [tokens/manual_registration.go](../tokens/manual_registration.go) + +**Constructor:** +```go +func ManualRegistration() cldf.ChangeSetV2[ManualRegistrationInput] +``` + +**Input:** [`ManualRegistrationInput`](types.md#manualregistrationinput) + +### SetTokenPoolRateLimits + +Sets rate limits on token pools for specific remote chains. + +**Source:** [tokens/rate_limits.go](../tokens/rate_limits.go) + +**Constructor:** +```go +func SetTokenPoolRateLimits() cldf.ChangeSetV2[TPRLInput] +``` + +**Input:** [`TPRLInput`](types.md#tprlinput) + +**Behavior:** +- Resolves token decimals on both local and remote chains +- Scales float-based user inputs by token decimals +- Derives inbound rate limiter from counterpart's outbound config +- Inbound capacity is scaled by 1.1x to avoid accidental rate limit hits + +**Validation:** +- If rate limiter is enabled, capacity and rate must be positive + +--- + +## Fee Configuration + +### SetTokenTransferFee + +Sets per-token transfer fee configurations on the FeeQuoter for each source-destination pair. + +**Source:** [fees/set_token_transfer_fee.go](../fees/set_token_transfer_fee.go) + +**Constructor:** +```go +func SetTokenTransferFee(feeRegistry *FeeAdapterRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[SetTokenTransferFeeInput] +``` + +**Input:** +```go +type SetTokenTransferFeeInput struct { + Version *semver.Version + Args []TokenTransferFeeForSrc + MCMS mcms.Input +} +``` + +**Fee Resolution:** +For each token, the changeset uses a three-tier fallback: +1. **User-specified values** (if `Valid` is true in `UnresolvedTokenTransferFeeArgs`) +2. **On-chain values** (if the token already has fee config set) +3. **Adapter defaults** (from `adapter.GetDefaultTokenTransferFeeConfig()`) + +**Validation:** +- No duplicate source chain selectors +- No duplicate destination selectors within the same source +- Source and destination selectors cannot be the same +- Token addresses cannot be empty +- Same address cannot appear in both updates and resets + +--- + +## Ownership Management + +### TransferOwnershipChangeset + +Proposes ownership transfer of contracts through MCMS governance. On chains where `ShouldAcceptOwnershipWithTransferOwnership()` returns true (e.g., Solana), also accepts ownership atomically. + +**Source:** [deploy/transfer_ownership.go](../deploy/transfer_ownership.go) + +**Constructor:** +```go +func TransferOwnershipChangeset(cr *TransferOwnershipAdapterRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[TransferOwnershipInput] +``` + +**Input:** [`TransferOwnershipInput`](types.md#transferownershipinput) + +**Behavior:** +1. For each chain, resolves the `TransferOwnershipAdapter` +2. Initializes timelock address via `adapter.InitializeTimelockAddress()` +3. Resolves partial contract refs to full refs +4. Executes `adapter.SequenceTransferOwnershipViaMCMS()` +5. If `adapter.ShouldAcceptOwnershipWithTransferOwnership()` returns true, also executes `adapter.SequenceAcceptOwnership()` + +### AcceptOwnershipChangeset + +Accepts previously proposed ownership transfers. + +**Constructor:** +```go +func AcceptOwnershipChangeset(cr *TransferOwnershipAdapterRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[TransferOwnershipInput] +``` + +Same input as `TransferOwnershipChangeset`. Only executes `adapter.SequenceAcceptOwnership()`. + +--- + +## RMN Curse Operations + +**Source:** [fastcurse/fastcurse.go](../fastcurse/fastcurse.go) + +### CurseChangeset + +Curses specified subjects on specified chains via the RMNRemote contract. + +**Constructor:** +```go +func CurseChangeset(cr *CurseRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[RMNCurseConfig] +``` + +**Input:** +```go +type RMNCurseConfig struct { + CurseActions []CurseActionInput + Force bool // Include already-cursed subjects + MCMS mcms.Input +} + +type CurseActionInput struct { + IsGlobalCurse bool + ChainSelector uint64 + SubjectChainSelector uint64 + Version *semver.Version +} +``` + +**Behavior:** +- Groups curse actions by chain selector +- Filters out already-cursed subjects (unless `Force` is true) +- Executes `adapter.Curse()` sequence for each chain + +### UncurseChangeset + +Lifts curses on specified subjects. + +**Constructor:** +```go +func UncurseChangeset(cr *CurseRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[RMNCurseConfig] +``` + +Same input as `CurseChangeset`. Executes `adapter.Uncurse()` instead. + +### GloballyCurseChainChangeset + +Globally curses chains across the entire network. Automatically discovers connected chains and creates curse actions for all of them. + +**Constructor:** +```go +func GloballyCurseChainChangeset(cr *CurseRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[GlobalCurseOnNetworkInput] +``` + +**Input:** +```go +type GlobalCurseOnNetworkInput struct { + ChainSelectors map[uint64]*semver.Version // Chains to curse -> RMN version + Force bool + MCMS mcms.Input +} +``` + +### GloballyUncurseChainChangeset + +Reverses a global curse across the network. + +**Constructor:** +```go +func GloballyUncurseChainChangeset(cr *CurseRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[GlobalCurseOnNetworkInput] +``` + +--- + +## Lane Migration + +### LaneMigrateToNewVersionChangeset + +Migrates lanes to a new contract version by updating router configurations and ramp contracts. + +**Source:** [deploy/lanemigrator.go](../deploy/lanemigrator.go) + +**Constructor:** +```go +func LaneMigrateToNewVersionChangeset(migratorReg *LaneMigratorRegistry, mcmsRegistry *MCMSReaderRegistry) cldf.ChangeSetV2[LaneMigratorConfig] +``` + +**Input:** +```go +type LaneMigratorConfig struct { + Input map[uint64]LaneMigratorConfigPerChain + MCMS mcms.Input +} + +type LaneMigratorConfigPerChain struct { + RemoteChains []uint64 // Remote chains to migrate + RouterVersion *semver.Version // Router version to use + RampVersion *semver.Version // Ramp version to upgrade to (must be >= 1.6.0) +} +``` + +**Behavior:** +1. Looks up OnRamp, OffRamp, and Router refs from the DataStore +2. Executes `routerUpdater.UpdateRouter()` to point the router to new ramps +3. Executes `rampUpdater.UpdateVersionWithRouter()` to configure ramps with the new router + +**Validation:** +- Ramp version must be >= 1.6.0 +- Both router updater and ramp updater must be registered +- All chain selectors must exist in the environment +- Existing addresses must be present in the DataStore diff --git a/deployment/docs/implementing-adapters.md b/deployment/docs/implementing-adapters.md new file mode 100644 index 0000000000..72e60e731a --- /dev/null +++ b/deployment/docs/implementing-adapters.md @@ -0,0 +1,313 @@ +--- +title: "Implementing a New Chain Family Adapter" +sidebar_label: "Implementing Adapters" +sidebar_position: 6 +--- + +# Implementing a New Chain Family Adapter + +This guide provides a step-by-step walkthrough for adding support for a new chain family to the CCIP Deployment Tooling API. + +For the complete interface definitions, see [Interfaces Reference](interfaces.md). For type definitions, see [Types Reference](types.md). + +## Prerequisites + +- Familiarity with the [Architecture](architecture.md) (adapter-registry pattern, operations-sequences-changesets hierarchy) +- The `chain-selectors` library must already define a family constant for your chain (e.g., `chain_selectors.FamilyAptos`) +- Smart contracts for your chain family are deployed or in development + +## Step 1: Choose Your Module Location + +By convention, chain-family adapters live alongside their contracts: + +``` +chainlink-ccip/chains//deployment/ +``` + +Or in an external repository: + +``` +chainlink-/deployment/ +``` + +Create a Go module with its own `go.mod` that imports `chainlink-ccip/deployment` for the shared types and interfaces. + +## Step 2: Set Up the Directory Structure + +Follow this canonical layout: + +``` +chains//deployment/ +├── go.mod +├── go.sum +├── utils/ # Shared utilities for this chain family +│ ├── common.go # Chain-specific contract type constants +│ ├── deploy.go # Deployment helpers +│ ├── mcms.go # MCMS-related utilities +│ └── datastore.go # DataStore helpers +├── v1_6_0/ # Version-specific implementation +│ ├── adapters/ # Adapters registered separately (curse, fees) +│ │ └── init.go # init() registrations for adapters +│ ├── operations/ # Low-level contract operations +│ │ ├── router/ +│ │ ├── offramp/ +│ │ ├── fee_quoter/ +│ │ ├── token_pools/ +│ │ ├── tokens/ +│ │ ├── rmn_remote/ +│ │ └── mcms/ +│ ├── sequences/ # High-level operation orchestration +│ │ ├── adapter.go # Main adapter struct + init() registration +│ │ ├── deploy_chain_contracts.go +│ │ ├── connect_chains.go +│ │ ├── ocr.go +│ │ ├── mcms.go +│ │ ├── tokens.go +│ │ ├── fee_quoter.go +│ │ └── transfer_ownership.go +│ └── testadapter/ # Test adapter implementation +└── docs/ # Chain-specific documentation +``` + +## Step 3: Create the Adapter Struct + +Define a single struct that will implement all required interfaces: + +```go +// sequences/adapter.go +package sequences + +type MyChainAdapter struct { + // Add any stateful fields your chain needs. + // For example, Solana caches timelock addresses: + // timelockAddr map[uint64]solana.PublicKey + // + // EVM uses a stateless empty struct: + // (no fields) +} +``` + +## Step 4: Implement Required Interfaces + +### Interface Checklist + +Each method returns a `*operations.Sequence` that the framework executes. Your job is to implement the sequence logic using chain-specific operations. + +#### 4.1 Deployer + +Deploy all CCIP infrastructure contracts on your chain: + +```go +func (a *MyChainAdapter) DeployChainContracts() *Sequence[ContractDeploymentConfigPerChainWithAddress, OnChainOutput, BlockChains] { + return DeployChainContractsSequence // your sequence variable +} + +func (a *MyChainAdapter) DeployMCMS() *Sequence[MCMSDeploymentConfigPerChainWithAddress, OnChainOutput, BlockChains] { + return DeployMCMSSequence +} + +func (a *MyChainAdapter) FinalizeDeployMCMS() *Sequence[MCMSDeploymentConfigPerChainWithAddress, OnChainOutput, BlockChains] { + return FinalizeDeployMCMSSequence // can be no-op if not needed +} + +func (a *MyChainAdapter) SetOCR3Config() *Sequence[SetOCR3ConfigInput, OnChainOutput, BlockChains] { + return SetOCR3ConfigSequence +} + +func (a *MyChainAdapter) GrantAdminRoleToTimelock() *Sequence[GrantAdminRoleToTimelockConfigPerChainWithSelector, OnChainOutput, BlockChains] { + return GrantAdminRoleToTimelockSequence +} +``` + +#### 4.2 LaneAdapter + +Address retrieval methods must encode addresses in your chain's native format: + +```go +func (a *MyChainAdapter) GetOnRampAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) { + // Look up the OnRamp address from the DataStore and return as bytes +} + +func (a *MyChainAdapter) GetOffRampAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) { ... } +func (a *MyChainAdapter) GetRouterAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) { ... } +func (a *MyChainAdapter) GetFQAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) { ... } + +func (a *MyChainAdapter) ConfigureLaneLegAsSource() *Sequence[UpdateLanesInput, OnChainOutput, BlockChains] { + return ConfigureLaneLegAsSourceSequence +} + +func (a *MyChainAdapter) ConfigureLaneLegAsDest() *Sequence[UpdateLanesInput, OnChainOutput, BlockChains] { + return ConfigureLaneLegAsDestSequence +} +``` + +#### 4.3 TokenAdapter + +The token adapter requires several helper methods for address derivation: + +```go +func (a *MyChainAdapter) AddressRefToBytes(ref datastore.AddressRef) ([]byte, error) { + // Convert the AddressRef.Address string to bytes using your chain's encoding + // EVM: hex decoding, Solana: base58 decoding +} + +func (a *MyChainAdapter) DeriveTokenAddress(e Environment, chainSelector uint64, poolRef datastore.AddressRef) ([]byte, error) { + // Read the token address from the pool contract on-chain +} + +func (a *MyChainAdapter) DeriveTokenDecimals(e Environment, chainSelector uint64, poolRef datastore.AddressRef, token []byte) (uint8, error) { + // Read the token decimals on-chain +} + +func (a *MyChainAdapter) DeriveTokenPoolCounterpart(e Environment, chainSelector uint64, tokenPool []byte, token []byte) ([]byte, error) { + // For chains where the operational pool address differs from the deployed address + // (e.g., Solana PDAs). Return tokenPool unchanged if not applicable. +} +``` + +#### 4.4 FeeAdapter, MCMSReader, TransferOwnershipAdapter, CurseAdapter, CurseSubjectAdapter + +Follow the same pattern -- see [Interfaces Reference](interfaces.md) for the full method signatures. + +## Step 5: Create Operations + +Operations are the atomic building blocks. Define them in the `operations/` directory, organized by contract. + +Each operation follows this pattern: + +```go +var Deploy = operations.NewOperation( + "my-contract:deploy", // Operation ID: contract:method + semver.MustParse("1.6.0"), // Contract version + "Deploys the MyContract program", // Description + func(b operations.Bundle, chain MyChain, input DeployInput) (DeployOutput, error) { + // Chain-specific deployment logic + }, +) +``` + +## Step 6: Compose Sequences + +Sequences orchestrate multiple operations into complete workflows. Define them in the `sequences/` directory. + +```go +var DeployChainContractsSequence = operations.NewSequence( + "deploy-chain-contracts", + semver.MustParse("1.6.0"), + "Deploys all CCIP contracts on a chain", + func(b operations.Bundle, chains BlockChains, input ContractDeploymentConfigPerChainWithAddress) (OnChainOutput, error) { + var agg sequences.OnChainOutput + + // Deploy Router + agg, err := sequences.RunAndMergeSequence(b, chains, deployRouterSeq, routerInput, agg) + if err != nil { + return agg, err + } + + // Deploy FeeQuoter + agg, err = sequences.RunAndMergeSequence(b, chains, deployFeeQuoterSeq, fqInput, agg) + if err != nil { + return agg, err + } + + // ... more contract deployments + + return agg, nil + }, +) +``` + +Key patterns: +- Use `sequences.RunAndMergeSequence` to compose sub-sequences and aggregate their outputs +- Each sequence should target a single chain for simplicity +- Return `OnChainOutput` with deployed addresses, metadata, and MCMS batch operations + +## Step 7: Register via init() + +Create `init()` functions that run automatically when your package is imported. + +**Main adapter registration** (`sequences/adapter.go`): + +```go +func init() { + v, err := semver.NewVersion("1.6.0") + if err != nil { + panic(err) + } + + // Required registrations + laneapi.GetLaneAdapterRegistry().RegisterLaneAdapter(chain_selectors.FamilyMyChain, v, &MyChainAdapter{}) + deployapi.GetRegistry().RegisterDeployer(chain_selectors.FamilyMyChain, v, &MyChainAdapter{}) + deployapi.GetTransferOwnershipRegistry().RegisterAdapter(chain_selectors.FamilyMyChain, v, &MyChainAdapter{}) + mcmsreaderapi.GetRegistry().RegisterMCMSReader(chain_selectors.FamilyMyChain, &MyChainAdapter{}) + tokensapi.GetTokenAdapterRegistry().RegisterTokenAdapter(chain_selectors.FamilyMyChain, v, &MyChainAdapter{}) +} +``` + +**Separate adapter registration** (`adapters/init.go`): + +```go +func init() { + // Curse/RMN adapter + fastcurse.GetCurseRegistry().RegisterNewCurse(fastcurse.CurseRegistryInput{ + CursingFamily: chain_selectors.FamilyMyChain, + CursingVersion: semver.MustParse("1.6.0"), + CurseAdapter: NewCurseAdapter(), + CurseSubjectAdapter: NewCurseAdapter(), + }) + + // Fee adapter + fees.GetRegistry().RegisterFeeAdapter(chain_selectors.FamilyMyChain, semver.MustParse("1.6.0"), &FeesAdapter{}) +} +``` + +## Step 8: Wire Into chainlink-deployments + +In `chainlink-deployments/domains///pipelines.go`, import your adapter package to trigger the `init()` functions: + +```go +import ( + _ "github.com/smartcontractkit/chainlink-ccip/chains/mychain/deployment/v1_6_0/sequences" + _ "github.com/smartcontractkit/chainlink-ccip/chains/mychain/deployment/v1_6_0/adapters" +) +``` + +The blank import is sufficient -- it triggers `init()` and registers all adapters into the global registries. + +## Reference Implementations + +Study these existing implementations: + +| Chain | Adapter Source | Key Patterns | +|-------|---------------|--------------| +| **EVM** | [chains/evm/deployment/v1_6_0/sequences/adapter.go](../../chains/evm/deployment/v1_6_0/sequences/adapter.go) | Stateless adapter, auto-generated operations from gethwrappers, multi-version support (v1_0_0 through v1_6_5) | +| **Solana** | [chains/solana/deployment/v1_6_0/sequences/adapter.go](../../chains/solana/deployment/v1_6_0/sequences/adapter.go) | Stateful adapter (caches timelock addresses), two-phase MCMS deployment, PDA-based address derivation | + +## Common Considerations + +### Address Encoding + +Each chain family must handle address serialization consistently: +- `AddressRefToBytes`: Convert string addresses from the DataStore to bytes +- `Get*Address` methods on `LaneAdapter`: Return addresses as bytes in your chain's native format +- Cross-chain references: When configuring a lane, the source chain receives the destination's addresses as `[]byte` + +### Two-Phase MCMS + +Some chains (like Solana) require a two-phase MCMS deployment: +1. `DeployMCMS()` -- deploy the contracts +2. `FinalizeDeployMCMS()` -- initialize the timelock and configure roles + +If your chain can do everything in one step, `FinalizeDeployMCMS()` can return a no-op sequence. + +### MCMS Batch Operations + +When a write operation cannot be executed directly by the deployer key (because the contract is owned by a timelock), the operation should produce `mcms_types.BatchOperation` entries instead. These get collected by the `OutputBuilder` and assembled into an MCMS proposal. + +### DataStore Integration + +Sequences should return all deployed contract addresses in `OnChainOutput.Addresses` so the changeset can persist them to the DataStore. Use `datastore.AddressRef` with your chain's contract types, versions, and qualifiers. + +### Contract Type Constants + +Define your chain-specific contract types in `utils/common.go`. Use the shared constants from `deployment/utils/common.go` where applicable (e.g., `RBACTimelock`, `BurnMintTokenPool`). diff --git a/deployment/docs/index.md b/deployment/docs/index.md new file mode 100644 index 0000000000..7abda1add2 --- /dev/null +++ b/deployment/docs/index.md @@ -0,0 +1,129 @@ +--- +title: "CCIP Deployment Tooling API" +sidebar_label: "Overview" +sidebar_position: 1 +--- + +# CCIP Deployment Tooling API + +The CCIP Deployment Tooling API provides a unified, strongly-typed Go-based operational layer for deploying, configuring, and managing CCIP across all chain families (EVM, Solana, Aptos, TON, Sui, etc.). + +This shared library (`chainlink-ccip/deployment`) contains all generic, chain-agnostic types, registries, and utilities. Each chain family implements its own adapter alongside its contracts, either in `chainlink-ccip/chains//deployment` or in a dedicated repository. + +## Architecture Overview + +```mermaid +flowchart LR + subgraph chainlink-deployments + dp[Durable Pipelines] + ds[DataStore] + end + + subgraph Chain Family Repo + cfmr[MCMSReader] + subgraph Sequences + subgraph v1.6.0 + clls1_6[ConfigureLaneLegAsSource] + clld1_6[ConfigureLaneLegAsDest] + cllb1_6[ConfigureLaneLegBidirectionally] + ctft1_6[ConfigureTokenForTransfers] + end + subgraph v1.7.0 + clls1_7[ConfigureChainForLanes] + ctft1_7[ConfigureTokenForTransfers] + end + subgraph v1.5.0 + ctft1_5[ConfigureTokenForTransfers] + end + end + subgraph Helpers + artb[AddressRefToBytes] + dtfp[DeriveTokenFromPool] + end + end + + subgraph chainlink-ccip/deployment + subgraph changesets + ctft[ConfigureTokensForTransfers] + cc1_6[v1_6.ConnectChains] + cc1_7[v1_7.ConnectChains] + end + subgraph interfaces + mr[MCMSReader] + ta[TokensAdapter] + ca1_6[v1_6.ChainAdapter] + ca1_7[v1_7.ChainAdapter] + end + subgraph registries + tar[TokenAdapterRegistry] + car1_6[v1_6.ChainAdapterRegistry] + car1_7[v1_7.ChainAdapterRegistry] + mrr[MCMSReaderRegistry] + end + end + + ctft --> tar + ctft --> mrr + + tar --> ta + mrr --> mr + + ta --> artb + ta --> dtfp + ta --> ctft1_5 + ta --> ctft1_6 + ta --> ctft1_7 + mr ------> cfmr + + dp ---init---> registries + dp ---run---> changesets + + cc1_6 --> mrr & car1_6 + car1_6 --> ca1_6 + ca1_6 ------> cllb1_6 + ca1_6 ------> clls1_6 + ca1_6 ------> clld1_6 +``` + +## Three-Level Hierarchy + +The API is structured in three levels of granularity: + +| Level | Description | Use When | +|-------|-------------|----------| +| **Changesets** | Environment-aware entry points that read from DataStore, invoke sequences, and produce MCMS proposals | Executing operations via Durable Pipelines or full deployment environments | +| **Sequences** | Ordered collections of operations. Accept serializable input and minimal dependencies | Completing an operational workflow without a full deployment environment | +| **Operations** | Single side-effect actions (deploy, read, write). Produce reports for stateful retries | Making a single contract call or deployment | + +## Package Layout + +| Package | Purpose | +|---------|---------| +| `deploy/` | Contract deployment, MCMS deployment, OCR3 config, ownership transfer changesets and interfaces | +| `lanes/` | Lane configuration and inter-chain connection changesets and interfaces | +| `tokens/` | Token pool configuration, expansion, manual registration, rate limits | +| `fees/` | Fee configuration and token transfer fee management | +| `fastcurse/` | RMN curse/uncurse operations | +| `utils/changesets/` | `MCMSReader` interface, `OutputBuilder`, changeset utilities | +| `utils/sequences/` | `OnChainOutput` type, sequence execution utilities | +| `utils/mcms/` | MCMS input types | +| `utils/` | Common types, version constants, contract type constants | +| `testadapters/` | Test adapter framework for cross-chain message testing | + +## Documentation + +| Document | Description | +|----------|-------------| +| [Architecture](architecture.md) | Design principles, adapter-registry pattern, dispatch flow, DataStore and MCMS integration | +| [Interfaces](interfaces.md) | Complete API reference for all adapter interfaces and their registries | +| [Types](types.md) | All input/output types, config structs, and constants | +| [Changesets](changesets.md) | Reference for all changesets (entry points) with config types and usage | +| [Implementing Adapters](implementing-adapters.md) | Step-by-step guide for adding a new chain family | +| [MCMS and Utilities](mcms-and-utilities.md) | MCMS integration, `OutputBuilder`, DataStore and sequence utilities | + +## Chain-Specific Documentation + +| Chain Family | Documentation | +|-------------|---------------| +| EVM | [EVM Deployment Docs](../../chains/evm/deployment/docs/index.md) | +| Solana | [Solana Deployment Docs](../../chains/solana/deployment/docs/index.md) | diff --git a/deployment/docs/interfaces.md b/deployment/docs/interfaces.md new file mode 100644 index 0000000000..2ded7e6c13 --- /dev/null +++ b/deployment/docs/interfaces.md @@ -0,0 +1,524 @@ +--- +title: "Adapter Interfaces Reference" +sidebar_label: "Interfaces" +sidebar_position: 3 +--- + +# Adapter Interfaces Reference + +This document provides a complete API reference for all adapter interfaces that chain-specific implementations must or can provide. Each interface is accompanied by its registry, registration pattern, and method signatures. + +For a step-by-step guide on implementing these interfaces for a new chain family, see [Implementing Adapters](implementing-adapters.md). + +## Quick Reference + +### Required Interfaces + +Every chain family **must** implement these interfaces: + +| Interface | Registry | Key Format | Source | +|-----------|----------|------------|--------| +| [Deployer](#deployer) | `DeployerRegistry` | `chainFamily-version` | [deploy/product.go](../deploy/product.go) | +| [LaneAdapter](#laneadapter) | `LaneAdapterRegistry` | `chainFamily-version` | [lanes/product.go](../lanes/product.go) | +| [TokenAdapter](#tokenadapter) | `TokenAdapterRegistry` | `chainFamily-version` | [tokens/product.go](../tokens/product.go) | +| [FeeAdapter](#feeadapter) | `FeeAdapterRegistry` | `chainFamily-version` | [fees/product.go](../fees/product.go) | +| [MCMSReader](#mcmsreader) | `MCMSReaderRegistry` | `chainFamily` | [utils/changesets/output.go](../utils/changesets/output.go) | +| [TransferOwnershipAdapter](#transferownershipadapter) | `TransferOwnershipAdapterRegistry` | `chainFamily-version` | [deploy/product.go](../deploy/product.go) | +| [CurseAdapter](#curseadapter) | `CurseRegistry` | `chainFamily-version` | [fastcurse/product.go](../fastcurse/product.go) | +| [CurseSubjectAdapter](#cursesubjectadapter) | `CurseRegistry` | `chainFamily` | [fastcurse/product.go](../fastcurse/product.go) | + +### Optional Interfaces + +These interfaces are optional depending on what the chain family supports: + +| Interface | Registry | Purpose | Source | +|-----------|----------|---------|--------| +| [TokenPriceProvider](#tokenpriceprovider) | None (embedded) | Provide default fee token prices | [lanes/product.go](../lanes/product.go) | +| [PingPongAdapter](#pingpongadapter) | `PingPongAdapterRegistry` | PingPong demo contract support | [lanes/pingpong.go](../lanes/pingpong.go) | +| [ConfigImporter](#configimporter) | None | Import config from existing deployments | [deploy/product.go](../deploy/product.go) | +| [RampUpdateInRouter](#rampupdateinrouter) | `LaneMigratorRegistry` | Lane migration: update router | [deploy/lanemigrator.go](../deploy/lanemigrator.go) | +| [RouterUpdateInRamp](#routerupdateinramp) | `LaneMigratorRegistry` | Lane migration: update ramps | [deploy/lanemigrator.go](../deploy/lanemigrator.go) | +| [TestAdapter](#testadapter) | `TestAdapterRegistry` | Cross-chain message testing | [testadapters/adapters.go](../testadapters/adapters.go) | + +--- + +## Required Interfaces + +### Deployer + +Handles deployment of CCIP core contracts and MCMS governance contracts on a chain. + +**Source:** [deploy/product.go](../deploy/product.go) +**Registry:** `DeployerRegistry` via `deploy.GetRegistry()` +**Key:** `chainFamily-version` + +```go +type Deployer interface { + // DeployChainContracts deploys all CCIP contracts required for a chain + // (Router, OnRamp, OffRamp, FeeQuoter, RMNRemote, etc.) + DeployChainContracts() *Sequence[ContractDeploymentConfigPerChainWithAddress, OnChainOutput, BlockChains] + + // DeployMCMS deploys Multi-Chain Multi-Sig governance contracts + // (AccessController, MCM, Timelock) + DeployMCMS() *Sequence[MCMSDeploymentConfigPerChainWithAddress, OnChainOutput, BlockChains] + + // FinalizeDeployMCMS finalizes MCMS deployment (e.g., timelock initialization on Solana). + // Not all chains require this - can be a no-op sequence. + FinalizeDeployMCMS() *Sequence[MCMSDeploymentConfigPerChainWithAddress, OnChainOutput, BlockChains] + + // SetOCR3Config sets OCR3 configuration on the chain's OffRamp + SetOCR3Config() *Sequence[SetOCR3ConfigInput, OnChainOutput, BlockChains] + + // GrantAdminRoleToTimelock grants admin role from one timelock to another + GrantAdminRoleToTimelock() *Sequence[GrantAdminRoleToTimelockConfigPerChainWithSelector, OnChainOutput, BlockChains] +} +``` + +**Registration:** +```go +deploy.GetRegistry().RegisterDeployer(chain_selectors.FamilyEVM, semver.MustParse("1.6.0"), &EVMAdapter{}) +``` + +--- + +### LaneAdapter + +Handles lane configuration between chains -- configuring a chain as a message source or destination for a given lane. + +**Source:** [lanes/product.go](../lanes/product.go) +**Registry:** `LaneAdapterRegistry` via `lanes.GetLaneAdapterRegistry()` +**Key:** `chainFamily-version` + +```go +type LaneAdapter interface { + // ConfigureLaneLegAsSource configures this chain as the source for a lane. + // Sets up OnRamp destination chain config, FeeQuoter destination config, and token prices. + ConfigureLaneLegAsSource() *Sequence[UpdateLanesInput, OnChainOutput, BlockChains] + + // ConfigureLaneLegAsDest configures this chain as the destination for a lane. + // Sets up OffRamp source chain config and Router integration. + ConfigureLaneLegAsDest() *Sequence[UpdateLanesInput, OnChainOutput, BlockChains] + + // GetOnRampAddress returns the OnRamp contract address (as bytes) for the given chain. + // On Solana, this returns the Router address since Solana has a unified contract. + GetOnRampAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) + + // GetOffRampAddress returns the OffRamp contract address (as bytes) for the given chain. + GetOffRampAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) + + // GetRouterAddress returns the Router contract address (as bytes) for the given chain. + GetRouterAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) + + // GetFQAddress returns the FeeQuoter contract address (as bytes) for the given chain. + GetFQAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) +} +``` + +**Registration:** +```go +lanes.GetLaneAdapterRegistry().RegisterLaneAdapter(chain_selectors.FamilySolana, semver.MustParse("1.6.0"), &SolanaAdapter{}) +``` + +**Note:** The `Get*Address` methods are used by the `ConnectChains` changeset to programmatically populate `ChainDefinition` fields. Address bytes must be chain-family encoded (e.g., 20-byte EVM address, 32-byte Solana public key). + +--- + +### TokenAdapter + +Handles token pool configuration, deployment, and cross-chain token transfer setup. + +**Source:** [tokens/product.go](../tokens/product.go) +**Registry:** `TokenAdapterRegistry` via `tokens.GetTokenAdapterRegistry()` +**Key:** `chainFamily-version` + +Each chain-family-version combination registers separately, because configuration differs by token pool version (e.g., 1.7.0 pools require CCV config, 1.5.0 pools require remote pool addresses). + +```go +type TokenAdapter interface { + // ConfigureTokenForTransfersSequence configures a token pool for cross-chain transfers. + // Assumes the token and pool are already deployed and registered. + ConfigureTokenForTransfersSequence() *Sequence[ConfigureTokenForTransfersInput, OnChainOutput, BlockChains] + + // AddressRefToBytes converts an AddressRef to a byte slice. + // Each chain family serializes addresses differently (hex for EVM, base58 for Solana). + AddressRefToBytes(ref datastore.AddressRef) ([]byte, error) + + // DeriveTokenAddress derives the token address from a token pool reference. + // Used when the token address is stored on the pool contract. + DeriveTokenAddress(e Environment, chainSelector uint64, poolRef datastore.AddressRef) ([]byte, error) + + // DeriveTokenDecimals derives the token's decimal count from a pool reference. + DeriveTokenDecimals(e Environment, chainSelector uint64, poolRef datastore.AddressRef, token []byte) (uint8, error) + + // DeriveTokenPoolCounterpart derives the effective pool address for chains where + // the deployed address differs from the operational address (e.g., Solana PDAs). + DeriveTokenPoolCounterpart(e Environment, chainSelector uint64, tokenPool []byte, token []byte) ([]byte, error) + + // ManualRegistration registers a customer token with the token admin registry. + // Used when the customer has already deployed and no longer has mint authority. + ManualRegistration() *Sequence[ManualRegistrationSequenceInput, OnChainOutput, BlockChains] + + // SetTokenPoolRateLimits sets rate limits on a token pool. + SetTokenPoolRateLimits() *Sequence[TPRLRemotes, OnChainOutput, BlockChains] + + // DeployToken deploys a new token on the chain. + DeployToken() *Sequence[DeployTokenInput, OnChainOutput, BlockChains] + + // DeployTokenVerify validates the DeployToken input before execution. + DeployTokenVerify(e Environment, in DeployTokenInput) error + + // DeployTokenPoolForToken deploys a token pool for an existing token. + DeployTokenPoolForToken() *Sequence[DeployTokenPoolInput, OnChainOutput, BlockChains] + + // UpdateAuthorities transfers token and pool ownership to the timelock signer. + UpdateAuthorities() *Sequence[UpdateAuthoritiesInput, OnChainOutput, *Environment] +} +``` + +**Registration:** +```go +tokens.GetTokenAdapterRegistry().RegisterTokenAdapter(chain_selectors.FamilyEVM, semver.MustParse("1.6.0"), &EVMAdapter{}) +``` + +--- + +### FeeAdapter + +Handles token transfer fee configuration and retrieval. + +**Source:** [fees/product.go](../fees/product.go) +**Registry:** `FeeAdapterRegistry` via `fees.GetRegistry()` +**Key:** `chainFamily-version` + +```go +type FeeAdapter interface { + // SetTokenTransferFee returns a sequence that sets per-token transfer fees for each destination chain. + SetTokenTransferFee(e Environment) *Sequence[SetTokenTransferFeeSequenceInput, OnChainOutput, BlockChains] + + // GetOnchainTokenTransferFeeConfig reads the current on-chain fee configuration for a token on a lane. + GetOnchainTokenTransferFeeConfig(e Environment, src uint64, dst uint64, token string) (TokenTransferFeeArgs, error) + + // GetDefaultTokenTransferFeeConfig returns default fee configuration for a token on a lane. + GetDefaultTokenTransferFeeConfig(src uint64, dst uint64) TokenTransferFeeArgs +} +``` + +**Registration:** +```go +fees.GetRegistry().RegisterFeeAdapter(chain_selectors.FamilySolana, semver.MustParse("1.6.0"), &FeesAdapter{}) +``` + +--- + +### MCMSReader + +Resolves MCMS governance metadata for a chain -- timelock addresses, MCMS contract references, and chain metadata needed to build proposals. + +**Source:** [utils/changesets/output.go](../utils/changesets/output.go) +**Registry:** `MCMSReaderRegistry` via `changesets.GetRegistry()` +**Key:** `chainFamily` (version-agnostic -- one reader per chain family) + +```go +type MCMSReader interface { + // GetChainMetadata returns MCMS chain metadata (e.g., starting op count, MCM address). + GetChainMetadata(e Environment, chainSelector uint64, input mcms.Input) (mcms_types.ChainMetadata, error) + + // GetTimelockRef returns the timelock contract AddressRef for a given MCMS input. + GetTimelockRef(e Environment, chainSelector uint64, input mcms.Input) (datastore.AddressRef, error) + + // GetMCMSRef returns the MCMS contract AddressRef for a given MCMS input. + GetMCMSRef(e Environment, chainSelector uint64, input mcms.Input) (datastore.AddressRef, error) +} +``` + +**Registration:** +```go +changesets.GetRegistry().RegisterMCMSReader(chain_selectors.FamilySolana, &SolanaAdapter{}) +``` + +**Note:** Unlike other registries, this one is keyed by chain family only (no version), since MCMS metadata resolution is typically family-wide. + +--- + +### TransferOwnershipAdapter + +Handles transferring contract ownership via MCMS governance proposals. + +**Source:** [deploy/product.go](../deploy/product.go) +**Registry:** `TransferOwnershipAdapterRegistry` via `deploy.GetTransferOwnershipRegistry()` +**Key:** `chainFamily-version` + +```go +type TransferOwnershipAdapter interface { + // InitializeTimelockAddress resolves and caches the timelock address for use in ownership sequences. + InitializeTimelockAddress(e Environment, input mcms.Input) error + + // SequenceTransferOwnershipViaMCMS proposes ownership transfer of contracts through MCMS. + SequenceTransferOwnershipViaMCMS() *Sequence[TransferOwnershipPerChainInput, OnChainOutput, BlockChains] + + // SequenceAcceptOwnership accepts previously proposed ownership transfers. + SequenceAcceptOwnership() *Sequence[TransferOwnershipPerChainInput, OnChainOutput, BlockChains] + + // ShouldAcceptOwnershipWithTransferOwnership returns true if accept-ownership should be + // called automatically as part of the transfer-ownership flow (chain-specific behavior). + ShouldAcceptOwnershipWithTransferOwnership(e Environment, in TransferOwnershipPerChainInput) (bool, error) +} +``` + +**Registration:** +```go +deploy.GetTransferOwnershipRegistry().RegisterAdapter(chain_selectors.FamilyEVM, semver.MustParse("1.6.0"), &EVMAdapter{}) +``` + +--- + +### CurseAdapter + +Handles RMN (Risk Management Network) curse and uncurse operations on a chain. + +**Source:** [fastcurse/product.go](../fastcurse/product.go) +**Registry:** `CurseRegistry` via `fastcurse.GetCurseRegistry()` +**Key:** `chainFamily-version` + +```go +type CurseAdapter interface { + // Initialize sets up the adapter state for a given chain (e.g., loads RMN contract addresses). + Initialize(e Environment, selector uint64) error + + // IsSubjectCursedOnChain returns true if the given subject is cursed on the chain. + // Does NOT follow EVM RMN behavior of returning true for global curse. + // Use GlobalCurseSubject() to check global curse state. + IsSubjectCursedOnChain(e Environment, selector uint64, subject Subject) (bool, error) + + // IsChainConnectedToTargetChain returns true if the chain is connected to the target chain. + // E.g., on EVM, checks if router.isChainSupported(targetSel) returns true. + IsChainConnectedToTargetChain(e Environment, selector uint64, targetSel uint64) (bool, error) + + // IsCurseEnabledForChain returns true if the chain supports cursing + // (e.g., RMNRemote contract is deployed). + IsCurseEnabledForChain(e Environment, selector uint64) (bool, error) + + // SubjectToSelector converts a Subject to a chain selector. + SubjectToSelector(subject Subject) (uint64, error) + + // Curse returns a sequence that curses the given subjects on a chain. + Curse() *Sequence[CurseInput, OnChainOutput, BlockChains] + + // Uncurse returns a sequence that lifts curses on the given subjects. + Uncurse() *Sequence[CurseInput, OnChainOutput, BlockChains] + + // ListConnectedChains returns all chain selectors connected to this chain. + // Used to determine which chains need to curse subjects derived from a given selector. + ListConnectedChains(e Environment, selector uint64) ([]uint64, error) +} +``` + +--- + +### CurseSubjectAdapter + +Maps between chain selectors and curse subjects, and derives the correct curse adapter version for a chain. + +**Source:** [fastcurse/product.go](../fastcurse/product.go) +**Registry:** `CurseRegistry` via `fastcurse.GetCurseRegistry()` +**Key:** `chainFamily` (version-agnostic for subject mapping) + +```go +type CurseSubjectAdapter interface { + // SelectorToSubject converts a chain selector to a curse Subject. + SelectorToSubject(selector uint64) Subject + + // DeriveCurseAdapterVersion derives which version of the curse adapter to use for a chain. + // E.g., for EVM, this could check which RMN version is deployed on the chain. + DeriveCurseAdapterVersion(e Environment, selector uint64) (*semver.Version, error) +} +``` + +**Registration (both adapters together):** +```go +fastcurse.GetCurseRegistry().RegisterNewCurse(fastcurse.CurseRegistryInput{ + CursingFamily: chain_selectors.FamilyEVM, + CursingVersion: semver.MustParse("1.6.0"), + CurseAdapter: NewCurseAdapter(), + CurseSubjectAdapter: NewCurseAdapter(), +}) +``` + +--- + +## Optional Interfaces + +### TokenPriceProvider + +An optional interface that `LaneAdapter` implementations can also satisfy to provide default fee token prices. Primarily used by EVM chains. + +**Source:** [lanes/product.go](../lanes/product.go) +**Registry:** None -- checked via Go type assertion on the `LaneAdapter` instance. + +```go +type TokenPriceProvider interface { + // GetDefaultTokenPrices returns default fee token prices for a chain. + // Returns a map of contract type (e.g., "WETH", "LINK") to USD price (18 decimals). + GetDefaultTokenPrices() map[datastore.ContractType]*big.Int +} +``` + +--- + +### PingPongAdapter + +Supports the PingPong demo contract for testing lane connectivity. Chains that do not support PingPong (e.g., Solana) should not implement this. + +**Source:** [lanes/pingpong.go](../lanes/pingpong.go) +**Registry:** `PingPongAdapterRegistry` via `lanes.GetPingPongAdapterRegistry()` +**Key:** `chainFamily-version` + +```go +type PingPongAdapter interface { + // GetPingPongDemoAddress returns the PingPongDemo contract address for the given chain. + GetPingPongDemoAddress(ds datastore.DataStore, chainSelector uint64) ([]byte, error) + + // ConfigurePingPong configures PingPong for a lane between source and dest. + ConfigurePingPong() *Sequence[PingPongInput, PingPongOutput, BlockChains] +} +``` + +--- + +### ConfigImporter + +Imports configuration from existing deployments to bootstrap the DataStore. + +**Source:** [deploy/product.go](../deploy/product.go) +**Registry:** None currently. + +```go +type ConfigImporter interface { + // InitializeAdapter sets up the importer for the given chain selectors. + InitializeAdapter(e Environment, selectors []uint64) error + + // ConnectedChains returns the chain selectors connected to a given chain. + ConnectedChains(e Environment, chainsel uint64) ([]uint64, error) + + // SupportedTokensPerRemoteChain returns supported tokens per remote chain. + SupportedTokensPerRemoteChain(e Environment, chainSelector uint64) (map[uint64][]common.Address, error) + + // SequenceImportConfig returns a sequence to import lane config from on-chain state. + SequenceImportConfig() *Sequence[ImportConfigPerChainInput, OnChainOutput, BlockChains] +} +``` + +--- + +### RampUpdateInRouter + +Updates router configuration for lane migration scenarios (pointing routers to new ramps). + +**Source:** [deploy/lanemigrator.go](../deploy/lanemigrator.go) +**Registry:** `LaneMigratorRegistry` via `deploy.GetLaneMigratorRegistry()` (as RouterUpdater) +**Key:** `chainFamily-version` + +```go +type RampUpdateInRouter interface { + // UpdateRouter updates the router to point to new OnRamp/OffRamp contracts for remote chains. + UpdateRouter() *Sequence[RouterUpdaterConfig, OnChainOutput, BlockChains] +} +``` + +--- + +### RouterUpdateInRamp + +Updates ramp configuration with new router addresses for lane migration scenarios. + +**Source:** [deploy/lanemigrator.go](../deploy/lanemigrator.go) +**Registry:** `LaneMigratorRegistry` via `deploy.GetLaneMigratorRegistry()` (as RampUpdater) +**Key:** `chainFamily-version` + +```go +type RouterUpdateInRamp interface { + // UpdateVersionWithRouter updates OnRamp/OffRamp contracts with a new router address. + UpdateVersionWithRouter() *Sequence[RampUpdaterConfig, OnChainOutput, BlockChains] +} +``` + +--- + +### TestAdapter + +Interface for integration testing of cross-chain message passing. Each adapter instance represents a concrete chain. + +**Source:** [testadapters/adapters.go](../testadapters/adapters.go) +**Registry:** `TestAdapterRegistry` via `testadapters.GetTestAdapterRegistry()` +**Key:** `chainFamily-version` + +```go +type TestAdapter interface { + // ChainSelector returns the selector of the chain for this adapter. + ChainSelector() uint64 + + // Family returns the chain family string (e.g., "evm", "solana"). + Family() string + + // BuildMessage builds a chain-family-specific message from generic components. + // E.g., EVM produces router.ClientEVM2AnyMessage, Solana produces ccip_router.SVM2AnyMessage. + BuildMessage(components MessageComponents) (any, error) + + // SendMessage sends a CCIP message and returns the sequence number. + SendMessage(ctx context.Context, destChainSelector uint64, msg any) (uint64, error) + + // CCIPReceiver returns the address of a CCIP receiver contract on this chain. + CCIPReceiver() []byte + + // NativeFeeToken returns the native fee token identifier for this chain. + NativeFeeToken() string + + // GetExtraArgs returns encoded extra args for sending to this chain from a given source family. + // Extra args are source-family encoded (abi.encode for EVM, borsh for Solana). + GetExtraArgs(receiver []byte, sourceFamily string, opts ...ExtraArgOpt) ([]byte, error) + + // GetInboundNonce returns the inbound nonce for a sender from a source chain. + // Returns 0 for chains without nonce concepts. + GetInboundNonce(ctx context.Context, sender []byte, srcSel uint64) (uint64, error) + + // ValidateCommit validates that a message was committed on this chain. + ValidateCommit(t *testing.T, sourceSelector uint64, startBlock *uint64, seqNumRange ccipocr3.SeqNumRange) + + // ValidateExec validates that a message was executed on this chain and returns execution states. + ValidateExec(t *testing.T, sourceSelector uint64, startBlock *uint64, seqNrs []uint64) (execStates map[uint64]int) + + // AllowRouterToWithdrawTokens approves the router to spend tokens from the deployer. + AllowRouterToWithdrawTokens(ctx context.Context, tokenAddress string, amount *big.Int) error + + // GetTokenBalance returns the token balance for a given owner address. + GetTokenBalance(ctx context.Context, tokenAddress string, ownerAddress []byte) (*big.Int, error) + + // GetTokenExpansionConfig returns default token expansion config for testing. + GetTokenExpansionConfig() TokenExpansionInputPerChain + + // GetRegistryAddress returns the address of the token admin registry contract. + GetRegistryAddress() (string, error) +} +``` + +**Note:** `TestAdapter` is instantiated via a factory function: +```go +type TestAdapterFactory = func(env *Environment, selector uint64) TestAdapter +``` + +--- + +## Registry Accessor Summary + +| Registry | Accessor | Key Format | +|----------|----------|------------| +| `DeployerRegistry` | `deploy.GetRegistry()` | `chainFamily-version` | +| `TransferOwnershipAdapterRegistry` | `deploy.GetTransferOwnershipRegistry()` | `chainFamily-version` | +| `LaneAdapterRegistry` | `lanes.GetLaneAdapterRegistry()` | `chainFamily-version` | +| `PingPongAdapterRegistry` | `lanes.GetPingPongAdapterRegistry()` | `chainFamily-version` | +| `TokenAdapterRegistry` | `tokens.GetTokenAdapterRegistry()` | `chainFamily-version` | +| `FeeAdapterRegistry` | `fees.GetRegistry()` | `chainFamily-version` | +| `MCMSReaderRegistry` | `changesets.GetRegistry()` | `chainFamily` | +| `CurseRegistry` | `fastcurse.GetCurseRegistry()` | `chainFamily-version` / `chainFamily` | +| `LaneMigratorRegistry` | `deploy.GetLaneMigratorRegistry()` | `chainFamily-version` | +| `TestAdapterRegistry` | `testadapters.GetTestAdapterRegistry()` | `chainFamily-version` | diff --git a/deployment/docs/mcms-and-utilities.md b/deployment/docs/mcms-and-utilities.md new file mode 100644 index 0000000000..1cd2cb5826 --- /dev/null +++ b/deployment/docs/mcms-and-utilities.md @@ -0,0 +1,281 @@ +--- +title: "MCMS and Utilities Reference" +sidebar_label: "MCMS & Utilities" +sidebar_position: 7 +--- + +# MCMS and Utilities Reference + +This document covers the Multi-Chain Multi-Sig (MCMS) governance integration and shared utility functions used throughout the CCIP Deployment Tooling API. + +For overall system design, see [Architecture Guide](architecture.md). For the full list of adapter interfaces including `MCMSReader`, see [Interfaces Reference](interfaces.md). + +## MCMS Overview + +MCMS (Multi-Chain Multi-Sig) is the governance layer for contract operations across chains. It allows write operations to be: + +1. **Proposed** -- bundled into a `TimelockProposal` containing batch operations across one or more chains. +2. **Approved** -- signed by the required number of multi-sig signers. +3. **Executed** -- carried out via a timelock contract after an enforced delay. + +When a changeset produces write operations that the deployer key cannot execute directly (because the contracts are owned by the timelock), those operations are collected as `BatchOperation` entries and assembled into a proposal. The `OutputBuilder` and `MCMSReader` interface handle this assembly automatically. + +## mcms.Input + +`mcms.Input` configures how an MCMS proposal is constructed. Every changeset that may produce governance operations accepts this as part of its config. + +**Source:** [utils/mcms/mcms.go](../utils/mcms/mcms.go) + +```go +type Input struct { + OverridePreviousRoot bool + ValidUntil uint32 + TimelockDelay mcms_types.Duration + TimelockAction mcms_types.TimelockAction + Qualifier string + Description string +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `OverridePreviousRoot` | `bool` | When `true`, overrides the existing root of the MCMS contract. Set this when a previous proposal was not executed and its root should be replaced. | +| `ValidUntil` | `uint32` | Unix timestamp after which the proposal can no longer be set or executed. Acts as an expiration deadline. | +| `TimelockDelay` | `mcms_types.Duration` | Minimum wait time between scheduling an operation and executing it. Enforced on-chain by the timelock contract. | +| `TimelockAction` | `mcms_types.TimelockAction` | One of `schedule`, `bypass`, or `cancel`. Controls what the timelock does with the operations: queue them for delayed execution, execute immediately (bypasser role), or cancel previously scheduled operations. | +| `Qualifier` | `string` | Qualifies which MCMS + Timelock contract addresses to use. Allows multiple MCMS deployments to coexist (e.g., `"CLLCCIP"` for CLL-managed CCIP contracts, `"RMNMCMS"` for RMN-specific governance). | +| `Description` | `string` | Human-readable description included in the proposal for review by signers. | + +Validation enforces that `TimelockAction` is one of the three allowed values. + +## MCMSReader Interface + +The `MCMSReader` interface resolves chain-specific MCMS governance metadata. Each chain family registers one reader (keyed by `chainFamily` only, not versioned). + +**Source:** [utils/changesets/output.go](../utils/changesets/output.go) +**Registry:** `MCMSReaderRegistry` via `changesets.GetRegistry()` +**Full definition:** See [Interfaces Reference -- MCMSReader](interfaces.md#mcmsreader) + +```go +type MCMSReader interface { + GetChainMetadata(e Environment, chainSelector uint64, input mcms.Input) (mcms_types.ChainMetadata, error) + GetTimelockRef(e Environment, chainSelector uint64, input mcms.Input) (datastore.AddressRef, error) + GetMCMSRef(e Environment, chainSelector uint64, input mcms.Input) (datastore.AddressRef, error) +} +``` + +Changesets use `MCMSReader` indirectly through `OutputBuilder`. When `Build()` is called, the builder iterates over the batch operations, determines each chain's family from its selector, looks up the registered reader, and calls: + +- `GetTimelockRef` -- to resolve the timelock contract address that will execute the operations. +- `GetChainMetadata` -- to fetch the starting operation count and MCM address needed by the proposal builder. + +The `MCMSReaderRegistry` is a thread-safe singleton. Readers register in `init()`: + +```go +changesets.GetRegistry().RegisterMCMSReader(chain_selectors.FamilyEVM, &EVMMCMSReader{}) +``` + +## OutputBuilder + +`OutputBuilder` is a builder pattern that assembles a `ChangesetOutput` from sequence execution results, optionally including an MCMS `TimelockProposal` when there are batch operations to govern. + +**Source:** [utils/changesets/output.go](../utils/changesets/output.go) + +### Construction + +```go +output, err := changesets.NewOutputBuilder(env, mcmsRegistry). + WithReports(reports). + WithBatchOps(batchOps). + WithDataStore(ds). + Build(mcmsInput) +``` + +### Methods + +| Method | Signature | Description | +|--------|-----------|-------------| +| `NewOutputBuilder` | `NewOutputBuilder(e Environment, registry *MCMSReaderRegistry) *OutputBuilder` | Creates a new builder bound to an environment and MCMS reader registry. | +| `WithReports` | `WithReports(reports []operations.Report[any, any]) *OutputBuilder` | Attaches execution reports to the output. Reports contain operation-level traces for debugging and retry. | +| `WithBatchOps` | `WithBatchOps(ops []mcms_types.BatchOperation) *OutputBuilder` | Sets the MCMS batch operations. Automatically filters out any `BatchOperation` entries with zero transactions. | +| `WithDataStore` | `WithDataStore(ds datastore.MutableDataStore) *OutputBuilder` | Attaches a `MutableDataStore` containing newly deployed addresses and metadata. | +| `Build` | `Build(input mcms.Input) (ChangesetOutput, error)` | Finalizes the output. If batch operations exist, resolves timelock addresses and chain metadata via the `MCMSReaderRegistry`, then constructs and attaches a `TimelockProposal`. If no batch operations are present, returns the output without a proposal. | + +### Build Internals + +When `Build` is called with non-empty batch operations: + +1. For each unique chain selector in the batch operations, the builder resolves the chain family and retrieves the registered `MCMSReader`. +2. `getTimelockAddresses` calls `GetTimelockRef` for each chain to build a `map[ChainSelector]string` of timelock addresses. +3. `getChainMetadata` calls `GetChainMetadata` for each chain to build a `map[ChainSelector]ChainMetadata`. +4. These maps are passed to `mcms.NewTimelockProposalBuilder` along with the `mcms.Input` fields to construct the final `TimelockProposal`. + +## OnChainOutput + +`OnChainOutput` is the standard return type for sequences that deploy contracts or perform on-chain write operations. It aggregates all artifacts produced during sequence execution. + +**Source:** [utils/sequences/sequences.go](../utils/sequences/sequences.go) + +```go +type OnChainOutput struct { + Addresses []datastore.AddressRef + Metadata Metadata + BatchOps []mcms_types.BatchOperation +} + +type Metadata struct { + Contracts []datastore.ContractMetadata + Chain *datastore.ChainMetadata + Env *datastore.EnvMetadata +} +``` + +| Field | Description | +|-------|-------------| +| `Addresses` | Contract addresses deployed or managed by the sequence. Each `AddressRef` includes chain selector, address, contract type, version, and optional qualifier. | +| `Metadata.Contracts` | Per-contract metadata entries. Keyed by address + chain selector in the datastore (upsert semantics). | +| `Metadata.Chain` | Per-chain metadata. At most one per sequence; keyed by chain selector. | +| `Metadata.Env` | Per-environment metadata. At most one per environment. | +| `BatchOps` | Ordered list of `BatchOperation` entries for MCMS proposal construction. Each batch operation is executed atomically. Order is preserved during proposal assembly. | + +## Sequence Utilities + +### RunAndMergeSequence + +Composes sub-sequences by executing them and merging their `OnChainOutput` into an aggregator. This is the primary mechanism for building complex workflows from smaller, focused sequences. + +**Source:** [utils/sequences/sequences.go](../utils/sequences/sequences.go) + +```go +func RunAndMergeSequence[IN any]( + b operations.Bundle, + chains cldf_chain.BlockChains, + seq *operations.Sequence[IN, OnChainOutput, cldf_chain.BlockChains], + input IN, + agg OnChainOutput, +) (OnChainOutput, error) +``` + +Merge behavior: + +| Field | Strategy | +|-------|----------| +| `BatchOps` | Appended to the aggregator's list. | +| `Addresses` | Appended to the aggregator's list. | +| `Metadata.Contracts` | Appended to the aggregator's list. | +| `Metadata.Chain` | Set if aggregator has none; returns an error if both the aggregator and the sub-sequence provide conflicting chain metadata. | +| `Metadata.Env` | Set if aggregator has none; returns an error on conflict. | + +### WriteMetadataToDatastore + +Persists metadata from an `OnChainOutput` into a `MutableDataStore`. + +**Source:** [utils/sequences/sequences.go](../utils/sequences/sequences.go) + +```go +func WriteMetadataToDatastore(ds datastore.MutableDataStore, metadata Metadata) error +``` + +Upsert behavior: + +- **Contract metadata**: Each entry is upserted individually. Key = address + chain selector. +- **Chain metadata**: Upserted if non-nil. Key = chain selector. +- **Env metadata**: Set if non-nil. One record per environment. + +Because upsert replaces the entire record for a given key, callers must include all required fields when writing to a key that may already exist. + +## Common Utility Functions + +**Source:** [utils/common.go](../utils/common.go) + +### NewRegistererID + +Creates a composite registry key from a chain family and semver version: + +```go +func NewRegistererID(chainFamily string, version *semver.Version) string +``` + +Returns `fmt.Sprintf("%s-%s", chainFamily, version.String())`, e.g., `"evm-1.6.0"` or `"solana-1.6.0"`. + +### NewIDFromSelector + +Resolves the chain family from a numeric chain selector and creates the registry key: + +```go +func NewIDFromSelector(chainSelector uint64, version *semver.Version) string +``` + +Uses `chain_selectors.GetSelectorFamily` to determine the family, then delegates to `NewRegistererID`. Panics if the chain selector is invalid. + +### GetSelectorHex + +Returns the 4-byte on-chain family selector for a given chain selector: + +```go +func GetSelectorHex(selector uint64) []byte +``` + +Maps chain families to their on-chain identifiers (derived from `keccak256` of the family name): + +| Family | Hex Constant | Value | +|--------|-------------|-------| +| EVM | `EVMFamilySelector` | `0x2812d52c` | +| SVM (Solana) | `SVMFamilySelector` | `0x1e10bdc4` | +| Aptos | `AptosFamilySelector` | `0xac77ffec` | +| TVM (TON) | `TVMFamilySelector` | `0x647e2ba9` | +| Sui | `SuiFamilySelector` | `0xc4e05953` | + +These selectors are defined in the CCIP Solidity library (`Internal.sol`) and must match across all chain families. + +## Version Constants + +**Source:** [utils/common.go](../utils/common.go) + +Pre-parsed `semver.Version` values used for adapter registration and contract version identification: + +| Constant | Value | +|----------|-------| +| `Version_1_0_0` | `1.0.0` | +| `Version_1_5_0` | `1.5.0` | +| `Version_1_5_1` | `1.5.1` | +| `Version_1_6_0` | `1.6.0` | +| `Version_1_6_1` | `1.6.1` | + +These are created with `semver.MustParse` and are safe to use as map keys and in comparisons. + +## Contract Type Constants + +**Source:** [utils/common.go](../utils/common.go) + +Shared `cldf.ContractType` strings used across all chain families for datastore lookups and adapter registration: + +| Constant | String Value | Description | +|----------|-------------|-------------| +| `BypasserManyChainMultisig` | `"BypasserManyChainMultiSig"` | MCMS bypasser multi-sig contract | +| `CancellerManyChainMultisig` | `"CancellerManyChainMultiSig"` | MCMS canceller multi-sig contract | +| `ProposerManyChainMultisig` | `"ProposerManyChainMultiSig"` | MCMS proposer multi-sig contract | +| `RBACTimelock` | `"RBACTimelock"` | Role-based access control timelock | +| `CallProxy` | `"CallProxy"` | Call proxy contract | +| `CapabilitiesRegistry` | `"CapabilitiesRegistry"` | DON capabilities registry | +| `CCIPHome` | `"CCIPHome"` | CCIP home contract (chain config) | +| `RMNHome` | `"RMNHome"` | RMN home contract | +| `BurnMintTokenPool` | `"BurnMintTokenPool"` | Burn-and-mint token pool | +| `LockReleaseTokenPool` | `"LockReleaseTokenPool"` | Lock-and-release token pool | +| `TokenPoolLookupTable` | `"TokenPoolLookupTable"` | Token pool lookup table | +| `BurnWithFromMintTokenPool` | `"BurnWithFromMintTokenPool"` | Burn-with-from-mint token pool | +| `BurnFromMintTokenPool` | `"BurnFromMintTokenPool"` | Burn-from-mint token pool | +| `CCTPTokenPool` | `"CCTPTokenPool"` | CCTP (Circle) token pool | + +Additional qualifier constants: + +| Constant | Value | Purpose | +|----------|-------|---------| +| `CLLQualifier` | `"CLLCCIP"` | Qualifies CLL-managed CCIP contract addresses | +| `RMNTimelockQualifier` | `"RMNMCMS"` | Qualifies RMN-specific MCMS contract addresses | + +These qualifiers are passed via `mcms.Input.Qualifier` to scope timelock and multi-sig resolution to the correct deployment set. + +--- + +Cross-reference [Architecture Guide](architecture.md) for how MCMS proposal construction fits into the changeset execution flow, and [Interfaces Reference](interfaces.md) for the full `MCMSReader` interface definition and all other adapter interfaces. diff --git a/deployment/docs/types.md b/deployment/docs/types.md new file mode 100644 index 0000000000..2ca7463b84 --- /dev/null +++ b/deployment/docs/types.md @@ -0,0 +1,838 @@ +--- +title: "Types Reference" +sidebar_label: "Types" +sidebar_position: 4 +--- + +# Types Reference + +This document provides a complete reference for all input/output types used across the CCIP Deployment Tooling API. + +For the interfaces that use these types, see [Interfaces Reference](interfaces.md). For the changesets that accept these types as input, see [Changesets Reference](changesets.md). + +--- + +## Table of Contents + +- [Deployment Types](#deployment-types) +- [MCMS Deployment Types](#mcms-deployment-types) +- [Lane Types](#lane-types) +- [OCR3 Types](#ocr3-types) +- [Token Types](#token-types) +- [Fee Types](#fee-types) +- [Ownership Types](#ownership-types) +- [MCMS Input Types](#mcms-input-types) +- [Output Types](#output-types) +- [Curse Types](#curse-types) +- [Test Adapter Types](#test-adapter-types) +- [Constants](#constants) + +--- + +## Deployment Types + +**Source:** [deploy/contracts.go](../deploy/contracts.go) + +### ContractDeploymentConfig + +Top-level input for the `DeployContracts` changeset. + +```go +type ContractDeploymentConfig struct { + Chains map[uint64]ContractDeploymentConfigPerChain + MCMS mcms.Input +} +``` + +### ContractDeploymentConfigPerChain + +Per-chain configuration for deploying CCIP contracts. + +```go +type ContractDeploymentConfigPerChain struct { + Version *semver.Version + TokenPrivKey string // Private key for LINK token deployment (Solana: base58) + TokenDecimals uint8 // LINK token decimals + MaxFeeJuelsPerMsg *big.Int // FeeQuoter: max fee per message + TokenPriceStalenessThreshold uint32 // FeeQuoter: staleness threshold + LinkPremiumMultiplier uint64 // FeeQuoter: LINK premium (Wei per ETH on EVM) + NativeTokenPremiumMultiplier uint64 // FeeQuoter: native token premium (Wei per ETH on EVM) + PermissionLessExecutionThresholdSeconds uint32 // OffRamp: threshold for manual execution + GasForCallExactCheck uint16 // OffRamp: EVM only + MessageInterceptor string // OffRamp: EVM only, validates incoming messages + LegacyRMN string // RMN Remote config + ContractVersion string // Contract version string + DeployPingPongDapp bool // Deploy PingPongDemo contract +} +``` + +### ContractDeploymentConfigPerChainWithAddress + +Extends `ContractDeploymentConfigPerChain` with chain selector and existing addresses (populated by the framework). + +```go +type ContractDeploymentConfigPerChainWithAddress struct { + ContractDeploymentConfigPerChain + ChainSelector uint64 + ExistingAddresses []datastore.AddressRef +} +``` + +--- + +## MCMS Deployment Types + +**Source:** [deploy/mcms.go](../deploy/mcms.go) + +### MCMSDeploymentConfig + +Top-level input for `DeployMCMS` and `FinalizeDeployMCMS` changesets. + +```go +type MCMSDeploymentConfig struct { + Chains map[uint64]MCMSDeploymentConfigPerChain + AdapterVersion *semver.Version + MCMS mcms.Input +} +``` + +### MCMSDeploymentConfigPerChain + +Per-chain MCMS configuration specifying multi-sig roles and timelock settings. + +```go +type MCMSDeploymentConfigPerChain struct { + Canceller mcmstypes.Config // MCM canceller role configuration + Bypasser mcmstypes.Config // MCM bypasser role configuration + Proposer mcmstypes.Config // MCM proposer role configuration + TimelockMinDelay *big.Int // Minimum delay for timelock operations + Label *string // Optional label for the MCMS instance + Qualifier *string // Optional qualifier for the MCMS instance + TimelockAdmin common.Address // Admin address for the timelock + ContractVersion string // Contract version string +} +``` + +### MCMSDeploymentConfigPerChainWithAddress + +Extends `MCMSDeploymentConfigPerChain` with chain selector and existing addresses. + +```go +type MCMSDeploymentConfigPerChainWithAddress struct { + MCMSDeploymentConfigPerChain + ChainSelector uint64 + ExistingAddresses []datastore.AddressRef +} +``` + +### GrantAdminRoleToTimelockConfig + +Input for the `GrantAdminRoleToTimelock` changeset. + +```go +type GrantAdminRoleToTimelockConfig struct { + Chains map[uint64]GrantAdminRoleToTimelockConfigPerChain + AdapterVersion *semver.Version +} +``` + +### GrantAdminRoleToTimelockConfigPerChain + +Per-chain config specifying which timelock transfers admin rights and which receives them. + +```go +type GrantAdminRoleToTimelockConfigPerChain struct { + TimelockToTransferRef datastore.AddressRef // Timelock that transfers its admin rights + NewAdminTimelockRef datastore.AddressRef // Timelock that will be granted admin +} +``` + +--- + +## Lane Types + +**Source:** [lanes/lane_update.go](../lanes/lane_update.go) + +### ConnectChainsConfig + +Top-level input for the `ConnectChains` changeset. Configures bidirectional lanes between chains. + +```go +type ConnectChainsConfig struct { + Lanes []LaneConfig + MCMS mcms.Input +} +``` + +### LaneConfig + +Defines a single bidirectional lane between two chains. + +```go +type LaneConfig struct { + ChainA ChainDefinition + ChainB ChainDefinition + Version *semver.Version + IsDisabled bool + TestRouter bool // Use test router instead of production + ExtraConfigs ExtraConfigs +} +``` + +### ChainDefinition + +Complete definition of a chain's role in a lane. Some fields are user-provided, others are populated programmatically. + +```go +type ChainDefinition struct { + // User-provided fields + Selector uint64 // Chain selector + GasPrice *big.Int // USD price (18 decimals) per unit gas + TokenPrices map[string]*big.Int // Token USD prices (18 decimals) + FeeQuoterDestChainConfig FeeQuoterDestChainConfig // Fee config when this chain is a destination + RMNVerificationEnabled bool // RMN blessing for messages FROM this chain + AllowListEnabled bool // Allowlist for messages TO this chain + AllowList []string // Allowed sender addresses + + // Populated programmatically (do not set) + OnRamp []byte // OnRamp contract address + OffRamp []byte // OffRamp contract address + Router []byte // Router contract address + FeeQuoter []byte // FeeQuoter contract address +} +``` + +### FeeQuoterDestChainConfig + +Fee configuration applied on a source chain when the target chain is a destination. + +```go +type FeeQuoterDestChainConfig struct { + IsEnabled bool + MaxNumberOfTokensPerMsg uint16 + MaxDataBytes uint32 + MaxPerMsgGasLimit uint32 + DestGasOverhead uint32 + DestGasPerPayloadByteBase uint8 + DestGasPerPayloadByteHigh uint8 + DestGasPerPayloadByteThreshold uint16 + DestDataAvailabilityOverheadGas uint32 + DestGasPerDataAvailabilityByte uint16 + DestDataAvailabilityMultiplierBps uint16 + ChainFamilySelector uint32 + EnforceOutOfOrder bool + DefaultTokenFeeUSDCents uint16 + DefaultTokenDestGasOverhead uint32 + DefaultTxGasLimit uint32 + GasMultiplierWeiPerEth uint64 + GasPriceStalenessThreshold uint32 + NetworkFeeUSDCents uint32 +} +``` + +A default configuration is available via `DefaultFeeQuoterDestChainConfig(configEnabled bool, selector uint64)`. + +### UpdateLanesInput + +Input passed to `LaneAdapter.ConfigureLaneLegAsSource()` and `ConfigureLaneLegAsDest()` sequences. + +```go +type UpdateLanesInput struct { + Source *ChainDefinition + Dest *ChainDefinition + IsDisabled bool + TestRouter bool + ExtraConfigs ExtraConfigs +} +``` + +### ExtraConfigs + +Additional lane configuration options. + +```go +type ExtraConfigs struct { + OnRampVersion []byte +} +``` + +--- + +## OCR3 Types + +**Source:** [deploy/set_ocr3_config.go](../deploy/set_ocr3_config.go) + +### SetOCR3ConfigArgs + +Top-level input for the `SetOCR3Config` changeset. + +```go +type SetOCR3ConfigArgs struct { + HomeChainSel uint64 // Home chain selector (where CCIPHome lives) + RemoteChainSels []uint64 // Remote chains to configure + ConfigType utils.ConfigType // "active" or "candidate" + MCMS mcms.Input +} +``` + +### SetOCR3ConfigInput + +Per-chain input passed to the `Deployer.SetOCR3Config()` sequence. + +```go +type SetOCR3ConfigInput struct { + ChainSelector uint64 + Datastore datastore.DataStore + Configs map[ccipocr3.PluginType]OCR3ConfigArgs +} +``` + +### OCR3ConfigArgs + +OCR3 configuration parameters for a single plugin. + +```go +type OCR3ConfigArgs struct { + ConfigDigest [32]byte + PluginType ccipocr3.PluginType + F uint8 // Faulty nodes tolerance + IsSignatureVerificationEnabled bool + Signers [][]byte + Transmitters [][]byte +} +``` + +--- + +## Token Types + +**Source:** [tokens/product.go](../tokens/product.go), [tokens/token_expansion.go](../tokens/token_expansion.go), [tokens/configure_tokens_for_transfers.go](../tokens/configure_tokens_for_transfers.go), [tokens/manual_registration.go](../tokens/manual_registration.go), [tokens/rate_limits.go](../tokens/rate_limits.go) + +### TokenExpansionInput + +Top-level input for the `TokenExpansion` changeset. Deploys tokens, pools, and configures them for cross-chain transfers. + +```go +type TokenExpansionInput struct { + TokenExpansionInputPerChain map[uint64]TokenExpansionInputPerChain + ChainAdapterVersion *semver.Version + MCMS mcms.Input +} +``` + +### TokenExpansionInputPerChain + +Per-chain token expansion configuration. + +```go +type TokenExpansionInputPerChain struct { + TokenPoolVersion *semver.Version + DeployTokenInput *DeployTokenInput // nil = token already deployed + DeployTokenPoolInput *DeployTokenPoolInput // nil = pool already deployed + TokenTransferConfig *TokenTransferConfig // nil = skip transfer configuration + SkipOwnershipTransfer bool // Skip timelock ownership transfer +} +``` + +### DeployTokenInput + +Input for deploying a new token. + +```go +type DeployTokenInput struct { + Name string // Token name + Symbol string // Token symbol + Decimals uint8 // Token decimals + Supply *big.Int // Total supply + PreMint *big.Int // Amount to pre-mint + ExternalAdmin string // External admin address (chain-agnostic string) + CCIPAdmin string // CCIP admin address (defaults to timelock) + Senders []string // Addresses needing special processing (e.g., Solana ATAs) + Type cldf.ContractType // SPLToken, ERC20, etc. + TokenPrivKey string // Solana: base58 private key for vanity addresses + DisableFreezeAuthority bool // Solana: revoke freeze authority permanently + TokenMetadata *TokenMetadata // Solana: token metadata to upload + // Populated programmatically + ChainSelector uint64 + ExistingDataStore datastore.DataStore +} +``` + +### TokenMetadata + +Token metadata for Solana tokens (extensible to other VMs). + +```go +type TokenMetadata struct { + TokenPubkey string // Populated programmatically + MetadataJSONPath string // Path to metadata JSON (initial upload) + UpdateAuthority string // Update authority for metadata PDA + UpdateName string // Update token name after upload + UpdateSymbol string // Update token symbol after upload + UpdateURI string // Update token URI after upload +} +``` + +### DeployTokenPoolInput + +Input for deploying a token pool. + +```go +type DeployTokenPoolInput struct { + TokenRef *datastore.AddressRef // Reference to the token + TokenPoolQualifier string // Pool qualifier in DataStore + PoolType string // BurnMintTokenPool, LockReleaseTokenPool, etc. + TokenPoolVersion *semver.Version + Allowlist []string + AcceptLiquidity *bool // LockReleaseTokenPool v1.5.1 only + BurnAddress string // BurnToAddressMintTokenPool only + TokenGovernor string // BurnMintWithExternalMinterTokenPool only + // Populated programmatically + ChainSelector uint64 + ExistingDataStore datastore.DataStore +} +``` + +### TokenTransferConfig + +Configuration for enabling a token for cross-chain transfers. + +```go +type TokenTransferConfig struct { + ChainSelector uint64 // Target chain + TokenPoolRef datastore.AddressRef // Token pool reference + TokenRef datastore.AddressRef // Token reference + ExternalAdmin string // External admin (leave empty for internal) + RegistryRef datastore.AddressRef // Token admin registry reference + RemoteChains map[uint64]RemoteChainConfig[*datastore.AddressRef, datastore.AddressRef] +} +``` + +### ConfigureTokensForTransfersConfig + +Top-level input for the `ConfigureTokensForTransfers` changeset. + +```go +type ConfigureTokensForTransfersConfig struct { + ChainAdapterVersion *semver.Version + Tokens []TokenTransferConfig + MCMS mcms.Input +} +``` + +### ConfigureTokenForTransfersInput + +Per-chain input passed to the `TokenAdapter.ConfigureTokenForTransfersSequence()`. + +```go +type ConfigureTokenForTransfersInput struct { + ChainSelector uint64 + TokenPoolAddress string + RemoteChains map[uint64]RemoteChainConfig[[]byte, string] + ExternalAdmin string + RegistryAddress string + // Populated programmatically + ExistingDataStore datastore.DataStore + PoolType string + TokenRef datastore.AddressRef +} +``` + +### RemoteChainConfig + +Generic configuration for a remote chain on a token pool. Parameterized by address type (`R`) and cross-chain verifier type (`CCV`). + +```go +type RemoteChainConfig[R any, CCV any] struct { + RemoteToken R // Token on remote chain + RemotePool R // Token pool on remote chain + RemoteDecimals uint8 // Token decimals on remote chain + InboundRateLimiterConfig RateLimiterConfigFloatInput // Derived from counterpart outbound + OutboundRateLimiterConfig RateLimiterConfigFloatInput // User-specified + OutboundCCVs []CCV // Outbound cross-chain verifiers + InboundCCVs []CCV // Inbound cross-chain verifiers +} +``` + +### RateLimiterConfig + +On-chain rate limiter configuration (uses `*big.Int` for precision). + +```go +type RateLimiterConfig struct { + IsEnabled bool + Capacity *big.Int // Maximum tokens in bucket + Rate *big.Int // Refill rate (tokens per second) +} +``` + +### RateLimiterConfigFloatInput + +User-friendly rate limiter input (float values scaled by token decimals internally). + +```go +type RateLimiterConfigFloatInput struct { + IsEnabled bool + Capacity float64 + Rate float64 +} +``` + +### ManualRegistrationInput + +Top-level input for the `ManualRegistration` changeset. + +```go +type ManualRegistrationInput struct { + ChainAdapterVersion *semver.Version + Registrations []RegisterTokenConfig + MCMS mcms.Input +} +``` + +### RegisterTokenConfig + +Per-registration configuration for manual token registration. + +```go +type RegisterTokenConfig struct { + TokenPoolRef datastore.AddressRef // Token pool reference (always required on SVM) + TokenRef datastore.AddressRef // Token reference (always required on SVM) + ChainSelector uint64 // Chain selector (required) + ProposedOwner string // Proposed owner address (required) + SVMExtraArgs *SVMExtraArgs // SVM-specific extra args (optional) +} +``` + +### SVMExtraArgs + +Solana-specific arguments for manual registration. + +```go +type SVMExtraArgs struct { + CustomerMintAuthorities []solana.PublicKey + SkipTokenPoolInit bool +} +``` + +### TPRLInput + +Top-level input for the `SetTokenPoolRateLimits` changeset. + +```go +type TPRLInput struct { + Configs map[uint64]TPRLConfig + MCMS mcms.Input +} +``` + +### TPRLConfig + +Per-chain rate limit configuration. + +```go +type TPRLConfig struct { + ChainAdapterVersion *semver.Version + TokenRef datastore.AddressRef + TokenPoolRef datastore.AddressRef + RemoteOutbounds map[uint64]RateLimiterConfigFloatInput // Remote chain -> outbound limits +} +``` + +### TPRLRemotes + +Per-remote-chain input passed to the `TokenAdapter.SetTokenPoolRateLimits()` sequence. + +```go +type TPRLRemotes struct { + OutboundRateLimiterConfig RateLimiterConfig + InboundRateLimiterConfig RateLimiterConfig + ChainSelector uint64 + RemoteChainSelector uint64 + TokenRef datastore.AddressRef + TokenPoolRef datastore.AddressRef + ExistingDataStore datastore.DataStore +} +``` + +### UpdateAuthoritiesInput + +Input for transferring token and pool ownership to the timelock. + +```go +type UpdateAuthoritiesInput struct { + ChainSelector uint64 + TokenRef datastore.AddressRef + TokenPoolRef datastore.AddressRef +} +``` + +--- + +## Fee Types + +**Source:** [fees/models.go](../fees/models.go), [fees/product.go](../fees/product.go) + +### SetTokenTransferFeeSequenceInput + +Input passed to the `FeeAdapter.SetTokenTransferFee()` sequence. + +```go +type SetTokenTransferFeeSequenceInput struct { + // Settings maps destination chain selector -> token address -> fee args + Settings map[uint64]map[string]*TokenTransferFeeArgs + Selector uint64 +} +``` + +### TokenTransferFeeArgs + +Standardized token transfer fee configuration for all chain families. + +```go +type TokenTransferFeeArgs struct { + DestBytesOverhead uint32 // Additional bytes overhead on destination + DestGasOverhead uint32 // Additional gas overhead on destination + MinFeeUSDCents uint32 // Minimum fee in USD cents + MaxFeeUSDCents uint32 // Maximum fee in USD cents + DeciBps uint16 // Fee in deci-basis points (1/10th of a basis point) + IsEnabled bool // Whether fee is enabled +} +``` + +### UnresolvedTokenTransferFeeArgs + +Allows partial specification of fee configuration. Unset fields are auto-filled from on-chain data or defaults. + +```go +type UnresolvedTokenTransferFeeArgs struct { + DestBytesOverhead TokenTransferFeeValue[uint32] + DestGasOverhead TokenTransferFeeValue[uint32] + MinFeeUSDCents TokenTransferFeeValue[uint32] + MaxFeeUSDCents TokenTransferFeeValue[uint32] + DeciBps TokenTransferFeeValue[uint16] + IsEnabled TokenTransferFeeValue[bool] +} +``` + +### TokenTransferFeeValue + +A wrapper that indicates whether a value was explicitly set or should use a fallback. + +```go +type TokenTransferFeeValue[T any] struct { + Valid bool // If false, Value is auto-filled from on-chain data or defaults + Value T // Only used when Valid is true +} +``` + +--- + +## Ownership Types + +**Source:** [deploy/transfer_ownership.go](../deploy/transfer_ownership.go) + +### TransferOwnershipInput + +Top-level input for `TransferOwnership` and `AcceptOwnership` changesets. + +```go +type TransferOwnershipInput struct { + ChainInputs []TransferOwnershipPerChainInput + AdapterVersion *semver.Version + MCMS mcms.Input +} +``` + +### TransferOwnershipPerChainInput + +Per-chain ownership transfer configuration. + +```go +type TransferOwnershipPerChainInput struct { + ChainSelector uint64 + ContractRef []datastore.AddressRef // Contracts to transfer ownership of + CurrentOwner string + ProposedOwner string +} +``` + +--- + +## MCMS Input Types + +**Source:** [utils/mcms/mcms.go](../utils/mcms/mcms.go) + +### mcms.Input + +Configuration for MCMS proposal construction. Included in most changeset inputs. + +```go +type Input struct { + OverridePreviousRoot bool // Override existing MCMS root + ValidUntil uint32 // Unix timestamp for proposal expiry + TimelockDelay mcms_types.Duration // Delay before operations can execute + TimelockAction mcms_types.TimelockAction // schedule, bypass, or cancel + Qualifier string // Qualifier for MCMS + Timelock addresses + Description string // Human-readable proposal description +} +``` + +**TimelockAction values:** +- `mcms_types.TimelockActionSchedule` -- schedule operations through the timelock +- `mcms_types.TimelockActionBypass` -- bypass the timelock (immediate execution) +- `mcms_types.TimelockActionCancel` -- cancel pending operations + +--- + +## Output Types + +**Source:** [utils/sequences/sequences.go](../utils/sequences/sequences.go) + +### OnChainOutput + +Standard output type returned by all sequences. + +```go +type OnChainOutput struct { + Addresses []datastore.AddressRef // Deployed contract addresses + Metadata Metadata // Execution metadata + BatchOps []mcms_types.BatchOperation // MCMS batch operations +} +``` + +### Metadata + +Metadata about sequence execution, persisted to the DataStore. + +```go +type Metadata struct { + Contracts []datastore.ContractMetadata // Contract-level metadata + Chain *datastore.ChainMetadata // Chain-level metadata + Env *datastore.EnvMetadata // Environment-level metadata +} +``` + +--- + +## Curse Types + +**Source:** [fastcurse/product.go](../fastcurse/product.go) + +See [Interfaces Reference](interfaces.md#curseadapter) for `CurseInput`, `Subject`, and related types used by `CurseAdapter` and `CurseSubjectAdapter`. + +--- + +## Test Adapter Types + +**Source:** [testadapters/adapters.go](../testadapters/adapters.go) + +### MessageComponents + +Generic CCIP message components, independent of chain family. + +```go +type MessageComponents struct { + DestChainSelector uint64 // Destination chain selector + Receiver []byte // Dest-chain-encoded receiver address + Data []byte // Message payload + FeeToken string // Fee token identifier (source-chain encoded) + ExtraArgs []byte // Message extra args (chain-family encoded) + TokenAmounts []TokenAmount +} +``` + +### TokenAmount + +Token and amount for cross-chain transfers. + +```go +type TokenAmount struct { + Token string // Source-chain-encoded token address + Amount *big.Int +} +``` + +### ExtraArgOpt + +Chain-agnostic representation of a message extra arg. + +```go +type ExtraArgOpt struct { + Name string + Value any +} +``` + +Constructors: `NewOutOfOrderExtraArg(bool)`, `NewGasLimitExtraArg(*big.Int)`. + +--- + +## Constants + +**Source:** [utils/common.go](../utils/common.go) + +### Contract Type Constants + +```go +const ( + BypasserManyChainMultisig = "BypasserManyChainMultiSig" + CancellerManyChainMultisig = "CancellerManyChainMultiSig" + ProposerManyChainMultisig = "ProposerManyChainMultiSig" + RBACTimelock = "RBACTimelock" + CallProxy = "CallProxy" + CapabilitiesRegistry = "CapabilitiesRegistry" + CCIPHome = "CCIPHome" + RMNHome = "RMNHome" + BurnMintTokenPool = "BurnMintTokenPool" + LockReleaseTokenPool = "LockReleaseTokenPool" + TokenPoolLookupTable = "TokenPoolLookupTable" + BurnWithFromMintTokenPool = "BurnWithFromMintTokenPool" + BurnFromMintTokenPool = "BurnFromMintTokenPool" + CCTPTokenPool = "CCTPTokenPool" +) +``` + +### Version Constants + +```go +var ( + Version_1_0_0 = semver.MustParse("1.0.0") + Version_1_5_0 = semver.MustParse("1.5.0") + Version_1_5_1 = semver.MustParse("1.5.1") + Version_1_6_0 = semver.MustParse("1.6.0") + Version_1_6_1 = semver.MustParse("1.6.1") +) +``` + +### Chain Family Selectors + +On-chain identifiers for each chain family, derived from `keccak256`: + +| Family | Selector | Constant | +|--------|----------|----------| +| EVM | `0x2812d52c` | `EVMFamilySelector` | +| SVM (Solana) | `0x1e10bdc4` | `SVMFamilySelector` | +| Aptos | `0xac77ffec` | `AptosFamilySelector` | +| TVM (TON) | `0x647e2ba9` | `TVMFamilySelector` | +| Sui | `0xc4e05953` | `SuiFamilySelector` | + +### Qualifier Constants + +```go +const ( + CLLQualifier = "CLLCCIP" // Standard CLL qualifier + RMNTimelockQualifier = "RMNMCMS" // RMN timelock qualifier +) +``` + +### Execution State Constants + +```go +const ( + EXECUTION_STATE_UNTOUCHED = 0 + EXECUTION_STATE_INPROGRESS = 1 + EXECUTION_STATE_SUCCESS = 2 + EXECUTION_STATE_FAILURE = 3 +) +``` diff --git a/deployment/go.mod b/deployment/go.mod index 7e8d9e4a67..fdd641dae1 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -10,21 +10,21 @@ require ( github.com/BurntSushi/toml v1.5.0 github.com/Masterminds/semver/v3 v3.4.0 github.com/aptos-labs/aptos-go-sdk v1.11.0 - github.com/deckarep/golang-set/v2 v2.6.0 - github.com/ethereum/go-ethereum v1.17.0 + github.com/deckarep/golang-set/v2 v2.8.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/evanphx/json-patch/v5 v5.9.11 github.com/gagliardetto/solana-go v1.13.0 github.com/google/uuid v1.6.0 github.com/smartcontractkit/chain-selectors v1.0.97 github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260129103204-4c8453dd8139 - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 github.com/smartcontractkit/chainlink-deployments-framework v0.80.2 github.com/smartcontractkit/chainlink-evm v0.3.3 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3544aab8f3 github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 github.com/smartcontractkit/mcms v0.36.0 github.com/stretchr/testify v1.11.1 - google.golang.org/grpc v1.77.0 + google.golang.org/grpc v1.78.0 ) require ( @@ -68,7 +68,7 @@ require ( github.com/dchest/siphash v1.2.3 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/emicklei/dot v1.6.2 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/fatih/color v1.18.0 // indirect github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b // indirect @@ -171,8 +171,9 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b // indirect github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 // indirect github.com/smartcontractkit/chainlink-ton v0.0.0-20260213025045-83535910e2c0 // indirect github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 // indirect @@ -185,7 +186,7 @@ require ( github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -205,7 +206,7 @@ require ( go.mongodb.org/mongo-driver v1.17.2 // indirect 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/otel v1.39.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect @@ -216,24 +217,24 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/zap v1.27.1 // indirect golang.org/x/crypto v0.48.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect diff --git a/deployment/go.sum b/deployment/go.sum index 9a8c203400..933dc4629c 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -184,8 +184,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -213,12 +213,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= @@ -683,8 +683,8 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-8 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d/go.mod h1:bgmqE7x9xwmIVr8PqLbC0M5iPm4AV2DBl596lO6S5Sw= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 h1:Z4t2ZY+ZyGWxtcXvPr11y4o3CGqhg3frJB5jXkCSvWA= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 h1:eui+u6ge2RYW01F/DeXWrc5UOqc+8+lyPoi9TIAmMgo= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= 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-deployments-framework v0.80.2 h1:M944dbKDHJiqqGOfiaeQw9nUk/uuci8ggUXSgfSzW5Q= @@ -695,12 +695,14 @@ github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3544aab8f3/go.mod h1:3Lsp38qxen9PABVF+O5eocveQev+hyo9HLAgRodBD4Q= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 h1:JU1JUrkzdAUHsOYdS9DENPkJfmrxweFRPRSztad6oPM= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2/go.mod h1:+pRGfDej1r7cHMs1dYmuyPuOZzYB9Q+PKu0FvZOYlmw= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 h1:03tbcwjyIEjvHba1IWOj1sfThwebm2XNzyFHSuZtlWc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 h1:xHPmFDhff7QpeFxKsZfk+24j4AlnQiFjjRh5O87Peu4= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b h1:36knUpKHHAZ86K4FGWXtx8i/EQftGdk2bqCoEu/Cha8= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 h1:KyPROV+v7P8VdiU7JhVuGLcDlEBsURSpQmSCgNBTY+s= @@ -754,8 +756,8 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -816,8 +818,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -838,20 +840,20 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -900,8 +902,8 @@ golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZP golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= 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= @@ -1033,8 +1035,8 @@ 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.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1066,8 +1068,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1083,8 +1085,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/deployment/lanes/disable_lane.go b/deployment/lanes/disable_lane.go new file mode 100644 index 0000000000..0e40471422 --- /dev/null +++ b/deployment/lanes/disable_lane.go @@ -0,0 +1,130 @@ +package lanes + +import ( + "fmt" + + "github.com/Masterminds/semver/v3" + chain_selectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/changesets" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + mcms_types "github.com/smartcontractkit/mcms/types" +) + +// DisableRemoteChainInput provides the local and remote chain info +// needed to disable a lane on the local chain. +type DisableRemoteChainInput struct { + LocalChainSelector uint64 + RemoteChainSelector uint64 + OnRamp []byte + OffRamp []byte + Router []byte + FeeQuoter []byte +} + +// DisableLanePair identifies two chains whose bidirectional lane should be disabled. +type DisableLanePair struct { + ChainA uint64 + ChainB uint64 + Version *semver.Version +} + +// DisableLaneConfig is the input for the DisableLane changeset. +type DisableLaneConfig struct { + Lanes []DisableLanePair + MCMS mcms.Input +} + +// DisableLane returns a changeset that disables bidirectional CCIP lanes. +func DisableLane( + laneRegistry *LaneAdapterRegistry, + mcmsRegistry *changesets.MCMSReaderRegistry, +) cldf.ChangeSetV2[DisableLaneConfig] { + return cldf.CreateChangeSet( + makeDisableApply(laneRegistry, mcmsRegistry), + makeDisableVerify(), + ) +} + +func makeDisableVerify() func(cldf.Environment, DisableLaneConfig) error { + return func(_ cldf.Environment, _ DisableLaneConfig) error { + return nil + } +} + +func makeDisableApply( + laneRegistry *LaneAdapterRegistry, + mcmsRegistry *changesets.MCMSReaderRegistry, +) func(cldf.Environment, DisableLaneConfig) (cldf.ChangesetOutput, error) { + return func(e cldf.Environment, cfg DisableLaneConfig) (cldf.ChangesetOutput, error) { + batchOps := make([]mcms_types.BatchOperation, 0) + reports := make([]cldf_ops.Report[any, any], 0) + + for _, lane := range cfg.Lanes { + chainAFamily, err := chain_selectors.GetSelectorFamily(lane.ChainA) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get chain family for selector %d: %w", lane.ChainA, err) + } + chainBFamily, err := chain_selectors.GetSelectorFamily(lane.ChainB) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to get chain family for selector %d: %w", lane.ChainB, err) + } + + chainAAdapter, exists := laneRegistry.GetLaneAdapter(chainAFamily, lane.Version) + if !exists { + return cldf.ChangesetOutput{}, fmt.Errorf("no LaneAdapter registered for chain family '%s' version %s", chainAFamily, lane.Version) + } + chainBAdapter, exists := laneRegistry.GetLaneAdapter(chainBFamily, lane.Version) + if !exists { + return cldf.ChangesetOutput{}, fmt.Errorf("no LaneAdapter registered for chain family '%s' version %s", chainBFamily, lane.Version) + } + + chainADef := &ChainDefinition{Selector: lane.ChainA} + chainBDef := &ChainDefinition{Selector: lane.ChainB} + + err = populateAddresses(e.DataStore, chainADef, chainAAdapter, lane.Version) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("error fetching addresses for chain %d: %w", lane.ChainA, err) + } + err = populateAddresses(e.DataStore, chainBDef, chainBAdapter, lane.Version) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("error fetching addresses for chain %d: %w", lane.ChainB, err) + } + + type disablePair struct { + local *ChainDefinition + remote *ChainDefinition + adapter LaneAdapter + } + for _, pair := range []disablePair{ + {local: chainADef, remote: chainBDef, adapter: chainAAdapter}, + {local: chainBDef, remote: chainADef, adapter: chainBAdapter}, + } { + report, err := cldf_ops.ExecuteSequence( + e.OperationsBundle, + pair.adapter.DisableRemoteChain(), + e.BlockChains, + DisableRemoteChainInput{ + LocalChainSelector: pair.local.Selector, + RemoteChainSelector: pair.remote.Selector, + OnRamp: pair.local.OnRamp, + OffRamp: pair.local.OffRamp, + Router: pair.local.Router, + FeeQuoter: pair.local.FeeQuoter, + }, + ) + if err != nil { + return cldf.ChangesetOutput{}, fmt.Errorf("failed to disable remote chain %d on chain %d: %w", pair.remote.Selector, pair.local.Selector, err) + } + batchOps = append(batchOps, report.Output.BatchOps...) + reports = append(reports, report.ExecutionReports...) + } + } + + return changesets.NewOutputBuilder(e, mcmsRegistry). + WithReports(reports). + WithBatchOps(batchOps). + Build(cfg.MCMS) + } +} diff --git a/deployment/lanes/lane_update.go b/deployment/lanes/lane_update.go index a8d48ec6fd..f0a6825e0d 100644 --- a/deployment/lanes/lane_update.go +++ b/deployment/lanes/lane_update.go @@ -6,6 +6,7 @@ import ( "github.com/Masterminds/semver/v3" chain_selectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils" "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" diff --git a/deployment/lanes/product.go b/deployment/lanes/product.go index f2a51bcde2..5508a51d0f 100644 --- a/deployment/lanes/product.go +++ b/deployment/lanes/product.go @@ -16,6 +16,7 @@ type LaneAdapter interface { // high level API ConfigureLaneLegAsSource() *cldf_ops.Sequence[UpdateLanesInput, sequences.OnChainOutput, cldf_chain.BlockChains] ConfigureLaneLegAsDest() *cldf_ops.Sequence[UpdateLanesInput, sequences.OnChainOutput, cldf_chain.BlockChains] + DisableRemoteChain() *cldf_ops.Sequence[DisableRemoteChainInput, sequences.OnChainOutput, cldf_chain.BlockChains] // helpers to expose lower level functionality if needed // needed for populating values in chain specific configs diff --git a/deployment/testadapters/adapters.go b/deployment/testadapters/adapters.go index 0414448e68..9ea5e7b961 100644 --- a/deployment/testadapters/adapters.go +++ b/deployment/testadapters/adapters.go @@ -47,6 +47,9 @@ type MessageComponents struct { TokenAmounts []TokenAmount } +const ExtraArgGasLimit = "gasLimit|computeUnits" +const ExtraArgOOO = "outOfOrderExecutionEnabled" + // ExtraArgOpt is a generic representation of an extra arg that can be applied // to any kind of ccip message. // We use this to make it possible to specify extra args in a chain-agnostic way. @@ -57,14 +60,14 @@ type ExtraArgOpt struct { func NewOutOfOrderExtraArg(outOfOrder bool) ExtraArgOpt { return ExtraArgOpt{ - Name: "outOfOrderExecutionEnabled", + Name: ExtraArgOOO, Value: outOfOrder, } } func NewGasLimitExtraArg(gasLimit *big.Int) ExtraArgOpt { return ExtraArgOpt{ - Name: "gasLimit|computeUnits", + Name: ExtraArgGasLimit, Value: gasLimit, } } @@ -95,6 +98,10 @@ type TestAdapter interface { // // CCIPReceiver returns a CCIP receiver for the given chain family. CCIPReceiver() []byte + // SetReceiverRejectAll configures the receiver to reject all incoming messages. + // This is used for test cases with a a failing receiver. + SetReceiverRejectAll(ctx context.Context, rejectAll bool) error + // NativeFeeToken returns the native fee token for the given chain family. NativeFeeToken() string diff --git a/deployment/testhelpers/proposal.go b/deployment/testhelpers/proposal.go index 6fe69aea7c..ae9a00d329 100644 --- a/deployment/testhelpers/proposal.go +++ b/deployment/testhelpers/proposal.go @@ -31,7 +31,8 @@ import ( var ( // TestXXXMCMSSigner is a throwaway private key used for signing MCMS proposals. // in tests. - TestXXXMCMSSigner *ecdsa.PrivateKey + TestXXXMCMSSigner *ecdsa.PrivateKey + TestXXXMCMSSignerTwo *ecdsa.PrivateKey ) func init() { @@ -40,6 +41,13 @@ func init() { panic(err) } TestXXXMCMSSigner = key + + keyTwo, err := crypto.GenerateKey() + if err != nil { + panic(err) + } + TestXXXMCMSSignerTwo = keyTwo + } func SingleGroupMCMS() mcmstypes.Config { @@ -53,6 +61,19 @@ func SingleGroupMCMS() mcmstypes.Config { return c } +func SingleGroupMCMSTwoSigners() mcmstypes.Config { + publicKey := TestXXXMCMSSigner.Public().(*ecdsa.PublicKey) + publicKeyTwo := TestXXXMCMSSignerTwo.Public().(*ecdsa.PublicKey) + // Convert the public key to an Ethereum address + address := crypto.PubkeyToAddress(*publicKey) + addressTwo := crypto.PubkeyToAddress(*publicKeyTwo) + c, err := mcmstypes.NewConfig(1, []common.Address{address, addressTwo}, []mcmstypes.Config{}) + if err != nil { + panic(err) + } + return c +} + // SignMCMSTimelockProposal - Signs an MCMS timelock proposal. func SignMCMSTimelockProposal(t *testing.T, env cldf.Environment, proposal *mcmslib.TimelockProposal, realBackend bool) *mcmslib.Proposal { converters := make(map[mcmstypes.ChainSelector]mcmssdk.TimelockConverter) diff --git a/deployment/utils/common.go b/deployment/utils/common.go index 0fc6f6781d..c2d6a56ffc 100644 --- a/deployment/utils/common.go +++ b/deployment/utils/common.go @@ -31,6 +31,7 @@ const ( BurnWithFromMintTokenPool cldf.ContractType = "BurnWithFromMintTokenPool" BurnFromMintTokenPool cldf.ContractType = "BurnFromMintTokenPool" CCTPTokenPool cldf.ContractType = "CCTPTokenPool" + FeeQuoter cldf.ContractType = "FeeQuoter" // CLL Identifiers CLLQualifier = "CLLCCIP" RMNTimelockQualifier = "RMNMCMS" @@ -51,22 +52,29 @@ const ( SuiFamilySelector = "c4e05953" ) -func GetSelectorHex(selector uint64) []byte { +func GetSelectorHex(selector uint64) [4]byte { destFamily, _ := chain_selectors.GetSelectorFamily(selector) - var familySelector []byte + + var hexStr string switch destFamily { case chain_selectors.FamilyEVM: - familySelector, _ = hex.DecodeString(EVMFamilySelector) + hexStr = EVMFamilySelector case chain_selectors.FamilySolana: - familySelector, _ = hex.DecodeString(SVMFamilySelector) + hexStr = SVMFamilySelector case chain_selectors.FamilyAptos: - familySelector, _ = hex.DecodeString(AptosFamilySelector) + hexStr = AptosFamilySelector case chain_selectors.FamilyTon: - familySelector, _ = hex.DecodeString(TVMFamilySelector) + hexStr = TVMFamilySelector case chain_selectors.FamilySui: - familySelector, _ = hex.DecodeString(SuiFamilySelector) + hexStr = SuiFamilySelector + default: + panic(fmt.Sprintf("unsupported chain family: %s", destFamily)) } - return familySelector + + b, _ := hex.DecodeString(hexStr) + var out [4]byte + copy(out[:], b) + return out } var ( diff --git a/deployment/v1_7_0/changesets/configure_committee_verifiers_test.go b/deployment/v1_7_0/changesets/configure_committee_verifiers_test.go index 9989e7fc82..92c7a4794c 100644 --- a/deployment/v1_7_0/changesets/configure_committee_verifiers_test.go +++ b/deployment/v1_7_0/changesets/configure_committee_verifiers_test.go @@ -133,6 +133,10 @@ func (m *mockLaneAdapter) GetFQAddress(_ datastore.DataStore, _ uint64) ([]byte, return []byte("0xFeeQuoter"), nil } +func (m *mockLaneAdapter) DisableRemoteChain() *cldf_ops.Sequence[lanes.DisableRemoteChainInput, sequences.OnChainOutput, cldf_chain.BlockChains] { + return nil +} + func newCommitteeVerifierTestEnv(t *testing.T, selectors []uint64) deployment.Environment { t.Helper() lggr := logger.Test(t) diff --git a/devenv/cldf.go b/devenv/cldf.go index 2a1f8d0098..e4ff6bad76 100644 --- a/devenv/cldf.go +++ b/devenv/cldf.go @@ -4,7 +4,6 @@ import ( "context" // "encoding/hex" "errors" - "fmt" "os" "path/filepath" "sync" @@ -31,14 +30,14 @@ import ( // cldf_ton_provider "github.com/smartcontractkit/chainlink-deployments-framework/chain/ton/provider" // testutils "github.com/smartcontractkit/chainlink-ton/deployment/utils" - // ccipTon "github.com/smartcontractkit/chainlink-ton/devenv" - ccipEVM "github.com/smartcontractkit/chainlink-ccip/devenv/chainimpl/ccip-evm" ccipSolana "github.com/smartcontractkit/chainlink-ccip/devenv/chainimpl/ccip-solana" + ccipTon "github.com/smartcontractkit/chainlink-ton/devenv" // Register test adapters _ "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/testadapter" _ "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/testadapter" + _ "github.com/smartcontractkit/chainlink-ton/deployment/testadapter" ) type initOptions struct { @@ -244,37 +243,24 @@ func NewDefaultCLDFBundle(e *deployment.Environment) operations.Bundle { ) } -func NewCCIPImplFromNetwork(typ string, chainID string) (CCIP16ProductConfiguration, error) { - // TODO: extract to method - var family string - switch typ { - case "anvil", "geth": - family = chainsel.FamilyEVM - case "solana": - family = chainsel.FamilySolana - case "ton": - family = chainsel.FamilyTon - default: - return nil, fmt.Errorf("unsupported blockchain type: %s", typ) - } +func NewCCIPImplFromNetwork(family string, chainID string) (CCIP16ProductConfiguration, error) { networkInfo, err := chainsel.GetChainDetailsByChainIDAndFamily(chainID, family) if err != nil { return nil, err } - switch typ { - case "anvil", "geth": + switch family { + case chainsel.FamilyEVM: return ccipEVM.NewEmptyCCIP16EVM(networkInfo), nil - case "solana": + case chainsel.FamilySolana: return ccipSolana.NewEmptyCCIP16Solana(networkInfo), nil - case "sui": + case chainsel.FamilySui: panic("implement Sui") - case "aptos": + case chainsel.FamilyAptos: panic("implement Aptos") - case "ton": - panic("TON temporarily disabled") - // return ccipTon.NewEmptyCCIP16TON(networkInfo), nil + case chainsel.FamilyTon: + return ccipTon.NewEmptyCCIP16TON(networkInfo), nil default: - return nil, errors.New("unknown devenv network type " + typ) + return nil, errors.New("unsupported devenv chain family " + family) } } diff --git a/devenv/common/seq_home_chain.go b/devenv/common/seq_home_chain.go index 071812b34f..671e311df2 100644 --- a/devenv/common/seq_home_chain.go +++ b/devenv/common/seq_home_chain.go @@ -89,7 +89,7 @@ var AddDONAndSetCandidateSequence = operations.NewSequence( }, }) if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to execute AddDON for chain with selector %d and plugin type %s: %w", don.PluginConfig.ChainSelector, don.PluginConfig.PluginType, err) + return sequences.OnChainOutput{}, fmt.Errorf("failed to execute AddDON for chain with selector %d and plugin type %d: %w", don.PluginConfig.ChainSelector, don.PluginConfig.PluginType, err) } writes = append(writes, out.Output) } @@ -156,7 +156,7 @@ var SetCandidateSequence = operations.NewSequence( }, ) if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to execute UpdateDON for chain with selector %d and plugin type %s: %w", don.PluginConfig.ChainSelector, don.PluginConfig.PluginType, err) + return sequences.OnChainOutput{}, fmt.Errorf("failed to execute UpdateDON for chain with selector %d and plugin type %d: %w", don.PluginConfig.ChainSelector, don.PluginConfig.PluginType, err) } writes = append(writes, out.Output) } @@ -225,7 +225,7 @@ var PromoteCandidateSequence = operations.NewSequence( }, ) if err != nil { - return sequences.OnChainOutput{}, fmt.Errorf("failed to execute UpdateDONOp for chain with selector %d and plugin type %s: %w", don.ChainSelector, don.PluginType, err) + return sequences.OnChainOutput{}, fmt.Errorf("failed to execute UpdateDONOp for chain with selector %d and plugin type %d: %w", don.ChainSelector, don.PluginType, err) } writes = append(writes, out.Output) } diff --git a/devenv/environment.go b/devenv/environment.go index 93bea7a515..466426f324 100644 --- a/devenv/environment.go +++ b/devenv/environment.go @@ -154,7 +154,7 @@ func checkForkedEnvIsSet(in *Cfg) error { // NewEnvironment creates a new CCIP environment either locally in Docker or remotely in K8s. func NewEnvironment() (*Cfg, error) { - ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Minute) + ctx, cancelFunc := context.WithTimeout(context.Background(), 15*time.Minute) defer cancelFunc() tr := NewTimeTracker(Plog) ctx = L.WithContext(ctx) @@ -178,7 +178,18 @@ func NewEnvironment() (*Cfg, error) { impls := make([]CCIP16ProductConfiguration, 0) for _, bc := range in.Blockchains { - impl, err := NewCCIPImplFromNetwork(bc.Type, bc.ChainID) + var family string + switch bc.Type { + case "anvil", "geth": + family = chainsel.FamilyEVM + case "solana": + family = chainsel.FamilySolana + case "ton": + family = chainsel.FamilyTon + default: + return nil, fmt.Errorf("unsupported blockchain type: %s", bc.Type) + } + impl, err := NewCCIPImplFromNetwork(family, bc.ChainID) if err != nil { return nil, err } diff --git a/devenv/go.mod b/devenv/go.mod index 8176e60240..7f54151401 100644 --- a/devenv/go.mod +++ b/devenv/go.mod @@ -28,7 +28,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/docker/docker v28.5.1+incompatible github.com/docker/go-connections v0.6.0 // indirect - github.com/ethereum/go-ethereum v1.17.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/gagliardetto/solana-go v1.13.0 github.com/go-resty/resty/v2 v2.17.1 github.com/google/uuid v1.6.0 @@ -41,14 +41,16 @@ require ( github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment v0.0.0-00010101000000-000000000000 - github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20251014191100-bad58388f0c9 + github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260312233953-f588f8dc6d7c github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260312161144-d895b42081a0 - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 github.com/smartcontractkit/chainlink-deployments-framework v0.80.2 github.com/smartcontractkit/chainlink-evm v0.3.3 // indirect github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd - github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.0 + github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.1-0.20260212100725-fbd6b3bca4d1 github.com/smartcontractkit/chainlink-testing-framework/wasp v1.51.2 + github.com/smartcontractkit/chainlink-ton/deployment v0.0.0-20260312222135-d3b481b567a2 + github.com/smartcontractkit/chainlink-ton/devenv v0.0.0-20260312222135-d3b481b567a2 github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d github.com/smartcontractkit/mcms v0.36.0 github.com/spf13/cobra v1.10.1 @@ -74,6 +76,7 @@ require ( github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/TyphonHill/go-mermaid v1.0.0 // indirect github.com/VictoriaMetrics/fastcache v1.13.0 // indirect github.com/XSAM/otelsql v0.37.0 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect @@ -128,7 +131,7 @@ require ( github.com/creachadair/jrpc2 v1.2.0 // indirect github.com/creachadair/mds v0.13.4 // indirect github.com/dchest/siphash v1.2.3 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -139,7 +142,7 @@ require ( github.com/edsrzf/mmap-go v1.2.0 // indirect github.com/emicklei/dot v1.6.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect github.com/fatih/color v1.18.0 // indirect @@ -218,6 +221,7 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-sockaddr v1.0.7 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/memberlist v0.5.2 // indirect github.com/hashicorp/serf v0.10.1 // indirect github.com/hashicorp/yamux v0.1.2 // indirect @@ -338,9 +342,11 @@ require ( github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect + github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251210101658-1c5c8e4c4f15 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b // indirect github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 // indirect github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.2 // indirect @@ -356,7 +362,7 @@ require ( github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/testcontainers/testcontainers-go v0.39.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect @@ -392,7 +398,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect @@ -403,12 +409,12 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect @@ -416,19 +422,19 @@ require ( go.uber.org/ratelimit v0.3.1 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.21.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.32.0 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/api v0.221.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect diff --git a/devenv/go.sum b/devenv/go.sum index e98639250d..f71b823455 100644 --- a/devenv/go.sum +++ b/devenv/go.sum @@ -85,6 +85,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/TyphonHill/go-mermaid v1.0.0 h1:VtmgQwgZA+KNHJvG/O591ibBVuDkGhg2+F/olVXnXAs= +github.com/TyphonHill/go-mermaid v1.0.0/go.mod h1:BqMEbKnr2HHpZ4lJJvGjL47v6rZAUpJcOaE/db1Ppwc= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/Workiva/go-datastructures v1.1.5 h1:5YfhQ4ry7bZc2Mc7R0YZyYwpf5c6t1cEFvdAhd6Mkf4= @@ -330,8 +332,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -379,12 +381,12 @@ github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= -github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -708,6 +710,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -1020,8 +1024,8 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM= +github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -1201,8 +1205,8 @@ github.com/smartcontractkit/chain-selectors v1.0.97 h1:ECOin+SkJv2MUrfqTUu28J0ku github.com/smartcontractkit/chain-selectors v1.0.97/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 h1:vGdeMwHO3ow88HvxfhA4DDPYNY0X9jmdux7L83UF/W8= github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2/go.mod h1:iteU0WORHkArACVh/HoY/1bipV4TcNcJdTmom9uIT0E= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 h1:eui+u6ge2RYW01F/DeXWrc5UOqc+8+lyPoi9TIAmMgo= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= 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-deployments-framework v0.80.2 h1:M944dbKDHJiqqGOfiaeQw9nUk/uuci8ggUXSgfSzW5Q= @@ -1213,18 +1217,22 @@ github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd/go.mod h1:7Jlt72+V9891y3LnGwHzmQwt9tfEGYryRKiGlQHo/o8= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2 h1:JU1JUrkzdAUHsOYdS9DENPkJfmrxweFRPRSztad6oPM= github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250717121125-2350c82883e2/go.mod h1:+pRGfDej1r7cHMs1dYmuyPuOZzYB9Q+PKu0FvZOYlmw= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251210101658-1c5c8e4c4f15 h1:IXF7+k8I1YY/yvXC1wnS3FAAggtCy6ByEQ9hv/F2FvQ= +github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251210101658-1c5c8e4c4f15/go.mod h1:HG/aei0MgBOpsyRLexdKGtOUO8yjSJO3iUu0Uu8KBm4= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 h1:03tbcwjyIEjvHba1IWOj1sfThwebm2XNzyFHSuZtlWc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 h1:xHPmFDhff7QpeFxKsZfk+24j4AlnQiFjjRh5O87Peu4= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b h1:36knUpKHHAZ86K4FGWXtx8i/EQftGdk2bqCoEu/Cha8= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 h1:KyPROV+v7P8VdiU7JhVuGLcDlEBsURSpQmSCgNBTY+s= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9/go.mod h1:KpEWZJMLwbdMHeHQz9rbkES0vRrx4nk6OQXyhlHb9/8= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.0 h1:uaTiz6bA2gttm5AwCeqeT7ElrOkP9DIY1PPwSj5EeNs= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.0/go.mod h1:43xdIQuqw/gzfazsqJkBrGdF25TIJDiY/Ak/YrWFTmU= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.1-0.20260212100725-fbd6b3bca4d1 h1:w1KRBigXgoBYQBi4IU0gKbA2mBF6vq5vW/zbtan+mPo= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.1-0.20260212100725-fbd6b3bca4d1/go.mod h1:43xdIQuqw/gzfazsqJkBrGdF25TIJDiY/Ak/YrWFTmU= github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 h1:VIxK8u0Jd0Q/VuhmsNm6Bls6Tb31H/sA3A/rbc5hnhg= github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0/go.mod h1:lyAu+oMXdNUzEDScj2DXB2IueY+SDXPPfyl/kb63tMM= github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.2 h1:ZJ/8Jx6Be5//TyjPi1pS1uotnmcYq5vVkSyISIymSj8= @@ -1233,6 +1241,10 @@ github.com/smartcontractkit/chainlink-testing-framework/wasp v1.51.2 h1:QFO9Ar1z github.com/smartcontractkit/chainlink-testing-framework/wasp v1.51.2/go.mod h1:OLczwaAvyObFG+eq4tQHkWqkbPBB0cHkZj0JzY3inik= github.com/smartcontractkit/chainlink-ton v0.0.0-20260213025045-83535910e2c0 h1:+5wW8HbwNhHEBOanhSiT7sxyFAAeDP9TvQ6GwJ4STps= github.com/smartcontractkit/chainlink-ton v0.0.0-20260213025045-83535910e2c0/go.mod h1:IZvH2r16xcQvVLB7AtjU112wnHfEku+29OlI1vCQHCQ= +github.com/smartcontractkit/chainlink-ton/deployment v0.0.0-20260312222135-d3b481b567a2 h1:jnLgztBTgxBRZ13ggAl9z3XZkDsfWhpeh24bKvfzLQ4= +github.com/smartcontractkit/chainlink-ton/deployment v0.0.0-20260312222135-d3b481b567a2/go.mod h1:w83O9EU6BEtAJ1FifeVAqp1mQNISnH3BY+MLbYX5PBg= +github.com/smartcontractkit/chainlink-ton/devenv v0.0.0-20260312222135-d3b481b567a2 h1:l20zXumxZAgKE9Hye9Q3knb4fkvBUjB3J1x9YyT3Szk= +github.com/smartcontractkit/chainlink-ton/devenv v0.0.0-20260312222135-d3b481b567a2/go.mod h1:W4sihns3zi0UJF956bPdBI/DbP+fF9wQ5ON0RB0U2jY= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 h1:XRNxgcNqagXu6e4smJuS1crRK5cUAcCVd7u+iLduHDM= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513/go.mod h1:ccjEgNeqOO+bjPddnL4lUrNLzyCvGCxgBjJdhFX3wa8= github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20250528121202-292529af39df h1:36e3ROIZyV/qE8SvFOACXtXfMOMd9vG4+zY2v2ScXkI= @@ -1301,8 +1313,8 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -1434,8 +1446,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0. go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0/go.mod h1:54CaSNqYEXvpzDh8KPjiMVoWm60t5R0dZRt0leEPgAs= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -1456,20 +1468,20 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1544,8 +1556,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1777,8 +1789,8 @@ golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1840,8 +1852,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1902,8 +1914,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/devenv/tests/e2e/load_test.go b/devenv/tests/e2e/load_test.go index a705334546..b1faad375d 100644 --- a/devenv/tests/e2e/load_test.go +++ b/devenv/tests/e2e/load_test.go @@ -164,8 +164,12 @@ func TestE2ELoad(t *testing.T) { } impls := make([]ccip.CCIP16ProductConfiguration, 0) - for _, bc := range in.Blockchains { - i, err := ccip.NewCCIPImplFromNetwork(bc.Type, bc.ChainID) + for _, selector := range selectors { + family, err := chainsel.GetSelectorFamily(selector) + require.NoError(t, err) + chainID, err := chainsel.GetChainIDFromSelector(selector) + require.NoError(t, err) + i, err := ccip.NewCCIPImplFromNetwork(family, chainID) require.NoError(t, err) i.SetCLDF(e) impls = append(impls, i) diff --git a/devenv/tests/e2e/smoke_test.go b/devenv/tests/e2e/smoke_test.go index 77bb89cda4..7296dde4ef 100644 --- a/devenv/tests/e2e/smoke_test.go +++ b/devenv/tests/e2e/smoke_test.go @@ -2,22 +2,14 @@ package e2e import ( "fmt" - "math/big" - "os" "testing" - "time" "github.com/stretchr/testify/require" - datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore" - "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink-deployments-framework/datastore" "github.com/smartcontractkit/chainlink-testing-framework/framework" - chainsel "github.com/smartcontractkit/chain-selectors" - - "github.com/smartcontractkit/chainlink-ccip/deployment/testadapters" ccip "github.com/smartcontractkit/chainlink-ccip/devenv" + "github.com/smartcontractkit/chainlink-ccip/devenv/tests" ) func TestE2ESmoke(t *testing.T) { @@ -26,162 +18,14 @@ func TestE2ESmoke(t *testing.T) { if in.ForkedEnvConfig != nil { t.Skip("Skipping E2E tests on forked environments, not supported yet") } - chainIDs, wsURLs := make([]string, 0), make([]string, 0) - for _, bc := range in.Blockchains { - chainIDs = append(chainIDs, bc.ChainID) - wsURLs = append(wsURLs, bc.Out.Nodes[0].ExternalWSUrl) - } selectors, e, err := ccip.NewCLDFOperationsEnvironment(in.Blockchains, in.CLDF.DataStore) require.NoError(t, err) - selectorsToImpl := make(map[uint64]ccip.CCIP16ProductConfiguration) - - for _, bc := range in.Blockchains { - i, err := ccip.NewCCIPImplFromNetwork(bc.Type, bc.ChainID) - require.NoError(t, err) - i.SetCLDF(e) - family, err := chainsel.GetSelectorFamily(i.ChainSelector()) - require.NoError(t, err) - networkInfo, err := chainsel.GetChainDetailsByChainIDAndFamily(bc.ChainID, family) - require.NoError(t, err) - selectorsToImpl[networkInfo.ChainSelector] = i - } t.Cleanup(func() { _, err := framework.SaveContainerLogs(fmt.Sprintf("%s-%s", framework.DefaultCTFLogsDir, t.Name())) require.NoError(t, err) }) - type testcase struct { - name string - fromSelector uint64 - toSelector uint64 - } - tcs := []testcase{} - for i := range selectors { - for j := range selectors { - if i == j { - continue - } - fromFamily, _ := chainsel.GetSelectorFamily(selectors[i]) - toFamily, _ := chainsel.GetSelectorFamily(selectors[j]) - tcs = append(tcs, testcase{ - name: fmt.Sprintf("msg execution eoa receiver from %s to %s", fromFamily, toFamily), - fromSelector: selectors[i], - toSelector: selectors[j], - }) - } - } - - for _, tc := range tcs { - fromImpl := selectorsToImpl[tc.fromSelector] - toImpl := selectorsToImpl[tc.toSelector] - supportedTokenFamilies := map[string]bool{ - chainsel.FamilyEVM: true, - chainsel.FamilySolana: true, - } - _, fromSupported := supportedTokenFamilies[fromImpl.Family()] - _, toSupported := supportedTokenFamilies[toImpl.Family()] - if fromSupported && toSupported { - tc.name += " with token transfer" - } else { - tc.name += " without token transfer" - } - // Capture the loop variable so each goroutine gets its own copy. - t.Run(tc.name, func(t *testing.T) { - if os.Getenv("PARALLEL_E2E_TESTS") == "true" { - t.Parallel() - } - - t.Logf("Testing CCIP message from chain %d to chain %d", tc.fromSelector, tc.toSelector) - - receiver := toImpl.CCIPReceiver() - extraArgs, err := toImpl.GetExtraArgs(receiver, fromImpl.Family()) - require.NoError(t, err) - - // TODO: once non-EVM tooling supports token transfers, we'll be able - // to remove the EVM <-> EVM filter and directly set these variables. - var tokenAmounts []testadapters.TokenAmount = nil - var balanceCheck func() bool = nil - // technically something like solana <> solana isn't valid, but this - // check is just to ensure we only run token transfer tests on supported - // chain families for now. - if fromSupported && toSupported { - srcChainSel, srcTokenCfg := fromImpl.ChainSelector(), fromImpl.GetTokenExpansionConfig().DeployTokenInput - dstChainSel, dstTokenCfg := toImpl.ChainSelector(), toImpl.GetTokenExpansionConfig().DeployTokenInput - - srcTokenFilterDS := datastore.AddressRef{ChainSelector: srcChainSel, Qualifier: srcTokenCfg.Symbol, Type: datastore.ContractType(srcTokenCfg.Type)} - srcTokenRef, err := datastore_utils.FindAndFormatRef(e.DataStore, srcTokenFilterDS, srcChainSel, datastore_utils.FullRef) - require.NoError(t, err) - - dstTokenFilterDS := datastore.AddressRef{ChainSelector: dstChainSel, Qualifier: dstTokenCfg.Symbol, Type: datastore.ContractType(dstTokenCfg.Type)} - dstTokenRef, err := datastore_utils.FindAndFormatRef(e.DataStore, dstTokenFilterDS, dstChainSel, datastore_utils.FullRef) - require.NoError(t, err) - - // Here, we avoid using a fractional token amount to simplify the test logic. In - // this case, we transfer 10^src_decimals units on the *src* chain, which is the - // the equivalent of one whole token on the source. This results in the receiver - // getting the equivalent of 10^dst_decimals units which is also one whole token - // on the *destination* chain. If we want to test fractional amounts later, then - // we'd need to scale the amounts according to both the src/dst token decimals. - sendAmnt := new(big.Int).Exp(big.NewInt(10), new(big.Int).SetUint64(uint64(srcTokenCfg.Decimals)), nil) - - // We expect the receiver to get 1 whole token on the destination chain. - recvAmnt := new(big.Int).Exp(big.NewInt(10), new(big.Int).SetUint64(uint64(dstTokenCfg.Decimals)), nil) - - // Query the initial balance of the receiver account on the destination chain - initAmnt, err := toImpl.GetTokenBalance(t.Context(), dstTokenRef.Address, receiver) - require.NoError(t, err) - - // Calculate the total balance that the receiver should have after execution - trgtAmnt := new(big.Int).Add(initAmnt, recvAmnt) - - // This balance check function will be polled at regular intervals. It returns - // true when the receiver's current balance matches the expected target amount - balanceCheck = func() bool { - t.Helper() - - balance, err := toImpl.GetTokenBalance(t.Context(), dstTokenRef.Address, receiver) - require.NoError(t, err) - - t.Log(fmt.Sprintf("Fetched receiver token balance on chain %d (%s)", tc.toSelector, toImpl.Family()), - "token.qualifier="+dstTokenRef.Qualifier, - "token.address="+dstTokenRef.Address, - "token.type="+dstTokenRef.Type, - "balance.target="+trgtAmnt.String(), - "balance.actual="+balance.String(), - ) - - return balance.Cmp(trgtAmnt) == 0 - } - - tokenAmounts = []testadapters.TokenAmount{ - {Amount: sendAmnt, Token: srcTokenRef.Address}, - } - } - - msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ - DestChainSelector: tc.toSelector, - Receiver: receiver, - Data: []byte("hello eoa"), - FeeToken: "", - ExtraArgs: extraArgs, - TokenAmounts: tokenAmounts, - }) - require.NoError(t, err) - - seq, err := fromImpl.SendMessage(t.Context(), tc.toSelector, msg) - require.NoError(t, err) - seqNr := ccipocr3.SeqNum(seq) - seqNumRange := ccipocr3.NewSeqNumRange(seqNr, seqNr) - toImpl.ValidateCommit(t, tc.fromSelector, nil, seqNumRange) - toImpl.ValidateExec(t, tc.fromSelector, nil, []uint64{seq}) - - // TODO: once non-EVM tooling supports token transfers we can - // remove this if statement and always run the balance check. - if balanceCheck != nil { - require.Eventually(t, balanceCheck, 5*time.Second, time.Second) - } - }) - } + tests.RunSmokeTests(t, e, selectors) } diff --git a/devenv/tests/smoke.go b/devenv/tests/smoke.go new file mode 100644 index 0000000000..84be5aa895 --- /dev/null +++ b/devenv/tests/smoke.go @@ -0,0 +1,286 @@ +package tests + +import ( + "fmt" + "math" + "math/big" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore" + "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + + chainsel "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink-ccip/deployment/testadapters" + ccip "github.com/smartcontractkit/chainlink-ccip/devenv" +) + +var supportedTokenFamilies = map[string]bool{ + chainsel.FamilyEVM: true, + chainsel.FamilySolana: true, +} + +func RunSmokeTests(t *testing.T, e *deployment.Environment, selectors []uint64) { + selectorsToImpl := make(map[uint64]ccip.CCIP16ProductConfiguration) + for _, selector := range selectors { + family, err := chainsel.GetSelectorFamily(selector) + require.NoError(t, err) + chainID, err := chainsel.GetChainIDFromSelector(selector) + require.NoError(t, err) + i, err := ccip.NewCCIPImplFromNetwork(family, chainID) + require.NoError(t, err) + i.SetCLDF(e) + selectorsToImpl[selector] = i + } + + if os.Getenv("PARALLEL_E2E_TESTS") == "true" { + t.Parallel() + } + + type testpair struct { + fromChain ccip.CCIP16ProductConfiguration + toChain ccip.CCIP16ProductConfiguration + } + matrix := []testpair{} + for _, i := range selectors { + for _, j := range selectors { + if i == j { + continue + } + matrix = append(matrix, testpair{ + fromChain: selectorsToImpl[i], + toChain: selectorsToImpl[j], + }) + } + } + + for _, tc := range matrix { + fromImpl := tc.fromChain + toImpl := tc.toChain + laneTag := fmt.Sprintf("%s->%s", fromImpl.Family(), toImpl.Family()) + + t.Run(fmt.Sprintf("%s message", laneTag), func(t *testing.T) { + receiver := toImpl.CCIPReceiver() + extraArgs, err := toImpl.GetExtraArgs(receiver, fromImpl.Family()) + require.NoError(t, err) + + msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ + DestChainSelector: toImpl.ChainSelector(), + Receiver: receiver, + Data: []byte("hello eoa"), + FeeToken: "", + ExtraArgs: extraArgs, + TokenAmounts: nil, + }) + require.NoError(t, err) + + seq, err := fromImpl.SendMessage(t.Context(), toImpl.ChainSelector(), msg) + require.NoError(t, err) + seqNr := ccipocr3.SeqNum(seq) + seqNumRange := ccipocr3.NewSeqNumRange(seqNr, seqNr) + toImpl.ValidateCommit(t, fromImpl.ChainSelector(), nil, seqNumRange) + toImpl.ValidateExec(t, fromImpl.ChainSelector(), nil, []uint64{seq}) + }) + + t.Run(fmt.Sprintf("%s token transfer", laneTag), func(t *testing.T) { + receiver := toImpl.CCIPReceiver() + extraArgs, err := toImpl.GetExtraArgs(receiver, fromImpl.Family()) + require.NoError(t, err) + + _, fromSupported := supportedTokenFamilies[fromImpl.Family()] + _, toSupported := supportedTokenFamilies[toImpl.Family()] + if !fromSupported || !toSupported { + t.Skip("Token transfers not supported on " + laneTag) + } + + srcChainSel, srcTokenCfg := fromImpl.ChainSelector(), fromImpl.GetTokenExpansionConfig().DeployTokenInput + dstChainSel, dstTokenCfg := toImpl.ChainSelector(), toImpl.GetTokenExpansionConfig().DeployTokenInput + + srcTokenFilterDS := datastore.AddressRef{ChainSelector: srcChainSel, Qualifier: srcTokenCfg.Symbol, Type: datastore.ContractType(srcTokenCfg.Type)} + srcTokenRef, err := datastore_utils.FindAndFormatRef(e.DataStore, srcTokenFilterDS, srcChainSel, datastore_utils.FullRef) + require.NoError(t, err) + + dstTokenFilterDS := datastore.AddressRef{ChainSelector: dstChainSel, Qualifier: dstTokenCfg.Symbol, Type: datastore.ContractType(dstTokenCfg.Type)} + dstTokenRef, err := datastore_utils.FindAndFormatRef(e.DataStore, dstTokenFilterDS, dstChainSel, datastore_utils.FullRef) + require.NoError(t, err) + + // Here, we avoid using a fractional token amount to simplify the test logic. In + // this case, we transfer 10^src_decimals units on the *src* chain, which is the + // the equivalent of one whole token on the source. This results in the receiver + // getting the equivalent of 10^dst_decimals units which is also one whole token + // on the *destination* chain. If we want to test fractional amounts later, then + // we'd need to scale the amounts according to both the src/dst token decimals. + sendAmnt := new(big.Int).Exp(big.NewInt(10), new(big.Int).SetUint64(uint64(srcTokenCfg.Decimals)), nil) + + // We expect the receiver to get 1 whole token on the destination chain. + recvAmnt := new(big.Int).Exp(big.NewInt(10), new(big.Int).SetUint64(uint64(dstTokenCfg.Decimals)), nil) + + // Query the initial balance of the receiver account on the destination chain + initAmnt, err := toImpl.GetTokenBalance(t.Context(), dstTokenRef.Address, receiver) + require.NoError(t, err) + + // Calculate the total balance that the receiver should have after execution + trgtAmnt := new(big.Int).Add(initAmnt, recvAmnt) + + // This balance check function will be polled at regular intervals. It returns + // true when the receiver's current balance matches the expected target amount + balanceCheck := func() bool { + t.Helper() + + balance, err := toImpl.GetTokenBalance(t.Context(), dstTokenRef.Address, receiver) + require.NoError(t, err) + + t.Log(fmt.Sprintf("Fetched receiver token balance on chain %d (%s)", toImpl.ChainSelector(), toImpl.Family()), + "token.qualifier="+dstTokenRef.Qualifier, + "token.address="+dstTokenRef.Address, + "token.type="+dstTokenRef.Type, + "balance.target="+trgtAmnt.String(), + "balance.actual="+balance.String(), + ) + return balance.Cmp(trgtAmnt) == 0 + } + + msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ + DestChainSelector: toImpl.ChainSelector(), + Receiver: receiver, + Data: []byte("hello eoa"), + FeeToken: "", + ExtraArgs: extraArgs, + TokenAmounts: []testadapters.TokenAmount{ + {Amount: sendAmnt, Token: srcTokenRef.Address}, + }, + }) + require.NoError(t, err) + + seq, err := fromImpl.SendMessage(t.Context(), toImpl.ChainSelector(), msg) + require.NoError(t, err) + seqNr := ccipocr3.SeqNum(seq) + seqNumRange := ccipocr3.NewSeqNumRange(seqNr, seqNr) + toImpl.ValidateCommit(t, fromImpl.ChainSelector(), nil, seqNumRange) + toImpl.ValidateExec(t, fromImpl.ChainSelector(), nil, []uint64{seq}) + require.Eventually(t, balanceCheck, 5*time.Second, time.Second) + }) + + t.Run(fmt.Sprintf("%s gas limit too high", laneTag), func(t *testing.T) { + if fromImpl.Family() == chainsel.FamilySolana { + t.Skip("TODO: evm adapter GetExtraArgs returns nil adapter") + } + receiver := toImpl.CCIPReceiver() + + extraArgs, err := toImpl.GetExtraArgs(receiver, fromImpl.Family(), testadapters.NewGasLimitExtraArg(big.NewInt(math.MaxUint32))) + require.NoError(t, err) + + msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ + DestChainSelector: toImpl.ChainSelector(), + Receiver: receiver, + Data: []byte("hello world"), + ExtraArgs: extraArgs, + }) + require.NoError(t, err) + + _, err = fromImpl.SendMessage(t.Context(), toImpl.ChainSelector(), msg) + require.Error(t, err) + }) + + // TODO: send data payload larger than limit + + t.Run(fmt.Sprintf("%s invalid extra args tag", laneTag), func(t *testing.T) { + if fromImpl.Family() == chainsel.FamilyTon { + t.Skip("TON expects a well-formatted BOC or BuildMessage will fail") + } + + msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ + DestChainSelector: toImpl.ChainSelector(), + Receiver: toImpl.CCIPReceiver(), + Data: []byte("hello world"), + ExtraArgs: []byte{1, 2, 3, 4, 99, 99}, // invalid extraArgs prefix + }) + require.NoError(t, err) + + _, err = fromImpl.SendMessage(t.Context(), toImpl.ChainSelector(), msg) + require.Error(t, err) + }) + + t.Run(fmt.Sprintf("%s invalid/unconfigured chain selector", laneTag), func(t *testing.T) { + receiver := toImpl.CCIPReceiver() + extraArgs, err := toImpl.GetExtraArgs(receiver, fromImpl.Family()) + require.NoError(t, err) + + const invalidUnconfiguredChainSelector = 1 + msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ + DestChainSelector: invalidUnconfiguredChainSelector, + Receiver: toImpl.CCIPReceiver(), + Data: []byte("hello world"), + ExtraArgs: extraArgs, + }) + require.NoError(t, err) + + _, err = fromImpl.SendMessage(t.Context(), invalidUnconfiguredChainSelector, msg) + require.Error(t, err) + }) + + t.Run(fmt.Sprintf("%s invalid receiver", laneTag), func(t *testing.T) { + switch { + + case fromImpl.Family() == chainsel.FamilySolana: + t.Skip("GetExtraArgs fails with invalid pubkey receivers, we'd need to construct a raw payload to test against the contract") + case toImpl.Family() == chainsel.FamilySolana: + // TODO call skip in a getInvalidReceivers interface maybe + t.Skip("GetExtraArgs fails with invalid pubkey receivers, we'd need to construct a raw payload to test against the contract") + } + invalidReceiver := []byte{99} + + extraArgs, err := toImpl.GetExtraArgs(invalidReceiver, fromImpl.Family(), testadapters.NewGasLimitExtraArg(big.NewInt(math.MaxInt64))) + require.NoError(t, err) + + msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ + DestChainSelector: toImpl.ChainSelector(), + Receiver: invalidReceiver, + Data: []byte("hello world"), + ExtraArgs: extraArgs, + }) + require.NoError(t, err) + + _, err = fromImpl.SendMessage(t.Context(), toImpl.ChainSelector(), msg) + require.Error(t, err) + }) + + // TODO: message with not enough gas + // then manual re-exec with higher limit + + // TODO: test whitelisting + + t.Run(fmt.Sprintf("%s OOO flag is required on non-EVMs", laneTag), func(t *testing.T) { + if fromImpl.Family() == chainsel.FamilyEVM && toImpl.Family() == chainsel.FamilyEVM { + t.Skip("EVM->EVM still supports OOO, depending on config") + } + if (fromImpl.Family() == chainsel.FamilySolana && toImpl.Family() == chainsel.FamilyEVM) || + (fromImpl.Family() == chainsel.FamilyEVM && toImpl.Family() == chainsel.FamilySolana) { + t.Skip("TODO: Setup lane block OOO on Solana->EVM") + // 1. evm adapter returns nil adapter + // 2. solana setup lane seems not to be setting enforeceOOO on the contract side + } + + receiver := toImpl.CCIPReceiver() + extraArgs, err := toImpl.GetExtraArgs(receiver, fromImpl.Family(), testadapters.NewOutOfOrderExtraArg(false)) + require.NoError(t, err) + + msg, err := fromImpl.BuildMessage(testadapters.MessageComponents{ + DestChainSelector: toImpl.ChainSelector(), + Receiver: receiver, + Data: []byte("hello world"), + ExtraArgs: extraArgs, + }) + require.NoError(t, err) + + _, err = fromImpl.SendMessage(t.Context(), toImpl.ChainSelector(), msg) + require.Error(t, err) + }) + } +} diff --git a/execute/plugin_functions.go b/execute/plugin_functions.go index 5acf1caff5..950eb1811b 100644 --- a/execute/plugin_functions.go +++ b/execute/plugin_functions.go @@ -9,6 +9,7 @@ import ( "time" mapset "github.com/deckarep/golang-set/v2" + "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -540,7 +541,10 @@ func computeCommitObservationsConsensus( validRoots := make([]merkleRootData, 0, len(merkleRootsVotes)) for mr, votes := range merkleRootsVotes { - if consensus.GteFPlusOne(fChain[mr.SourceChain], votes) { + f, ok := fChain[mr.SourceChain] + if !ok { + lggr.Warnw("no fChain defined for chain", "chain", mr.SourceChain, "fChain", fChain) + } else if consensus.GteFPlusOne(f, votes) { validRoots = append(validRoots, mr) } else { lggr.Debugw("merkle root with less than f+1 votes was found, skipping it", "mr", mr, "votes", votes) diff --git a/execute/plugin_functions_test.go b/execute/plugin_functions_test.go index 27685db9f6..9dacf4ed20 100644 --- a/execute/plugin_functions_test.go +++ b/execute/plugin_functions_test.go @@ -1851,6 +1851,11 @@ func Test_computeCommitObservationsConsensus(t *testing.T) { }, } + baseObservationDupeRoot := map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: baseObservation[1], + 9999: baseObservation[1], + } + baseObservationDifferentOnRamp := map[cciptypes.ChainSelector][]exectypes.CommitData{ 1: { { @@ -1961,6 +1966,16 @@ func Test_computeCommitObservationsConsensus(t *testing.T) { }, fChain: map[cciptypes.ChainSelector]int{}, }, + { + name: "ignore observation from unknown source chain", + observations: []exectypes.CommitObservations{ + baseObservation, + baseObservation, + baseObservationDupeRoot, // <-- different data but same merkle root, should be ignored + }, + fChain: map[cciptypes.ChainSelector]int{1: 2}, // need 3 observations for src 1 + exp: baseObservation, + }, } for _, tc := range testCases { diff --git a/go.md b/go.md index 88ef0cd6dd..26518bbf84 100644 --- a/go.md +++ b/go.md @@ -9,11 +9,11 @@ flowchart LR chainlink-ccip --> chainlink-evm/gethwrappers/helpers chainlink-ccip --> chainlink-protos/rmn/v1.6/go click chainlink-ccip href "https://github.com/smartcontractkit/chainlink-ccip" - chainlink-common --> chain-selectors chainlink-common --> chainlink-common/pkg/chipingress chainlink-common --> chainlink-protos/billing/go chainlink-common --> chainlink-protos/cre/go chainlink-common --> chainlink-protos/linking-service/go + chainlink-common --> chainlink-protos/node-platform chainlink-common --> chainlink-protos/storage-service chainlink-common --> chainlink-protos/workflows/go chainlink-common --> freeport @@ -26,10 +26,12 @@ flowchart LR click chainlink-evm/gethwrappers/helpers href "https://github.com/smartcontractkit/chainlink-evm" chainlink-protos/billing/go click chainlink-protos/billing/go href "https://github.com/smartcontractkit/chainlink-protos" - chainlink-protos/cre/go + chainlink-protos/cre/go --> chain-selectors click chainlink-protos/cre/go href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/linking-service/go click chainlink-protos/linking-service/go href "https://github.com/smartcontractkit/chainlink-protos" + chainlink-protos/node-platform + click chainlink-protos/node-platform href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/rmn/v1.6/go click chainlink-protos/rmn/v1.6/go href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/storage-service @@ -53,6 +55,7 @@ flowchart LR chainlink-protos/billing/go chainlink-protos/cre/go chainlink-protos/linking-service/go + chainlink-protos/node-platform chainlink-protos/rmn/v1.6/go chainlink-protos/storage-service chainlink-protos/workflows/go @@ -101,15 +104,16 @@ flowchart LR chainlink-ccip/devenv --> chainlink-ccip/ccv/chains/evm chainlink-ccip/devenv --> chainlink-ccip/chains/evm/deployment chainlink-ccip/devenv --> chainlink-ccip/chains/solana/deployment + chainlink-ccip/devenv --> chainlink-ton/devenv click chainlink-ccip/devenv href "https://github.com/smartcontractkit/chainlink-ccip" chainlink-ccip/integration-tests --> chainlink-ccip/ccv/chains/evm/deployment chainlink-ccip/integration-tests --> chainlink-ccip/chains/solana/deployment click chainlink-ccip/integration-tests href "https://github.com/smartcontractkit/chainlink-ccip" - chainlink-common --> chain-selectors chainlink-common --> chainlink-common/pkg/chipingress chainlink-common --> chainlink-protos/billing/go chainlink-common --> chainlink-protos/cre/go chainlink-common --> chainlink-protos/linking-service/go + chainlink-common --> chainlink-protos/node-platform chainlink-common --> chainlink-protos/storage-service chainlink-common --> chainlink-protos/workflows/go chainlink-common --> freeport @@ -145,18 +149,20 @@ flowchart LR chainlink-framework/chains --> chainlink-common chainlink-framework/chains --> chainlink-framework/multinode click chainlink-framework/chains href "https://github.com/smartcontractkit/chainlink-framework" - chainlink-framework/metrics + chainlink-framework/metrics --> chainlink-common click chainlink-framework/metrics href "https://github.com/smartcontractkit/chainlink-framework" chainlink-framework/multinode click chainlink-framework/multinode href "https://github.com/smartcontractkit/chainlink-framework" chainlink-protos/billing/go click chainlink-protos/billing/go href "https://github.com/smartcontractkit/chainlink-protos" - chainlink-protos/cre/go + chainlink-protos/cre/go --> chain-selectors click chainlink-protos/cre/go href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/job-distributor click chainlink-protos/job-distributor href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/linking-service/go click chainlink-protos/linking-service/go href "https://github.com/smartcontractkit/chainlink-protos" + chainlink-protos/node-platform + click chainlink-protos/node-platform href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/op-catalog click chainlink-protos/op-catalog href "https://github.com/smartcontractkit/chainlink-protos" chainlink-protos/rmn/v1.6/go @@ -186,6 +192,10 @@ flowchart LR chainlink-ton --> chainlink-common/pkg/monitoring chainlink-ton --> chainlink-framework/metrics click chainlink-ton href "https://github.com/smartcontractkit/chainlink-ton" + chainlink-ton/deployment --> chainlink-ccip/deployment + click chainlink-ton/deployment href "https://github.com/smartcontractkit/chainlink-ton" + chainlink-ton/devenv --> chainlink-ton/deployment + click chainlink-ton/devenv href "https://github.com/smartcontractkit/chainlink-ton" chainlink-tron/relayer --> chainlink-common chainlink-tron/relayer --> chainlink-common/pkg/values click chainlink-tron/relayer href "https://github.com/smartcontractkit/chainlink-tron" @@ -244,6 +254,7 @@ flowchart LR chainlink-protos/cre/go chainlink-protos/job-distributor chainlink-protos/linking-service/go + chainlink-protos/node-platform chainlink-protos/op-catalog chainlink-protos/rmn/v1.6/go chainlink-protos/storage-service @@ -261,6 +272,13 @@ flowchart LR end click chainlink-testing-framework-repo href "https://github.com/smartcontractkit/chainlink-testing-framework" + subgraph chainlink-ton-repo[chainlink-ton] + chainlink-ton + chainlink-ton/deployment + chainlink-ton/devenv + end + click chainlink-ton-repo href "https://github.com/smartcontractkit/chainlink-ton" + classDef outline stroke-dasharray:6,fill:none; - class chainlink-ccip-repo,chainlink-common-repo,chainlink-evm-repo,chainlink-framework-repo,chainlink-protos-repo,chainlink-testing-framework-repo outline + class chainlink-ccip-repo,chainlink-common-repo,chainlink-evm-repo,chainlink-framework-repo,chainlink-protos-repo,chainlink-testing-framework-repo,chainlink-ton-repo outline ``` diff --git a/go.mod b/go.mod index 3df4dbfdab..4fd9257d03 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,24 @@ module github.com/smartcontractkit/chainlink-ccip go 1.25.3 require ( - github.com/deckarep/golang-set/v2 v2.6.0 - github.com/ethereum/go-ethereum v1.17.0 + github.com/deckarep/golang-set/v2 v2.8.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/prometheus/client_golang v1.22.0 + github.com/prometheus/client_golang v1.23.0 github.com/prometheus/client_model v0.6.2 github.com/smartcontractkit/chain-selectors v1.0.97 - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 github.com/smartcontractkit/chainlink-evm/gethwrappers/helpers v0.0.0-20260304234246-843d03f2badc github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d github.com/stretchr/testify v1.11.1 github.com/zksync-sdk/zksync2-go v1.1.0 - go.opentelemetry.io/otel v1.39.0 - go.opentelemetry.io/otel/metric v1.39.0 + go.opentelemetry.io/otel v1.41.0 + go.opentelemetry.io/otel/metric v1.41.0 go.uber.org/zap v1.27.1 golang.org/x/crypto v0.48.0 golang.org/x/sync v0.19.0 - golang.org/x/time v0.12.0 + golang.org/x/time v0.14.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 ) @@ -45,7 +45,7 @@ require ( github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 // indirect @@ -78,11 +78,11 @@ require ( github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect @@ -99,19 +99,19 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/grpc v1.78.0 // indirect ) diff --git a/go.sum b/go.sum index e2280e44ff..4de598663e 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -91,12 +91,12 @@ github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbz github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -263,8 +263,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= @@ -287,14 +287,14 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/smartcontractkit/chain-selectors v1.0.97 h1:ECOin+SkJv2MUrfqTUu28J0kub04Epds5NPMHERfGjo= github.com/smartcontractkit/chain-selectors v1.0.97/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 h1:eui+u6ge2RYW01F/DeXWrc5UOqc+8+lyPoi9TIAmMgo= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= 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-evm/gethwrappers/helpers v0.0.0-20260304234246-843d03f2badc h1:Xv7GCiWW8dVfgsvoB1O6ERD6dGpDNsE6ZgNL3b37Ex4= github.com/smartcontractkit/chainlink-evm/gethwrappers/helpers v0.0.0-20260304234246-843d03f2badc/go.mod h1:YyK80jgNa1/qrTsgZO2s4Cp0gnBa0+5JtcjEDg8gZyE= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 h1:03tbcwjyIEjvHba1IWOj1sfThwebm2XNzyFHSuZtlWc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 h1:L6KJ4kGv/yNNoCk8affk7Y1vAY0qglPMXC/hevV/IsA= @@ -310,8 +310,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -334,8 +334,8 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -356,20 +356,20 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -383,8 +383,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -416,21 +416,21 @@ 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= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E= google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/integration-tests/deployment/connect_chains_test.go b/integration-tests/deployment/connect_chains_test.go index ccaffcc230..1829f315e0 100644 --- a/integration-tests/deployment/connect_chains_test.go +++ b/integration-tests/deployment/connect_chains_test.go @@ -163,6 +163,52 @@ func checkBidirectionalLaneConnectivity( require.Equal(t, solanaChain.GasPrice, price.Value, "price must equal expected") } +func checkLaneDisabled( + t *testing.T, + e *fdeployment.Environment, + solanaChain lanesapi.ChainDefinition, + evmChain lanesapi.ChainDefinition, + solanaAdapter lanesapi.LaneAdapter, + evmAdapter lanesapi.LaneAdapter, +) { + t.Helper() + + // EVM Router: OnRamp must be zeroed for the remote chain + routerOnDestAddr, err := evmAdapter.GetRouterAddress(e.DataStore, evmChain.Selector) + require.NoError(t, err, "must get router from evmAdapter") + routerOnDest, err := router.NewRouter(common.BytesToAddress(routerOnDestAddr), e.BlockChains.EVMChains()[evmChain.Selector].Client) + require.NoError(t, err, "must instantiate router") + + onRampOnRouter, err := routerOnDest.GetOnRamp(nil, solanaChain.Selector) + require.NoError(t, err, "must get onRamp from router") + require.Equal(t, common.Address{}, onRampOnRouter, "onRamp must be zeroed after disable") + + // EVM Router: OffRamp must be removed for the remote chain + offRampDestAddr, err := evmAdapter.GetOffRampAddress(e.DataStore, evmChain.Selector) + require.NoError(t, err, "must get offRamp from evmAdapter") + isOffRamp, err := routerOnDest.IsOffRamp(nil, solanaChain.Selector, common.Address(offRampDestAddr)) + require.NoError(t, err, "must check if router has offRamp") + require.False(t, isOffRamp, "offRamp must be removed from router after disable") + + // Solana FeeQuoter: DestChain must be disabled for the remote chain + feeQuoterOnSrcAddr, err := solanaAdapter.GetFQAddress(e.DataStore, solanaChain.Selector) + require.NoError(t, err, "must get feeQuoter from solanaAdapter") + var destChainFqAccount fee_quoter.DestChain + fqEvmDestChainPDA, _, _ := state.FindFqDestChainPDA(evmChain.Selector, solana.PublicKeyFromBytes(feeQuoterOnSrcAddr)) + err = e.BlockChains.SolanaChains()[solanaChain.Selector].GetAccountDataBorshInto(e.GetContext(), fqEvmDestChainPDA, &destChainFqAccount) + require.NoError(t, err, "must get FeeQuoter dest chain account") + require.False(t, destChainFqAccount.Config.IsEnabled, "Solana FeeQuoter dest chain must be disabled") + + // Solana OffRamp: SourceChain must be disabled for the remote chain + offRampOnSrcAddr, err := solanaAdapter.GetOffRampAddress(e.DataStore, solanaChain.Selector) + require.NoError(t, err, "must get offRamp from solanaAdapter") + var offRampSourceChain ccip_offramp.SourceChain + offRampEvmSourceChainPDA, _, _ := state.FindOfframpSourceChainPDA(evmChain.Selector, solana.PublicKeyFromBytes(offRampOnSrcAddr)) + err = e.BlockChains.SolanaChains()[solanaChain.Selector].GetAccountDataBorshInto(e.GetContext(), offRampEvmSourceChainPDA, &offRampSourceChain) + require.NoError(t, err, "must get OffRamp source chain account") + require.False(t, offRampSourceChain.Config.IsEnabled, "Solana OffRamp source chain must be disabled") +} + func TestConnectChains_EVM2SVM_NoMCMS(t *testing.T) { t.Parallel() programsPath, ds, err := PreloadSolanaEnvironment(t, chain_selectors.SOLANA_MAINNET.Selector) @@ -262,3 +308,117 @@ func TestConnectChains_EVM2SVM_NoMCMS(t *testing.T) { // should add a new entry for remote source and remote dest in solana require.Equal(t, 2, len(connectOut.DataStore.Addresses().Filter())) } + +func TestDisableLane_EVM2SVM(t *testing.T) { + t.Parallel() + programsPath, ds, err := PreloadSolanaEnvironment(t, chain_selectors.SOLANA_MAINNET.Selector) + require.NoError(t, err, "Failed to set up Solana environment") + require.NotNil(t, ds, "Datastore should be created") + + evmChains := []uint64{ + chain_selectors.ETHEREUM_MAINNET.Selector, + } + solanaChains := []uint64{ + chain_selectors.SOLANA_MAINNET.Selector, + } + allChains := append(evmChains, solanaChains...) + e, err := environment.New(t.Context(), + environment.WithEVMSimulated(t, evmChains), + environment.WithSolanaContainer(t, solanaChains, programsPath, solanaProgramIDs), + ) + require.NoError(t, err, "Failed to create test environment") + require.NotNil(t, e, "Environment should be created") + e.DataStore = ds.Seal() + + mcmsRegistry := cs_core.GetRegistry() + dReg := deployops.GetRegistry() + version := semver.MustParse("1.6.0") + for _, chainSel := range allChains { + mint, _ := solana.NewRandomPrivateKey() + out, err := deployops.DeployContracts(dReg).Apply(*e, deployops.ContractDeploymentConfig{ + MCMS: mcms.Input{}, + Chains: map[uint64]deployops.ContractDeploymentConfigPerChain{ + chainSel: { + Version: version, + TokenPrivKey: mint.String(), + TokenDecimals: 9, + MaxFeeJuelsPerMsg: big.NewInt(0).Mul(big.NewInt(200), big.NewInt(1e18)), + TokenPriceStalenessThreshold: uint32(24 * 60 * 60), + LinkPremiumMultiplier: 9e17, + NativeTokenPremiumMultiplier: 1e18, + PermissionLessExecutionThresholdSeconds: uint32((20 * time.Minute).Seconds()), + GasForCallExactCheck: uint16(5000), + }, + }, + }) + require.NoError(t, err, "Failed to apply DeployChainContracts changeset") + out.DataStore.Merge(e.DataStore) + e.DataStore = out.DataStore.Seal() + } + DeployMCMS(t, e, chain_selectors.SOLANA_MAINNET.Selector, []string{cciputils.CLLQualifier}) + SolanaTransferOwnership(t, e, chain_selectors.SOLANA_MAINNET.Selector) + + chain1 := lanesapi.ChainDefinition{ + Selector: chain_selectors.SOLANA_MAINNET.Selector, + GasPrice: big.NewInt(1e17), + FeeQuoterDestChainConfig: lanesapi.DefaultFeeQuoterDestChainConfig(true, chain_selectors.SOLANA_MAINNET.Selector), + } + chain2 := lanesapi.ChainDefinition{ + Selector: chain_selectors.ETHEREUM_MAINNET.Selector, + GasPrice: big.NewInt(1e9), + FeeQuoterDestChainConfig: lanesapi.DefaultFeeQuoterDestChainConfig(true, chain_selectors.ETHEREUM_MAINNET.Selector), + } + + connectOut, err := lanesapi.ConnectChains(lanesapi.GetLaneAdapterRegistry(), mcmsRegistry).Apply(*e, lanesapi.ConnectChainsConfig{ + Lanes: []lanesapi.LaneConfig{ + { + Version: version, + ChainA: chain1, + ChainB: chain2, + }, + }, + MCMS: mcms.Input{ + OverridePreviousRoot: false, + ValidUntil: 3759765795, + TimelockDelay: mcms_types.MustParseDuration("1s"), + TimelockAction: mcms_types.TimelockActionSchedule, + Qualifier: cciputils.CLLQualifier, + Description: "Connect Chains", + }, + }) + require.NoError(t, err, "Failed to apply ConnectChains changeset") + testhelpers.ProcessTimelockProposals(t, *e, connectOut.MCMSTimelockProposals, false) + + laneRegistry := lanesapi.GetLaneAdapterRegistry() + srcFamily, err := chain_selectors.GetSelectorFamily(chain1.Selector) + require.NoError(t, err, "must get selector family for src") + srcAdapter, exists := laneRegistry.GetLaneAdapter(srcFamily, version) + require.True(t, exists, "must have ChainAdapter registered for src chain family") + destFamily, err := chain_selectors.GetSelectorFamily(chain2.Selector) + require.NoError(t, err, "must get selector family for dest") + destAdapter, exists := laneRegistry.GetLaneAdapter(destFamily, version) + require.True(t, exists, "must have ChainAdapter registered for dest chain family") + checkBidirectionalLaneConnectivity(t, e, chain1, chain2, srcAdapter, destAdapter, false, false) + + disableOut, err := lanesapi.DisableLane(lanesapi.GetLaneAdapterRegistry(), mcmsRegistry).Apply(*e, lanesapi.DisableLaneConfig{ + Lanes: []lanesapi.DisableLanePair{ + { + ChainA: chain_selectors.SOLANA_MAINNET.Selector, + ChainB: chain_selectors.ETHEREUM_MAINNET.Selector, + Version: version, + }, + }, + MCMS: mcms.Input{ + OverridePreviousRoot: true, + ValidUntil: 3759765795, + TimelockDelay: mcms_types.MustParseDuration("1s"), + TimelockAction: mcms_types.TimelockActionSchedule, + Qualifier: cciputils.CLLQualifier, + Description: "Disable Lane", + }, + }) + require.NoError(t, err, "Failed to apply DisableLane changeset") + testhelpers.ProcessTimelockProposals(t, *e, disableOut.MCMSTimelockProposals, false) + + checkLaneDisabled(t, e, chain1, chain2, srcAdapter, destAdapter) +} diff --git a/integration-tests/deployment/helpers.go b/integration-tests/deployment/helpers.go index b4cb009ebc..2dcc1e2227 100644 --- a/integration-tests/deployment/helpers.go +++ b/integration-tests/deployment/helpers.go @@ -15,9 +15,9 @@ import ( "github.com/stretchr/testify/require" evmrouterops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_2_0/operations/router" - evmfqops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/fee_quoter" evmofframpops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/offramp" evmonrampops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/onramp" + fq163ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_3/operations/fee_quoter" "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/utils" fqops "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/operations/fee_quoter" offrampops "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/operations/offramp" @@ -317,8 +317,8 @@ func EVMTransferOwnership(t *testing.T, e *cldf_deployment.Environment, selector ChainSelector: chain.Selector, ContractRef: []datastore.AddressRef{ { - Type: datastore.ContractType(evmfqops.ContractType), - Version: evmfqops.Version, + Type: datastore.ContractType(fq163ops.ContractType), + Version: fq163ops.Version, }, }, ProposedOwner: timelockAddrs[chain.Selector], @@ -364,7 +364,6 @@ func EVMTransferOwnership(t *testing.T, e *cldf_deployment.Environment, selector Description: "Transfer ownership test", }, } - transferOutput, err := mcmsapi.TransferOwnershipChangeset(mcmsapi.GetTransferOwnershipRegistry(), mcmsreaderapi.GetRegistry()).Apply(*e, mcmsInput) require.NoError(t, err) require.Greater(t, len(transferOutput.Reports), 0) diff --git a/integration-tests/deployment/mcms_test.go b/integration-tests/deployment/mcms_test.go new file mode 100644 index 0000000000..e06cd73dd7 --- /dev/null +++ b/integration-tests/deployment/mcms_test.go @@ -0,0 +1,127 @@ +package deployment + +import ( + "testing" + + "github.com/Masterminds/semver/v3" + "github.com/gagliardetto/solana-go" + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + "github.com/smartcontractkit/chainlink-deployments-framework/engine/test/environment" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/utils" + solseqV1_6_0 "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/sequences" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/v0_1_1/mcm" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" + deployops "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" + mcmsapi "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" + "github.com/smartcontractkit/chainlink-ccip/deployment/testhelpers" + + _ "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_0_0/adapters" + deploymentutils "github.com/smartcontractkit/chainlink-ccip/deployment/utils" + datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" + + mcms_solana "github.com/smartcontractkit/mcms/sdk/solana" + mcms_types "github.com/smartcontractkit/mcms/types" +) + +func TestUpdateMCMSConfigSolana(t *testing.T) { + programsPath, dstr, err := PreloadSolanaEnvironment(t, chainsel.SOLANA_MAINNET.Selector) + require.NoError(t, err, "Failed to set up Solana environment") + require.NotNil(t, dstr, "Datastore should be created") + solanaChains := []uint64{ + chainsel.SOLANA_MAINNET.Selector, + } + env, err := environment.New(t.Context(), + environment.WithSolanaContainer(t, solanaChains, programsPath, solanaProgramIDs), + ) + require.NoError(t, err) + require.NotNil(t, env, "Environment should be created") + env.DataStore = dstr.Seal() // Add preloaded contracts to env datastore + chain := env.BlockChains.SolanaChains()[chainsel.SOLANA_MAINNET.Selector] + dReg := mcmsapi.GetRegistry() + solAdapter := solseqV1_6_0.SolanaAdapter{} + dReg.RegisterDeployer(chainsel.FamilySolana, deployops.MCMSVersion, &solAdapter) + err = utils.FundSolanaAccounts( + t.Context(), + []solana.PublicKey{chain.DeployerKey.PublicKey()}, + 100, + chain.Client, + ) + require.NoError(t, err) + + // deploy MCMS Contracts + DeployMCMS(t, env, solanaChains[0], []string{deploymentutils.CLLQualifier}) + + // get recently deployed MCMS addresses + mcmsRefs := []datastore.AddressRef{} + cancellerRef, err := datastore_utils.FindAndFormatRef(env.DataStore, datastore.AddressRef{ + ChainSelector: solanaChains[0], + Type: datastore.ContractType(deploymentutils.CancellerManyChainMultisig), + Qualifier: deploymentutils.CLLQualifier, + Version: semver.MustParse("1.6.0"), + }, solanaChains[0], datastore_utils.FullRef) + require.NoError(t, err) + bypasserRef, err := datastore_utils.FindAndFormatRef(env.DataStore, datastore.AddressRef{ + ChainSelector: solanaChains[0], + Type: datastore.ContractType(deploymentutils.CancellerManyChainMultisig), + Qualifier: deploymentutils.CLLQualifier, + Version: semver.MustParse("1.6.0"), + }, solanaChains[0], datastore_utils.FullRef) + require.NoError(t, err) + proposerRef, err := datastore_utils.FindAndFormatRef(env.DataStore, datastore.AddressRef{ + ChainSelector: solanaChains[0], + Type: datastore.ContractType(deploymentutils.CancellerManyChainMultisig), + Qualifier: deploymentutils.CLLQualifier, + Version: semver.MustParse("1.6.0"), + }, solanaChains[0], datastore_utils.FullRef) + require.NoError(t, err) + mcmsRefs = append(mcmsRefs, cancellerRef, bypasserRef, proposerRef) + + // check that deployed config is correct + for _, ref := range mcmsRefs { + var mcmConfig mcm.MultisigConfig + id, seed, _ := mcms_solana.ParseContractAddress(ref.Address) + err := chain.GetAccountDataBorshInto(env.GetContext(), state.GetMCMConfigPDA(id, state.PDASeed([]byte(seed[:]))), &mcmConfig) + require.NoError(t, err) + + numOfSigners := len(mcmConfig.Signers) + require.Equal(t, numOfSigners, len(testhelpers.SingleGroupMCMS().Signers)) // should be 1 + } + + // update the config for each MCMS contract + updateMcmsConfigMCMS := mcmsapi.UpdateMCMSConfig(dReg, nil) + output, err := updateMcmsConfigMCMS.Apply(*env, mcmsapi.UpdateMCMSConfigInput{ + AdapterVersion: semver.MustParse("1.0.0"), + Chains: map[uint64]mcmsapi.UpdateMCMSConfigInputPerChain{ + solanaChains[0]: { + MCMConfig: testhelpers.SingleGroupMCMSTwoSigners(), + MCMContracts: mcmsRefs, + }, + }, + MCMS: mcms.Input{ + OverridePreviousRoot: false, + ValidUntil: 3759765795, + TimelockDelay: mcms_types.MustParseDuration("0s"), + TimelockAction: mcms_types.TimelockActionSchedule, + Qualifier: deploymentutils.CLLQualifier, + Description: "update mcms config test", + }, + }) + require.NoError(t, err) + require.Greater(t, len(output.Reports), 0) + + // check that MCMS configs are updated correctly + for _, ref := range mcmsRefs { + var mcmConfig mcm.MultisigConfig + id, seed, _ := mcms_solana.ParseContractAddress(ref.Address) + err := chain.GetAccountDataBorshInto(env.GetContext(), state.GetMCMConfigPDA(id, state.PDASeed([]byte(seed[:]))), &mcmConfig) + require.NoError(t, err) + + numOfSigners := len(mcmConfig.Signers) + require.Equal(t, numOfSigners, len(testhelpers.SingleGroupMCMSTwoSigners().Signers)) // should be 2 + } + +} diff --git a/integration-tests/deployment/update_to_FeeQuoter_1_7_test.go b/integration-tests/deployment/update_to_FeeQuoter_1_7_test.go index 5dc19302e6..ff4a69ee57 100644 --- a/integration-tests/deployment/update_to_FeeQuoter_1_7_test.go +++ b/integration-tests/deployment/update_to_FeeQuoter_1_7_test.go @@ -16,6 +16,7 @@ import ( fqops "github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm/deployment/v2_0_0/operations/fee_quoter" "github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm/gobindings/generated/latest/fee_quoter" fq16ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/fee_quoter" + fq163ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_3/operations/fee_quoter" onrampops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/onramp" fq16 "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_6_0/fee_quoter" "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_6_0/offramp" @@ -88,9 +89,8 @@ func TestUpdateToFeeQuoter_1_7(t *testing.T) { }, }) require.NoError(t, err, "Failed to apply ConnectChains changeset") - fqReg := deployops.GetFQAndRampUpdaterRegistry() // Now update to FeeQuoter 2.0.0 - fqUpdateChangeset := deployops.UpdateFeeQuoterChangeset(fqReg, nil) + fqUpdateChangeset := deployops.UpdateFeeQuoterChangeset() out, err = fqUpdateChangeset.Apply(*e, deployops.UpdateFeeQuoterInput{ Chains: fqInput, }) @@ -114,9 +114,9 @@ func TestUpdateToFeeQuoter_1_7(t *testing.T) { fq16AddrRefs := e.DataStore.Addresses().Filter( datastore.AddressRefByChainSelector(chainSel), datastore.AddressRefByType(datastore.ContractType(fq16ops.ContractType)), - datastore.AddressRefByVersion(fq16ops.Version), + datastore.AddressRefByVersion(fq163ops.Version), ) - require.Len(t, fq16AddrRefs, 1, "Expected exactly 1 FeeQuoter address ref for version 1.6.0 and chain selector %d", chainSel) + require.Len(t, fq16AddrRefs, 1, "Expected exactly 1 FeeQuoter address ref for version 1.6.3 and chain selector %d", chainSel) fq16Addr := common.HexToAddress(fq16AddrRefs[0].Address) // check that the new fee quoter has the same config as the old fee quoter fq16Contract, err := fq16.NewFeeQuoter(fq16Addr, chain.Client) diff --git a/integration-tests/deployment/update_to_FeeQuoter_2_0_test.go b/integration-tests/deployment/update_to_FeeQuoter_2_0_test.go new file mode 100644 index 0000000000..6a77435df8 --- /dev/null +++ b/integration-tests/deployment/update_to_FeeQuoter_2_0_test.go @@ -0,0 +1,248 @@ +package deployment + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/Masterminds/semver/v3" + "github.com/ethereum/go-ethereum/common" + chain_selectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + "github.com/smartcontractkit/chainlink-deployments-framework/engine/test/environment" + "github.com/smartcontractkit/chainlink-deployments-framework/operations" + mcms_types "github.com/smartcontractkit/mcms/types" + "github.com/stretchr/testify/require" + + fq16ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/fee_quoter" + onrampops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/operations/onramp" + fq163ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_3/operations/fee_quoter" + _ "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/adapters" + fqops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/operations/fee_quoter" + "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_6_0/offramp" + "github.com/smartcontractkit/chainlink-ccip/chains/evm/gobindings/generated/v1_6_0/onramp" + offrampops "github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment/v1_6_0/operations/offramp" + deployops "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" + lanesapi "github.com/smartcontractkit/chainlink-ccip/deployment/lanes" + "github.com/smartcontractkit/chainlink-ccip/deployment/testhelpers" + common_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils" + cs_core "github.com/smartcontractkit/chainlink-ccip/deployment/utils/changesets" + datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" +) + +func TestUpdateToFeeQuoter_2_0(t *testing.T) { + chains := []uint64{ + chain_selectors.ETHEREUM_MAINNET.Selector, + chain_selectors.AVALANCHE_MAINNET.Selector, + } + + e, err := environment.New(t.Context(), + environment.WithEVMSimulated(t, chains), + ) + require.NoError(t, err, "Failed to create test environment") + require.NotNil(t, e, "Environment should be created") + mcmsRegistry := cs_core.GetRegistry() + dReg := deployops.GetRegistry() + version := semver.MustParse("1.6.0") + chainInput := make(map[uint64]deployops.ContractDeploymentConfigPerChain) + fqInput := make(map[uint64]deployops.UpdateFeeQuoterInputPerChain) + + for _, chainSel := range chains { + chainInput[chainSel] = deployops.ContractDeploymentConfigPerChain{ + Version: version, + // FEE QUOTER CONFIG + MaxFeeJuelsPerMsg: big.NewInt(0).Mul(big.NewInt(200), big.NewInt(1e18)), + TokenPriceStalenessThreshold: uint32(24 * 60 * 60), + LinkPremiumMultiplier: 9e17, // 0.9 ETH + NativeTokenPremiumMultiplier: 1e18, // 1.0 ETH + // OFFRAMP CONFIG + PermissionLessExecutionThresholdSeconds: uint32((20 * time.Minute).Seconds()), + GasForCallExactCheck: uint16(5000), + } + fqInput[chainSel] = deployops.UpdateFeeQuoterInputPerChain{ + FeeQuoterVersion: semver.MustParse("2.0.0"), + RampsVersion: semver.MustParse("1.6.0"), + } + } + out, err := deployops.DeployContracts(dReg).Apply(*e, deployops.ContractDeploymentConfig{ + MCMS: mcms.Input{}, + Chains: chainInput, + }) + require.NoError(t, err, "Failed to apply DeployChainContracts changeset") + require.NoError(t, out.DataStore.Merge(e.DataStore)) + e.DataStore = out.DataStore.Seal() + chain1 := lanesapi.ChainDefinition{ + Selector: chain_selectors.ETHEREUM_MAINNET.Selector, + GasPrice: big.NewInt(1e9), + FeeQuoterDestChainConfig: lanesapi.DefaultFeeQuoterDestChainConfig(true, chain_selectors.ETHEREUM_MAINNET.Selector), + } + chain2 := lanesapi.ChainDefinition{ + Selector: chain_selectors.AVALANCHE_MAINNET.Selector, + GasPrice: big.NewInt(1e9), + FeeQuoterDestChainConfig: lanesapi.DefaultFeeQuoterDestChainConfig(true, chain_selectors.AVALANCHE_MAINNET.Selector), + } + _, err = lanesapi.ConnectChains(lanesapi.GetLaneAdapterRegistry(), mcmsRegistry).Apply(*e, lanesapi.ConnectChainsConfig{ + Lanes: []lanesapi.LaneConfig{ + { + Version: version, + ChainA: chain1, + ChainB: chain2, + }, + }, + }) + require.NoError(t, err, "Failed to apply ConnectChains changeset") + // Deploy MCMS + DeployMCMS(t, e, chain_selectors.ETHEREUM_MAINNET.Selector, []string{common_utils.CLLQualifier}) + DeployMCMS(t, e, chain_selectors.AVALANCHE_MAINNET.Selector, []string{common_utils.CLLQualifier}) + // now update to FeeQuoter 2.0.0 + fqUpdateChangeset := deployops.UpdateFeeQuoterChangeset() + out, err = fqUpdateChangeset.Apply(*e, deployops.UpdateFeeQuoterInput{ + Chains: fqInput, + MCMS: mcms.Input{ + OverridePreviousRoot: false, + ValidUntil: 3759765795, + TimelockDelay: mcms_types.MustParseDuration("0s"), + TimelockAction: mcms_types.TimelockActionSchedule, + Qualifier: common_utils.CLLQualifier, + Description: "Transfer ownership FQ2", + }, + }) + require.NoError(t, err, "Failed to apply UpdateFeeQuoterChangeset changeset") + require.Greater(t, len(out.Reports), 0) + require.Equal(t, 1, len(out.MCMSTimelockProposals)) + + testhelpers.ProcessTimelockProposals(t, *e, out.MCMSTimelockProposals, false) + // update datastore with changeset output + require.NoError(t, out.DataStore.Merge(e.DataStore), "Failed to merge changeset output datastore") + e.DataStore = out.DataStore.Seal() + for _, chainSel := range chains { + fqUpgradeValidation(t, e, chainSel, chains, true) + } + // do this to reset cached executions + bundle := operations.NewBundle( + func() context.Context { return context.Background() }, + e.Logger, + operations.NewMemoryReporter(), + ) + e.OperationsBundle = bundle + // downgrade back to 1.6.3 to make sure the changeset is reversible + for _, chainSel := range chains { + fqInput[chainSel] = deployops.UpdateFeeQuoterInputPerChain{ + FeeQuoterVersion: fq163ops.Version, + RampsVersion: semver.MustParse("1.6.0"), + } + } + // now downgrade back to FeeQuoter 1.6.3 + fqUpdateChangeset = deployops.UpdateFeeQuoterChangeset() + out, err = fqUpdateChangeset.Apply(*e, deployops.UpdateFeeQuoterInput{ + Chains: fqInput, + }) + require.NoError(t, err, "Failed to apply UpdateFeeQuoterChangeset changeset") + require.Len(t, out.DataStore.Addresses().Filter(), 0, "new addresses found on downgrade") + + for _, chainSel := range chains { + fqUpgradeValidation(t, e, chainSel, chains, false) + } +} + +func fqUpgradeValidation(t *testing.T, e *cldf.Environment, chainSel uint64, chains []uint64, expected17fq bool) { + chain := e.BlockChains.EVMChains()[chainSel] + + // get fee quoter address and check config + fq17AddrRefs := e.DataStore.Addresses().Filter( + datastore.AddressRefByChainSelector(chainSel), + datastore.AddressRefByType(datastore.ContractType(fqops.ContractType)), + datastore.AddressRefByVersion(fqops.Version), + ) + require.Len(t, fq17AddrRefs, 1, "Expected exactly 1 FeeQuoter address ref for chain selector %d", chainSel) + fq17Addr := common.HexToAddress(fq17AddrRefs[0].Address) + fq17Contract, err := fqops.NewFeeQuoterContract(fq17Addr, chain.Client) + require.NoError(t, err, "Failed to instantiate FeeQuoter 2.0 contract for chain selector %d", chainSel) + fq16AddrRefs := e.DataStore.Addresses().Filter( + datastore.AddressRefByChainSelector(chainSel), + datastore.AddressRefByType(datastore.ContractType(fq16ops.ContractType)), + datastore.AddressRefByVersion(fq163ops.Version), + ) + require.Len(t, fq16AddrRefs, 1, "Expected exactly 1 FeeQuoter address ref for version 1.6.0 and chain selector %d", chainSel) + fq16Addr := common.HexToAddress(fq16AddrRefs[0].Address) + // check that the new fee quoter has the same config as the old fee quoter + fq16Contract, err := fq163ops.NewFeeQuoterContract(fq16Addr, chain.Client) + require.NoError(t, err, "Failed to instantiate old FeeQuoter 1.6.0 contract for chain selector %d", chainSel) + staticConfig16, err := fq16Contract.GetStaticConfig(nil) + require.NoError(t, err, "Failed to get FeeQuoter config for old contract for chain selector %d", chainSel) + staticConfig17, err := fq17Contract.GetStaticConfig(nil) + require.NoError(t, err, "Failed to get FeeQuoter config for chain selector %d", chainSel) + require.Equal(t, staticConfig16.MaxFeeJuelsPerMsg, staticConfig17.MaxFeeJuelsPerMsg, "MaxFeeJuelsPerMsg should be the same after update for chain selector %d", chainSel) + require.Equal(t, staticConfig16.LinkToken, staticConfig17.LinkToken, "LinkToken address should be the same after update for chain selector %d", chainSel) + updaters16, err := fq16Contract.GetAllAuthorizedCallers(nil) + require.NoError(t, err, "Failed to get FeeQuoter dynamic config for old contract for chain selector %d", chainSel) + updaters17, err := fq17Contract.GetAllAuthorizedCallers(nil) + require.NoError(t, err, "Failed to get FeeQuoter dynamic config for chain selector %d", chainSel) + for _, caller := range updaters16 { + require.Contains(t, updaters17, caller, "FQ 2.0 should contain all authorized callers from FQ 1.6 for chain selector %d", chainSel) + } + + if expected17fq { + timelockRef := datastore_utils.GetAddressRef( + e.DataStore.Addresses().Filter(), + chainSel, + common_utils.RBACTimelock, + semver.MustParse("1.0.0"), + common_utils.CLLQualifier, + ) + require.NotEmpty(t, timelockRef.Address, "Expected timelock address for chain selector %d", chainSel) + timelockAddr := common.HexToAddress(timelockRef.Address) + require.Contains(t, updaters17, timelockAddr, "FQ 2.0 should have timelock as a price updater for chain selector %d", chainSel) + fq17Owner, err := fq17Contract.Owner(nil) + require.NoError(t, err, "Failed to get FeeQuoter 2.0 owner for chain selector %d", chainSel) + require.Equal(t, timelockAddr, fq17Owner, "FeeQuoter 2.0 should be owned by timelock after ownership transfer for chain selector %d", chainSel) + } + + var remoteChainSelector uint64 + for _, sel := range chains { + if sel != chainSel { + remoteChainSelector = sel + break + } + } + // check the destination chain config for the lane for few elements to make sure it was copied correctly + destChainConfig17, err := fq17Contract.GetDestChainConfig(nil, remoteChainSelector) + require.NoError(t, err, "Failed to get FeeQuoter dest chain config for chain selector %d", chainSel) + destChainConfig16, err := fq16Contract.GetDestChainConfig(nil, remoteChainSelector) + require.NoError(t, err, "Failed to get FeeQuoter dest chain config for old contract for chain selector %d", chainSel) + require.Equal(t, destChainConfig16.IsEnabled, destChainConfig17.IsEnabled, "IsEnabled in dest chain config should be the same after update for chain selector %d", chainSel) + require.Equal(t, destChainConfig16.DefaultTxGasLimit, destChainConfig17.DefaultTxGasLimit, "DefaultTxGasLimit in dest chain config should be the same after update for chain selector %d", chainSel) + + // get the onramp and offramp + onRampAddrRefs := e.DataStore.Addresses().Filter( + datastore.AddressRefByChainSelector(chainSel), + datastore.AddressRefByType(datastore.ContractType(onrampops.ContractType)), + datastore.AddressRefByVersion(onrampops.Version), + ) + require.Len(t, onRampAddrRefs, 1, "Expected exactly 1 OnRamp address ref for chain selector %d", chainSel) + onRampAddr := common.HexToAddress(onRampAddrRefs[0].Address) + offRampAddrRefs := e.DataStore.Addresses().Filter( + datastore.AddressRefByChainSelector(chainSel), + datastore.AddressRefByType(datastore.ContractType(offrampops.ContractType)), + datastore.AddressRefByVersion(offrampops.Version), + ) + require.Len(t, offRampAddrRefs, 1, "Expected exactly 1 OffRamp address ref for chain selector %d", chainSel) + offRampAddr := common.HexToAddress(offRampAddrRefs[0].Address) + onRampContract, err := onramp.NewOnRamp(onRampAddr, chain.Client) + require.NoError(t, err, "Failed to instantiate OnRamp contract for chain selector %d", chainSel) + offRampContract, err := offramp.NewOffRamp(offRampAddr, chain.Client) + require.NoError(t, err, "Failed to instantiate OffRamp contract for chain selector %d", chainSel) + onRampDCfg, err := onRampContract.GetDynamicConfig(nil) + require.NoError(t, err, "Failed to get OnRamp static config for chain selector %d", chainSel) + offRampDCfg, err := offRampContract.GetDynamicConfig(nil) + require.NoError(t, err, "Failed to get OffRamp static config for chain selector %d", chainSel) + expectedFQAddr := fq17Addr + if !expected17fq { + expectedFQAddr = fq16Addr + } + require.Equal(t, onRampDCfg.FeeQuoter, expectedFQAddr, "OnRamp should point to expected FeeQuoter after update for chain selector %d", chainSel) + require.Equal(t, offRampDCfg.FeeQuoter, expectedFQAddr, "OffRamp should point to expected FeeQuoter after update for chain selector %d", chainSel) +} diff --git a/integration-tests/go.mod b/integration-tests/go.mod index f755416018..893aeddeb8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,14 +24,14 @@ replace ( require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/aws/smithy-go v1.24.0 - github.com/ethereum/go-ethereum v1.17.0 + github.com/ethereum/go-ethereum v1.17.1 github.com/gagliardetto/solana-go v1.13.0 github.com/smartcontractkit/chain-selectors v1.0.97 github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260312182032-b2b38700f19b github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.1.1-solana.0.20251125153543-0b618525aa03 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d github.com/smartcontractkit/chainlink-ccip/chains/solana/deployment v0.0.0-00010101000000-000000000000 - github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260206132835-4c50734ccf2a + github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260312233953-f588f8dc6d7c github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260312182032-b2b38700f19b github.com/smartcontractkit/chainlink-deployments-framework v0.80.2 github.com/smartcontractkit/chainlink-evm v0.3.3 @@ -47,7 +47,7 @@ require ( require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 // indirect ) require ( @@ -101,7 +101,7 @@ require ( github.com/creachadair/mds v0.13.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dchest/siphash v1.2.3 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v28.5.1+incompatible // indirect @@ -110,7 +110,7 @@ require ( github.com/ebitengine/purego v0.9.0 // indirect github.com/emicklei/dot v1.6.2 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/fatih/color v1.18.0 // indirect github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b // indirect @@ -242,10 +242,11 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-contract-examples/chains/evm v0.0.0-20250826190403-aed7f5f33cde // indirect github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 // indirect - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 // indirect + github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect + github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b // indirect github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 // indirect github.com/smartcontractkit/chainlink-testing-framework/framework v0.14.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.2 // indirect @@ -261,7 +262,7 @@ require ( github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/supranational/blst v0.3.16 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/testcontainers/testcontainers-go v0.39.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect @@ -282,7 +283,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.63.0 // indirect - go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect @@ -293,29 +294,29 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/metric v1.39.0 // indirect - go.opentelemetry.io/otel/sdk v1.39.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect - go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/zap v1.27.1 // indirect golang.org/x/crypto v0.48.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index e34f82693d..02e5b490ee 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -255,8 +255,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= +github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -289,12 +289,12 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= -github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= +github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/ethereum/go-ethereum v1.17.1 h1:IjlQDjgxg2uL+GzPRkygGULPMLzcYWncEI7wbaizvho= +github.com/ethereum/go-ethereum v1.17.1/go.mod h1:7UWOVHL7K3b8RfVRea022btnzLCaanwHtBuH1jUCH/I= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -908,10 +908,10 @@ github.com/smartcontractkit/chain-selectors v1.0.97 h1:ECOin+SkJv2MUrfqTUu28J0ku github.com/smartcontractkit/chain-selectors v1.0.97/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 h1:vGdeMwHO3ow88HvxfhA4DDPYNY0X9jmdux7L83UF/W8= github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2/go.mod h1:iteU0WORHkArACVh/HoY/1bipV4TcNcJdTmom9uIT0E= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260206132835-4c50734ccf2a h1:jrP1CEWevxo5Ls7C/leXfO24LUZ53uqPMVvX+D7DDV0= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260206132835-4c50734ccf2a/go.mod h1:1WcontO9PeuKdUf5HXfs3nuICtzUvFNnyCmrHkTCF9Y= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260312233953-f588f8dc6d7c h1:zHOzhm2TaGQKzPSlooyBkyT18xEZDO/PuEFf61cGsuU= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260312233953-f588f8dc6d7c/go.mod h1:67YbnoglYD61Pz/jTVCgav9wFq7S35OU8UyQSvPllRw= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0 h1:eui+u6ge2RYW01F/DeXWrc5UOqc+8+lyPoi9TIAmMgo= +github.com/smartcontractkit/chainlink-common v0.10.1-0.20260310151336-c98a9c147ac0/go.mod h1:0ghbAr7tRO0tT5ZqBXhOyzgUO37tNNe33Yn0hskauVM= 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-deployments-framework v0.80.2 h1:M944dbKDHJiqqGOfiaeQw9nUk/uuci8ggUXSgfSzW5Q= @@ -920,12 +920,14 @@ github.com/smartcontractkit/chainlink-evm v0.3.3 h1:JqwyJEtnNEUaoQQPoOBTT4sn2lpd github.com/smartcontractkit/chainlink-evm v0.3.3/go.mod h1:q0ZBvaoisNaqC8NcMYWNPTjee88nQktDEeJMQHq3hVI= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd h1:sK+pK4epQp20yQ7XztwrVgkTkRAr4FY+TvEegW8RuQk= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd/go.mod h1:7Jlt72+V9891y3LnGwHzmQwt9tfEGYryRKiGlQHo/o8= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 h1:03tbcwjyIEjvHba1IWOj1sfThwebm2XNzyFHSuZtlWc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0 h1:xHPmFDhff7QpeFxKsZfk+24j4AlnQiFjjRh5O87Peu4= github.com/smartcontractkit/chainlink-protos/job-distributor v0.17.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b h1:36knUpKHHAZ86K4FGWXtx8i/EQftGdk2bqCoEu/Cha8= +github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260205130626-db2a2aab956b/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 h1:KyPROV+v7P8VdiU7JhVuGLcDlEBsURSpQmSCgNBTY+s= @@ -994,8 +996,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= -github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE= +github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= @@ -1071,8 +1073,8 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= @@ -1093,20 +1095,20 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1173,8 +1175,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1373,8 +1375,8 @@ golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1434,8 +1436,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1491,8 +1493,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/chainaccessor/config_processors.go b/pkg/chainaccessor/config_processors.go index f00953516f..dab6023e62 100644 --- a/pkg/chainaccessor/config_processors.go +++ b/pkg/chainaccessor/config_processors.go @@ -29,8 +29,6 @@ func processConfigResults( config.RMNProxy, err = processRMNProxyResults(results) case consts.ContractNameRMNRemote: config.RMNRemote, config.CurseInfo, err = processRMNRemoteResults(results, destChainSelector) - case consts.ContractNameFeeQuoter: - config.FeeQuoter, err = processFeeQuoterResults(results) case consts.ContractNameOnRamp: // Only process OnRamp results for source chains if resultsChainSelector != destChainSelector { @@ -286,25 +284,6 @@ func processRMNRemoteResults( return config, curseInfo, nil } -func processFeeQuoterResults(results []types.BatchReadResult) (cciptypes.FeeQuoterConfig, error) { - if len(results) != 1 { - return cciptypes.FeeQuoterConfig{}, fmt.Errorf("expected 1 fee quoter result, got %d", len(results)) - } - - val, err := results[0].GetResult() - if err != nil { - return cciptypes.FeeQuoterConfig{}, fmt.Errorf("get fee quoter result: %w", err) - } - - if typed, ok := val.(*cciptypes.FeeQuoterStaticConfig); ok { - return cciptypes.FeeQuoterConfig{ - StaticConfig: *typed, - }, nil - } - - return cciptypes.FeeQuoterConfig{}, fmt.Errorf("invalid type for fee quoter static config: %T", val) -} - func processRouterResults(results []types.BatchReadResult) (cciptypes.RouterConfig, error) { if len(results) != 1 { return cciptypes.RouterConfig{}, fmt.Errorf("expected 1 router result, got %d", len(results)) diff --git a/pkg/chainaccessor/default_accessor.go b/pkg/chainaccessor/default_accessor.go index ba7e62b34d..fb17c3b470 100644 --- a/pkg/chainaccessor/default_accessor.go +++ b/pkg/chainaccessor/default_accessor.go @@ -158,7 +158,6 @@ func prepareDestChainRequest( rmnRemoteAddress []byte rmnDigestHeader cciptypes.RMNDigestHeader rmnVersionConfig cciptypes.VersionedConfig - feeQuoterStaticConfig cciptypes.FeeQuoterStaticConfig cursedSubjects cciptypes.RMNCurseResponse ) @@ -212,11 +211,6 @@ func prepareDestChainRequest( ReturnVal: &cursedSubjects, }, }, - consts.ContractNameFeeQuoter: {{ - ReadName: consts.MethodNameFeeQuoterGetStaticConfig, - Params: map[string]any{}, - ReturnVal: &feeQuoterStaticConfig, - }}, } // Get source chain config requests and append them to requests diff --git a/pkg/ocrtypecodec/v1/commit.go b/pkg/ocrtypecodec/v1/commit.go index 2991c9a1eb..22c3bab8bc 100644 --- a/pkg/ocrtypecodec/v1/commit.go +++ b/pkg/ocrtypecodec/v1/commit.go @@ -72,10 +72,10 @@ func (c *CommitCodecProto) DecodeQuery(data []byte) (committypes.Query, error) { q := committypes.Query{ MerkleRootQuery: merkleroot.Query{ - RetryRMNSignatures: pbQ.MerkleRootQuery.RetryRmnSignatures, + RetryRMNSignatures: pbQ.GetMerkleRootQuery().GetRetryRmnSignatures(), RMNSignatures: &rmn.ReportSignatures{ - Signatures: c.tr.rmnSignaturesFromProto(pbQ.MerkleRootQuery.RmnSignatures.Signatures), - LaneUpdates: c.tr.laneUpdatesFromProto(pbQ.MerkleRootQuery.RmnSignatures.LaneUpdates), + Signatures: c.tr.rmnSignaturesFromProto(pbQ.GetMerkleRootQuery().GetRmnSignatures().GetSignatures()), + LaneUpdates: c.tr.laneUpdatesFromProto(pbQ.GetMerkleRootQuery().GetRmnSignatures().GetLaneUpdates()), }, }, TokenPriceQuery: tokenprice.Query{}, @@ -120,44 +120,52 @@ func (c *CommitCodecProto) EncodeObservation(observation committypes.Observation return proto.Marshal(pbObs) } -func (c *CommitCodecProto) DecodeObservation(data []byte) (committypes.Observation, error) { +func (c *CommitCodecProto) DecodeObservation(data []byte) (obs committypes.Observation, err error) { if len(data) == 0 { - return committypes.Observation{}, nil + return obs, nil } pbObs := &ocrtypecodecpb.CommitObservation{} if err := proto.Unmarshal(data, pbObs); err != nil { - return committypes.Observation{}, fmt.Errorf("proto unmarshal observation: %w", err) + return obs, fmt.Errorf("proto unmarshal observation: %w", err) } + merkleRoots, err := c.tr.merkleRootsFromProto(pbObs.GetMerkleRootObs().GetMerkleRoots()) + if err != nil { + return obs, fmt.Errorf("merkle roots from proto: %w", err) + } + rmnRemoteCfg, err := c.tr.rmnRemoteConfigFromProto(pbObs.GetMerkleRootObs().GetRmnRemoteConfig()) + if err != nil { + return obs, fmt.Errorf("rmn remote config from proto: %w", err) + } return committypes.Observation{ MerkleRootObs: merkleroot.Observation{ - MerkleRoots: c.tr.merkleRootsFromProto(pbObs.MerkleRootObs.MerkleRoots), - RMNEnabledChains: c.tr.rmnEnabledChainsFromProto(pbObs.MerkleRootObs.RmnEnabledChains), - OnRampMaxSeqNums: c.tr.seqNumChainFromProto(pbObs.MerkleRootObs.OnRampMaxSeqNums), - OffRampNextSeqNums: c.tr.seqNumChainFromProto(pbObs.MerkleRootObs.OffRampNextSeqNums), - RMNRemoteConfig: c.tr.rmnRemoteConfigFromProto(pbObs.MerkleRootObs.RmnRemoteConfig), - FChain: c.tr.fChainFromProto(pbObs.MerkleRootObs.FChain), + MerkleRoots: merkleRoots, + RMNEnabledChains: c.tr.rmnEnabledChainsFromProto(pbObs.GetMerkleRootObs().GetRmnEnabledChains()), + OnRampMaxSeqNums: c.tr.seqNumChainFromProto(pbObs.GetMerkleRootObs().GetOnRampMaxSeqNums()), + OffRampNextSeqNums: c.tr.seqNumChainFromProto(pbObs.GetMerkleRootObs().GetOffRampNextSeqNums()), + RMNRemoteConfig: rmnRemoteCfg, + FChain: c.tr.fChainFromProto(pbObs.GetMerkleRootObs().GetFChain()), }, TokenPriceObs: tokenprice.Observation{ - FeedTokenPrices: c.tr.feedTokenPricesFromProto(pbObs.TokenPriceObs.FeedTokenPrices), - FeeQuoterTokenUpdates: c.tr.feeQuoterTokenUpdatesFromProto(pbObs.TokenPriceObs.FeeQuoterTokenUpdates), - FChain: c.tr.fChainFromProto(pbObs.TokenPriceObs.FChain), - Timestamp: pbObs.TokenPriceObs.Timestamp.AsTime(), + FeedTokenPrices: c.tr.feedTokenPricesFromProto(pbObs.GetTokenPriceObs().GetFeedTokenPrices()), + FeeQuoterTokenUpdates: c.tr.feeQuoterTokenUpdatesFromProto(pbObs.GetTokenPriceObs().GetFeeQuoterTokenUpdates()), + FChain: c.tr.fChainFromProto(pbObs.GetTokenPriceObs().GetFChain()), + Timestamp: pbObs.GetTokenPriceObs().GetTimestamp().AsTime(), }, ChainFeeObs: chainfee.Observation{ - FeeComponents: c.tr.feeComponentsFromProto(pbObs.ChainFeeObs.FeeComponents), - NativeTokenPrices: c.tr.nativeTokenPricesFromProto(pbObs.ChainFeeObs.NativeTokenPrices), - ChainFeeUpdates: c.tr.chainFeeUpdatesFromProto(pbObs.ChainFeeObs.ChainFeeUpdates), - FChain: c.tr.fChainFromProto(pbObs.ChainFeeObs.FChain), - TimestampNow: pbObs.ChainFeeObs.TimestampNow.AsTime(), + FeeComponents: c.tr.feeComponentsFromProto(pbObs.GetChainFeeObs().GetFeeComponents()), + NativeTokenPrices: c.tr.nativeTokenPricesFromProto(pbObs.GetChainFeeObs().GetNativeTokenPrices()), + ChainFeeUpdates: c.tr.chainFeeUpdatesFromProto(pbObs.GetChainFeeObs().GetChainFeeUpdates()), + FChain: c.tr.fChainFromProto(pbObs.GetChainFeeObs().GetFChain()), + TimestampNow: pbObs.GetChainFeeObs().GetTimestampNow().AsTime(), }, DiscoveryObs: discoverytypes.Observation{ - FChain: c.tr.fChainFromProto(pbObs.DiscoveryObs.FChain), - Addresses: c.tr.discoveryAddressesFromProto(pbObs.DiscoveryObs.ContractNames.Addresses), + FChain: c.tr.fChainFromProto(pbObs.GetDiscoveryObs().GetFChain()), + Addresses: c.tr.discoveryAddressesFromProto(pbObs.GetDiscoveryObs().GetContractNames().GetAddresses()), }, - FChain: c.tr.fChainFromProto(pbObs.FChain), - OnChainPriceOcrSeqNum: pbObs.OnchainPriceOcrSeqNum, + FChain: c.tr.fChainFromProto(pbObs.GetFChain()), + OnChainPriceOcrSeqNum: pbObs.GetOnchainPriceOcrSeqNum(), }, nil } @@ -198,26 +206,42 @@ func (c *CommitCodecProto) DecodeOutcome(data []byte) (committypes.Outcome, erro return committypes.Outcome{}, fmt.Errorf("proto unmarshal outcome: %w", err) } + rootsToReport, err := c.tr.merkleRootsFromProto(pbOutcome.GetMerkleRootOutcome().GetRootsToReport()) + if err != nil { + return committypes.Outcome{}, fmt.Errorf("merkle roots from proto: %w", err) + } + sigs, err := c.tr.ccipRmnSignaturesFromProto(pbOutcome.GetMerkleRootOutcome().GetRmnReportSignatures()) + if err != nil { + return committypes.Outcome{}, fmt.Errorf("rmn report signatures from proto: %w", err) + } + rmnRemoteCfg, err := c.tr.rmnRemoteConfigFromProto(pbOutcome.GetMerkleRootOutcome().GetRmnRemoteCfg()) + if err != nil { + return committypes.Outcome{}, fmt.Errorf("rmn remote config from proto: %w", err) + } return committypes.Outcome{ MerkleRootOutcome: merkleroot.Outcome{ - OutcomeType: merkleroot.OutcomeType(pbOutcome.MerkleRootOutcome.OutcomeType), - RangesSelectedForReport: c.tr.chainRangeFromProto(pbOutcome.MerkleRootOutcome.RangesSelectedForReport), - RootsToReport: c.tr.merkleRootsFromProto(pbOutcome.MerkleRootOutcome.RootsToReport), - RMNEnabledChains: c.tr.rmnEnabledChainsFromProto(pbOutcome.MerkleRootOutcome.RmnEnabledChains), - OffRampNextSeqNums: c.tr.seqNumChainFromProto(pbOutcome.MerkleRootOutcome.OffRampNextSeqNums), - ReportTransmissionCheckAttempts: uint(pbOutcome.MerkleRootOutcome.ReportTransmissionCheckAttempts), - RMNReportSignatures: c.tr.ccipRmnSignaturesFromProto(pbOutcome.MerkleRootOutcome.RmnReportSignatures), - RMNRemoteCfg: c.tr.rmnRemoteConfigFromProto(pbOutcome.MerkleRootOutcome.RmnRemoteCfg), + OutcomeType: merkleroot.OutcomeType(pbOutcome.GetMerkleRootOutcome().GetOutcomeType()), + RangesSelectedForReport: c.tr.chainRangeFromProto( + pbOutcome.GetMerkleRootOutcome().GetRangesSelectedForReport(), + ), + RootsToReport: rootsToReport, + RMNEnabledChains: c.tr.rmnEnabledChainsFromProto( + pbOutcome.GetMerkleRootOutcome().GetRmnEnabledChains(), + ), + OffRampNextSeqNums: c.tr.seqNumChainFromProto(pbOutcome.GetMerkleRootOutcome().GetOffRampNextSeqNums()), + ReportTransmissionCheckAttempts: uint(pbOutcome.GetMerkleRootOutcome().GetReportTransmissionCheckAttempts()), + RMNReportSignatures: sigs, + RMNRemoteCfg: rmnRemoteCfg, }, TokenPriceOutcome: tokenprice.Outcome{ - TokenPrices: c.tr.feedTokenPricesFromProto(pbOutcome.TokenPriceOutcome.TokenPrices), + TokenPrices: c.tr.feedTokenPricesFromProto(pbOutcome.GetTokenPriceOutcome().GetTokenPrices()), }, ChainFeeOutcome: chainfee.Outcome{ - GasPrices: c.tr.gasPriceChainFromProto(pbOutcome.ChainFeeOutcome.GasPrices), + GasPrices: c.tr.gasPriceChainFromProto(pbOutcome.GetChainFeeOutcome().GetGasPrices()), }, MainOutcome: committypes.MainOutcome{ - InflightPriceOcrSequenceNumber: cciptypes.SeqNum(pbOutcome.MainOutcome.InflightPriceOcrSequenceNumber), - RemainingPriceChecks: int(pbOutcome.MainOutcome.RemainingPriceChecks), + InflightPriceOcrSequenceNumber: cciptypes.SeqNum(pbOutcome.GetMainOutcome().GetInflightPriceOcrSequenceNumber()), + RemainingPriceChecks: int(pbOutcome.GetMainOutcome().GetRemainingPriceChecks()), }, }, nil } diff --git a/pkg/ocrtypecodec/v1/compatability_test.go b/pkg/ocrtypecodec/v1/compatability_test.go index b2f888dfc9..4c971ac9cc 100644 --- a/pkg/ocrtypecodec/v1/compatability_test.go +++ b/pkg/ocrtypecodec/v1/compatability_test.go @@ -7,6 +7,7 @@ import ( "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/chainlink-ccip/commit/committypes" "github.com/smartcontractkit/chainlink-ccip/pkg/ocrtypecodec/v1/ocrtypecodecpb" "github.com/stretchr/testify/require" @@ -225,3 +226,32 @@ func TestEdgeCases(t *testing.T) { require.Nil(t, decoded.Report.ChainReports) }) } + +func TestDecodeObservationHandlesMalformedProto(t *testing.T) { + commitCodec := v1.NewCommitCodecProto() + t.Run("ShortMerkleRoot", func(t *testing.T) { + + pb := &ocrtypecodecpb.CommitObservation{ + MerkleRootObs: &ocrtypecodecpb.MerkleRootObservation{ + MerkleRoots: []*ocrtypecodecpb.MerkleRootChain{ + { + ChainSel: 1, + SeqNumsRange: &ocrtypecodecpb.SeqNumRange{ + MinMsgNr: 1, + MaxMsgNr: 10, + }, + MerkleRoot: []byte{0x01}, + }, + }, + }, + } + + data, err := proto.Marshal(pb) + require.NoError(t, err) + + res, err := commitCodec.DecodeObservation(data) + require.Error(t, err) + require.ErrorContains(t, err, "merkle root must be 32 bytes") + require.Equal(t, committypes.Observation{}, res) + }) +} diff --git a/pkg/ocrtypecodec/v1/exec.go b/pkg/ocrtypecodec/v1/exec.go index 93f5b5f6b5..58aac1c235 100644 --- a/pkg/ocrtypecodec/v1/exec.go +++ b/pkg/ocrtypecodec/v1/exec.go @@ -55,27 +55,40 @@ func (e *ExecCodecProto) EncodeObservation(observation exectypes.Observation) ([ return proto.Marshal(pbObs) } -func (e *ExecCodecProto) DecodeObservation(data []byte) (exectypes.Observation, error) { +func (e *ExecCodecProto) DecodeObservation(data []byte) (obs exectypes.Observation, err error) { if len(data) == 0 { - return exectypes.Observation{}, nil + return obs, nil } pbObs := &ocrtypecodecpb.ExecObservation{} - if err := proto.Unmarshal(data, pbObs); err != nil { - return exectypes.Observation{}, fmt.Errorf("proto unmarshal ExecObservation: %w", err) + if err = proto.Unmarshal(data, pbObs); err != nil { + return obs, fmt.Errorf("proto unmarshal ExecObservation: %w", err) + } + + commitReports, err := e.tr.commitReportsFromProto(pbObs.GetCommitReports()) + if err != nil { + return obs, fmt.Errorf("commit reports from proto: %w", err) + } + messages, err := e.tr.messageObservationsFromProto(pbObs.GetSeqNumsToMsgs()) + if err != nil { + return obs, fmt.Errorf("message observations from proto: %w", err) + } + hashes, err := e.tr.messageHashesFromProto(pbObs.GetMsgHashes()) + if err != nil { + return obs, fmt.Errorf("message hashes from proto: %w", err) } return exectypes.Observation{ - CommitReports: e.tr.commitReportsFromProto(pbObs.CommitReports), - Messages: e.tr.messageObservationsFromProto(pbObs.SeqNumsToMsgs), - Hashes: e.tr.messageHashesFromProto(pbObs.MsgHashes), - TokenData: e.tr.tokenDataObservationsFromProto(pbObs.TokenDataObservations.TokenData), - Nonces: e.tr.nonceObservationsFromProto(pbObs.Nonces), + CommitReports: commitReports, + Messages: messages, + Hashes: hashes, + TokenData: e.tr.tokenDataObservationsFromProto(pbObs.GetTokenDataObservations().GetTokenData()), + Nonces: e.tr.nonceObservationsFromProto(pbObs.GetNonces()), Contracts: discoverytypes.Observation{ - FChain: e.tr.fChainFromProto(pbObs.Contracts.FChain), - Addresses: e.tr.discoveryAddressesFromProto(pbObs.Contracts.ContractNames.Addresses), + FChain: e.tr.fChainFromProto(pbObs.GetContracts().GetFChain()), + Addresses: e.tr.discoveryAddressesFromProto(pbObs.GetContracts().GetContractNames().GetAddresses()), }, - FChain: e.tr.fChainFromProto(pbObs.FChain), + FChain: e.tr.fChainFromProto(pbObs.GetFChain()), }, nil } @@ -116,17 +129,30 @@ func (e *ExecCodecProto) DecodeOutcome(data []byte) (exectypes.Outcome, error) { return exectypes.Outcome{}, fmt.Errorf("proto unmarshal ExecOutcome: %w", err) } + commitReports, err := e.tr.commitDataSliceFromProto(pbOtcm.GetCommitReports()) + if err != nil { + return exectypes.Outcome{}, fmt.Errorf("commit reports from proto: %w", err) + } + reports, err := e.tr.execPluginReportsFromProto(pbOtcm.GetExecutePluginReports()) + if err != nil { + return exectypes.Outcome{}, fmt.Errorf("exec plugin reports from proto: %w", err) + } otcm := exectypes.Outcome{ - State: exectypes.PluginState(pbOtcm.PluginState), - CommitReports: e.tr.commitDataSliceFromProto(pbOtcm.CommitReports), - Reports: e.tr.execPluginReportsFromProto(pbOtcm.ExecutePluginReports), + State: exectypes.PluginState(pbOtcm.GetPluginState()), + CommitReports: commitReports, + Reports: reports, } // Decode the legacy Report field into the new Reports field. This way the plugin layer doesn't // need to worry about type migration. // TODO: Remove temporary migration code after a few releases. if pbOtcm.ExecutePluginReport != nil { - otcm.Reports = e.tr.execPluginReportsFromProto([]*ocrtypecodecpb.ExecutePluginReport{pbOtcm.ExecutePluginReport}) + reports, err := e.tr.execPluginReportsFromProto( + []*ocrtypecodecpb.ExecutePluginReport{pbOtcm.GetExecutePluginReport()}) + if err != nil { + return exectypes.Outcome{}, fmt.Errorf("exec plugin reports from proto: %w", err) + } + otcm.Reports = reports } // Decode the new report format into the legacy field as an intermediate step for implementing this feature. diff --git a/pkg/ocrtypecodec/v1/translate.go b/pkg/ocrtypecodec/v1/translate.go index 53412d6d4b..55d82e30be 100644 --- a/pkg/ocrtypecodec/v1/translate.go +++ b/pkg/ocrtypecodec/v1/translate.go @@ -1,6 +1,7 @@ package v1 import ( + "fmt" "maps" "math/big" @@ -65,19 +66,22 @@ func (t *protoTranslator) ccipRmnSignaturesToProto( func (t *protoTranslator) ccipRmnSignaturesFromProto( pbSigs []*ocrtypecodecpb.SignatureEcdsa, -) []cciptypes.RMNECDSASignature { +) ([]cciptypes.RMNECDSASignature, error) { var sigs []cciptypes.RMNECDSASignature if len(pbSigs) > 0 { sigs = make([]cciptypes.RMNECDSASignature, len(pbSigs)) } for i := range pbSigs { + if len(pbSigs[i].R) != 32 || len(pbSigs[i].S) != 32 { + return nil, fmt.Errorf("signature must be 32 bytes: %v", pbSigs[i]) + } sigs[i] = cciptypes.RMNECDSASignature{ R: cciptypes.Bytes32(pbSigs[i].R), S: cciptypes.Bytes32(pbSigs[i].S), } } - return sigs + return sigs, nil } func (t *protoTranslator) laneUpdatesToProto( @@ -145,13 +149,16 @@ func (t *protoTranslator) merkleRootsToProto( func (t *protoTranslator) merkleRootsFromProto( pbMerkleRoots []*ocrtypecodecpb.MerkleRootChain, -) []cciptypes.MerkleRootChain { +) ([]cciptypes.MerkleRootChain, error) { var merkleRoots []cciptypes.MerkleRootChain if len(pbMerkleRoots) > 0 { merkleRoots = make([]cciptypes.MerkleRootChain, len(pbMerkleRoots)) } for i, mr := range pbMerkleRoots { + if len(mr.MerkleRoot) != 32 { + return nil, fmt.Errorf("merkle root must be 32 bytes: %v", mr.MerkleRoot) + } merkleRoots[i] = cciptypes.MerkleRootChain{ ChainSel: cciptypes.ChainSelector(mr.ChainSel), OnRampAddress: mr.OnRampAddress, @@ -163,7 +170,7 @@ func (t *protoTranslator) merkleRootsFromProto( } } - return merkleRoots + return merkleRoots, nil } func (t *protoTranslator) rmnEnabledChainsToProto(rmnEnabled map[cciptypes.ChainSelector]bool) map[uint64]bool { @@ -241,7 +248,7 @@ func (t *protoTranslator) rmnRemoteConfigToProto(rmnRemoteCfg cciptypes.RemoteCo func (t *protoTranslator) rmnRemoteConfigFromProto( pbRmnRemoteCfg *ocrtypecodecpb.RmnRemoteConfig, -) cciptypes.RemoteConfig { +) (cciptypes.RemoteConfig, error) { var rmnSigners []cciptypes.RemoteSignerInfo if len(pbRmnRemoteCfg.Signers) > 0 { rmnSigners = make([]cciptypes.RemoteSignerInfo, len(pbRmnRemoteCfg.Signers)) @@ -252,6 +259,13 @@ func (t *protoTranslator) rmnRemoteConfigFromProto( NodeIndex: s.NodeIndex, } } + if len(pbRmnRemoteCfg.ConfigDigest) != 32 { + return cciptypes.RemoteConfig{}, fmt.Errorf("config digest must be 32 bytes: %v", pbRmnRemoteCfg.ConfigDigest) + } + if len(pbRmnRemoteCfg.RmnReportVersion) != 32 { + return cciptypes.RemoteConfig{}, fmt.Errorf( + "rmn report version must be 32 bytes: %v", pbRmnRemoteCfg.RmnReportVersion) + } return cciptypes.RemoteConfig{ ContractAddress: pbRmnRemoteCfg.ContractAddress, @@ -260,7 +274,7 @@ func (t *protoTranslator) rmnRemoteConfigFromProto( FSign: pbRmnRemoteCfg.FSign, ConfigVersion: pbRmnRemoteCfg.ConfigVersion, RmnReportVersion: cciptypes.Bytes32(pbRmnRemoteCfg.RmnReportVersion), - } + }, nil } func (t *protoTranslator) fChainToProto(fChain map[cciptypes.ChainSelector]int) map[uint64]int32 { @@ -569,23 +583,39 @@ func (t *protoTranslator) commitDataSliceToProto(commits []exectypes.CommitData) func (t *protoTranslator) commitReportsFromProto( pbObservations map[uint64]*ocrtypecodecpb.CommitObservations, -) exectypes.CommitObservations { +) (exectypes.CommitObservations, error) { var commitReports exectypes.CommitObservations if len(pbObservations) > 0 { commitReports = make(exectypes.CommitObservations, len(pbObservations)) } for chainSel, commitObs := range pbObservations { - commitReports[cciptypes.ChainSelector(chainSel)] = t.commitDataSliceFromProto(commitObs.CommitData) + commitDataSlice, err := t.commitDataSliceFromProto(commitObs.CommitData) + if err != nil { + return nil, err + } + commitReports[cciptypes.ChainSelector(chainSel)] = commitDataSlice } - return commitReports + return commitReports, nil } -func (t *protoTranslator) commitDataSliceFromProto(pbCommits []*ocrtypecodecpb.CommitData) []exectypes.CommitData { +func (t *protoTranslator) commitDataSliceFromProto( + pbCommits []*ocrtypecodecpb.CommitData) ([]exectypes.CommitData, error) { commitData := make([]exectypes.CommitData, len(pbCommits)) for i, commit := range pbCommits { + if len(commit.MerkleRoot) != 32 { + return nil, fmt.Errorf("merkle root length is not 32: %d", len(commit.MerkleRoot)) + } + hashes, err := t.bytes32SliceFromProto(commit.Hashes) + if err != nil { + return nil, err + } + messages, err := t.decodeMessages(commit.Messages) + if err != nil { + return nil, err + } commitData[i] = exectypes.CommitData{ SourceChain: cciptypes.ChainSelector(commit.SourceChain), OnRampAddress: commit.OnRampAddress, @@ -597,13 +627,13 @@ func (t *protoTranslator) commitDataSliceFromProto(pbCommits []*ocrtypecodecpb.C cciptypes.SeqNum(commit.SequenceNumberRange.MaxMsgNr), ), ExecutedMessages: t.decodeSeqNums(commit.ExecutedMessages), - Messages: t.decodeMessages(commit.Messages), - Hashes: t.bytes32SliceFromProto(commit.Hashes), + Messages: messages, + Hashes: hashes, MessageTokenData: t.decodeMessageTokenData(commit.MessageTokenData), } } - return commitData + return commitData, nil } func (t *protoTranslator) messagesToProto(messages []cciptypes.Message) []*ocrtypecodecpb.Message { @@ -685,17 +715,20 @@ func (t *protoTranslator) bytes32SliceToProto(slice []cciptypes.Bytes32) [][]byt return result } -func (t *protoTranslator) bytes32SliceFromProto(pbSlice [][]byte) []cciptypes.Bytes32 { +func (t *protoTranslator) bytes32SliceFromProto(pbSlice [][]byte) ([]cciptypes.Bytes32, error) { var result []cciptypes.Bytes32 if len(pbSlice) > 0 { result = make([]cciptypes.Bytes32, len(pbSlice)) } for i, val := range pbSlice { + if len(val) != 32 { + return nil, fmt.Errorf("bytes32 length is not 32: %d", len(val)) + } result[i] = cciptypes.Bytes32(val) } - return result + return result, nil } func (t *protoTranslator) messageTokenDataSliceToProto( @@ -749,7 +782,7 @@ func (t *protoTranslator) messageObservationsToProto( func (t *protoTranslator) messageObservationsFromProto( pbMsgs map[uint64]*ocrtypecodecpb.SeqNumToMessage, -) exectypes.MessageObservations { +) (exectypes.MessageObservations, error) { var messages exectypes.MessageObservations if len(pbMsgs) > 0 { messages = make(exectypes.MessageObservations, len(pbMsgs)) @@ -758,12 +791,16 @@ func (t *protoTranslator) messageObservationsFromProto( for chainSel, msgMap := range pbMsgs { innerMap := make(map[cciptypes.SeqNum]cciptypes.Message, len(msgMap.Messages)) for seqNum, msg := range msgMap.Messages { - innerMap[cciptypes.SeqNum(seqNum)] = t.decodeMessage(msg) + decodedMsg, err := t.decodeMessage(msg) + if err != nil { + return nil, err + } + innerMap[cciptypes.SeqNum(seqNum)] = decodedMsg } messages[cciptypes.ChainSelector(chainSel)] = innerMap } - return messages + return messages, nil } func (t *protoTranslator) messageHashesToProto( @@ -787,7 +824,7 @@ func (t *protoTranslator) messageHashesToProto( func (t *protoTranslator) messageHashesFromProto( pbHashes map[uint64]*ocrtypecodecpb.SeqNumToBytes, -) exectypes.MessageHashes { +) (exectypes.MessageHashes, error) { var hashes exectypes.MessageHashes if len(pbHashes) > 0 { hashes = make(exectypes.MessageHashes, len(pbHashes)) @@ -796,12 +833,15 @@ func (t *protoTranslator) messageHashesFromProto( for chainSel, hashMap := range pbHashes { innerMap := make(map[cciptypes.SeqNum]cciptypes.Bytes32, len(hashMap.SeqNumToBytes)) for seqNum, hash := range hashMap.SeqNumToBytes { + if len(hash) != 32 { + return nil, fmt.Errorf("bytes32 length is not 32: %d", len(hash)) + } innerMap[cciptypes.SeqNum(seqNum)] = cciptypes.Bytes32(hash) } hashes[cciptypes.ChainSelector(chainSel)] = innerMap } - return hashes + return hashes, nil } func (t *protoTranslator) tokenDataObservationsToProto( @@ -907,7 +947,7 @@ func (t *protoTranslator) execPluginReportsToProto( func (t *protoTranslator) execPluginReportsFromProto( pbReports []*ocrtypecodecpb.ExecutePluginReport, -) []cciptypes.ExecutePluginReport { +) ([]cciptypes.ExecutePluginReport, error) { reports := make([]cciptypes.ExecutePluginReport, len(pbReports)) for i, r := range pbReports { @@ -920,17 +960,26 @@ func (t *protoTranslator) execPluginReportsFromProto( offchainTokenData = append(offchainTokenData, data.Items) } + proofs, err := t.bytes32SliceFromProto(cr.Proofs) + if err != nil { + return nil, err + } + + messages, err := t.decodeMessages(cr.Messages) + if err != nil { + return nil, err + } reports[i].ChainReports[j] = cciptypes.ExecutePluginReportSingleChain{ SourceChainSelector: cciptypes.ChainSelector(cr.SourceChainSelector), - Messages: t.decodeMessages(cr.Messages), + Messages: messages, OffchainTokenData: offchainTokenData, - Proofs: t.bytes32SliceFromProto(cr.Proofs), + Proofs: proofs, ProofFlagBits: cciptypes.NewBigInt(big.NewInt(0).SetBytes(cr.ProofFlagBits)), } } } - return reports + return reports, nil } func (t *protoTranslator) decodeMessageTokenData(data []*ocrtypecodecpb.MessageTokenData) []exectypes.MessageTokenData { @@ -957,21 +1006,29 @@ func (t *protoTranslator) decodeSeqNums(seqNums []uint64) []cciptypes.SeqNum { return result } -func (t *protoTranslator) decodeMessages(messages []*ocrtypecodecpb.Message) []cciptypes.Message { +func (t *protoTranslator) decodeMessages(messages []*ocrtypecodecpb.Message) ([]cciptypes.Message, error) { var result []cciptypes.Message if len(messages) > 0 { result = make([]cciptypes.Message, len(messages)) } for i, msg := range messages { - result[i] = t.decodeMessage(msg) + decodedMsg, err := t.decodeMessage(msg) + if err != nil { + return nil, err + } + result[i] = decodedMsg } - return result + return result, nil } -func (t *protoTranslator) decodeMessage(msg *ocrtypecodecpb.Message) cciptypes.Message { +func (t *protoTranslator) decodeMessage(msg *ocrtypecodecpb.Message) (cciptypes.Message, error) { + header, err := t.decodeMessageHeader(msg.Header) + if err != nil { + return cciptypes.Message{}, err + } return cciptypes.Message{ - Header: t.decodeMessageHeader(msg.Header), + Header: header, Sender: msg.Sender, Data: msg.Data, Receiver: msg.Receiver, @@ -980,10 +1037,17 @@ func (t *protoTranslator) decodeMessage(msg *ocrtypecodecpb.Message) cciptypes.M FeeTokenAmount: cciptypes.NewBigInt(big.NewInt(0).SetBytes(msg.FeeTokenAmount)), FeeValueJuels: cciptypes.NewBigInt(big.NewInt(0).SetBytes(msg.FeeValueJuels)), TokenAmounts: t.decodeRampTokenAmounts(msg.TokenAmounts), - } + }, nil } -func (t *protoTranslator) decodeMessageHeader(header *ocrtypecodecpb.RampMessageHeader) cciptypes.RampMessageHeader { +func (t *protoTranslator) decodeMessageHeader( + header *ocrtypecodecpb.RampMessageHeader) (cciptypes.RampMessageHeader, error) { + if len(header.MessageId) != 32 { + return cciptypes.RampMessageHeader{}, fmt.Errorf("message id length is not 32: %d", len(header.MessageId)) + } + if len(header.MsgHash) != 32 { + return cciptypes.RampMessageHeader{}, fmt.Errorf("msg hash length is not 32: %d", len(header.MsgHash)) + } return cciptypes.RampMessageHeader{ MessageID: cciptypes.Bytes32(header.MessageId), SourceChainSelector: cciptypes.ChainSelector(header.SourceChainSelector), @@ -993,7 +1057,7 @@ func (t *protoTranslator) decodeMessageHeader(header *ocrtypecodecpb.RampMessage MsgHash: cciptypes.Bytes32(header.MsgHash), OnRamp: header.OnRamp, TxHash: header.TxHash, - } + }, nil } func (t *protoTranslator) decodeRampTokenAmounts( diff --git a/pkg/reader/ccip.go b/pkg/reader/ccip.go index 6bd102f509..30fe75a96e 100644 --- a/pkg/reader/ccip.go +++ b/pkg/reader/ccip.go @@ -831,21 +831,6 @@ func (r *ccipChainReader) GetContractAddress(contractName string, chain cciptype return r.donAddressBook.GetContractAddress(addressbook.ContractName(contractName), chain) } -// getDestFeeQuoterStaticConfig returns the destination chain's Fee Quoter's StaticConfig -func (r *ccipChainReader) getDestFeeQuoterStaticConfig(ctx context.Context) (cciptypes.FeeQuoterStaticConfig, error) { - // Get from cache - config, err := r.configPoller.GetChainConfig(ctx, r.destChain) - if err != nil { - return cciptypes.FeeQuoterStaticConfig{}, fmt.Errorf("get chain config: %w", err) - } - - if len(config.FeeQuoter.StaticConfig.LinkToken) == 0 { - return cciptypes.FeeQuoterStaticConfig{}, fmt.Errorf("link token address is empty") - } - - return config.FeeQuoter.StaticConfig, nil -} - // getFeeQuoterTokenPriceUSD gets the token price in USD of the given token address from the FeeQuoter contract on the // destination chain. func (r *ccipChainReader) getFeeQuoterTokenPriceUSD(ctx context.Context, tokenAddr []byte) (cciptypes.BigInt, error) { diff --git a/pkg/reader/ccip_test.go b/pkg/reader/ccip_test.go index 12e16a2e9d..eea9a8528e 100644 --- a/pkg/reader/ccip_test.go +++ b/pkg/reader/ccip_test.go @@ -592,47 +592,6 @@ func TestCCIPChainReader_DiscoverContracts_GetOfframpStaticConfig_Errors(t *test mockCache.AssertExpectations(t) } -func TestCCIPChainReader_getDestFeeQuoterStaticConfig(t *testing.T) { - ctx := context.Background() - - // Setup expected values - offrampAddress := []byte{0x3} - expectedConfig := cciptypes.FeeQuoterStaticConfig{ - MaxFeeJuelsPerMsg: cciptypes.NewBigIntFromInt64(10), - LinkToken: []byte{0x3, 0x4}, - StalenessThreshold: 12, - } - - // Setup cache with the expected config - mockCache := new(mockConfigCache) - chainConfig := cciptypes.ChainConfigSnapshot{ - FeeQuoter: cciptypes.FeeQuoterConfig{ - StaticConfig: expectedConfig, - }, - } - mockCache.On("GetChainConfig", mock.Anything, chainC).Return(chainConfig, nil) - - mockAddrCodec := internal.NewMockAddressCodecHex(t) - - offrampAddressStr, err := mockAddrCodec.AddressBytesToString(offrampAddress, chainC) - require.NoError(t, err) - ccipReader := &ccipChainReader{ - lggr: logger.Test(t), - destChain: chainC, - configPoller: mockCache, - offrampAddress: offrampAddressStr, - } - - cfg, err := ccipReader.getDestFeeQuoterStaticConfig(ctx) - require.NoError(t, err) - - assert.Equal(t, expectedConfig.MaxFeeJuelsPerMsg, cfg.MaxFeeJuelsPerMsg) - assert.Equal(t, expectedConfig.LinkToken, cfg.LinkToken) - assert.Equal(t, expectedConfig.StalenessThreshold, cfg.StalenessThreshold) - - mockCache.AssertExpectations(t) -} - func TestCCIPChainReader_getFeeQuoterTokenPriceUSD(t *testing.T) { tokenAddr := []byte{0x3, 0x4} offrampAddress := []byte{0x3} diff --git a/pkg/reader/config_poller_v2.go b/pkg/reader/config_poller_v2.go index 5b7ca87447..85cd296736 100644 --- a/pkg/reader/config_poller_v2.go +++ b/pkg/reader/config_poller_v2.go @@ -191,7 +191,8 @@ func (c *configPollerV2) startBackgroundPolling() { // - chainSel: The chain selector to retrieve configuration for // // Returns: -// - ChainConfigSnapshot containing the chain's configuration data +// - ChainConfigSnapshot containing the chain's configuration data. +// Deprecated: ChainConfigSnapshot.FeeQuoter is no longer populated; do not use it. // - Error if no chain accessor exists, cache creation fails, or batch refresh fails func (c *configPollerV2) GetChainConfig( ctx context.Context, diff --git a/pluginconfig/token.go b/pluginconfig/token.go index 22412a32f5..59c39d28c5 100644 --- a/pluginconfig/token.go +++ b/pluginconfig/token.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "strings" "time" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" @@ -281,9 +282,15 @@ func (p *USDCCCTPObserverConfig) Validate() error { if len(p.Tokens) == 0 { return errors.New("Tokens not set") } - for _, token := range p.Tokens { + for chainSelector, token := range p.Tokens { if err := token.Validate(); err != nil { - return err + return fmt.Errorf( + "invalid usdc token config for chain selector %d, source pool %q, and source message transmitter %q: %w", + chainSelector, + token.SourcePoolAddress, + token.SourceMessageTransmitterAddr, + err, + ) } } return nil @@ -298,12 +305,27 @@ type USDCCCTPTokenConfig struct { SourceMessageTransmitterAddr string `json:"sourceMessageTransmitterAddress"` } +// validAddress performs strict validation on hex based addresses, and allows unknown stuff for AltVM addresses. +func invalidAddress(addr string) bool { + if strings.HasPrefix(addr, "0x") { + return cciptypes.IsZeroOrEmptyOrInvalidHexAddress(addr) + } + // If it is not hex, just ensure that there is some non-zero content in the address. + addr = strings.Replace(addr, "0", "", -1) + addr = strings.Replace(addr, " ", "", -1) + return len(addr) == 0 +} + func (t USDCCCTPTokenConfig) Validate() error { - if t.SourcePoolAddress == "" { - return errors.New("SourcePoolAddress not set") + if invalidAddress(t.SourcePoolAddress) { + return fmt.Errorf("SourcePoolAddress not set (source pool %q)", t.SourcePoolAddress) } - if t.SourceMessageTransmitterAddr == "" { - return errors.New("SourceMessageTransmitterAddress not set") + if invalidAddress(t.SourceMessageTransmitterAddr) { + return fmt.Errorf( + "SourceMessageTransmitterAddress not set (source pool %q, source message transmitter %q)", + t.SourcePoolAddress, + t.SourceMessageTransmitterAddr, + ) } return nil } @@ -330,7 +352,7 @@ func (c *LBTCObserverConfig) Validate() error { return errors.New("SourcePoolAddressByChain is not set") } for _, sourcePoolAddress := range c.SourcePoolAddressByChain { - if sourcePoolAddress == "" { + if invalidAddress(sourcePoolAddress) { return errors.New("SourcePoolAddressByChain is empty") } } diff --git a/pluginconfig/token_test.go b/pluginconfig/token_test.go index d4cd2ac607..125d9263e0 100644 --- a/pluginconfig/token_test.go +++ b/pluginconfig/token_test.go @@ -543,8 +543,8 @@ func Test_TokenDataObserver_Validation(t *testing.T) { AttestationAPICooldown: commonconfig.MustNewDuration(5 * time.Minute), Tokens: map[cciptypes.ChainSelector]USDCCCTPTokenConfig{ 1: { - SourcePoolAddress: "0xabc", - SourceMessageTransmitterAddr: "0xefg", + SourcePoolAddress: "0xabcd", + SourceMessageTransmitterAddr: "0x1234", }, }, } @@ -564,7 +564,7 @@ func Test_TokenDataObserver_Validation(t *testing.T) { ObserveTimeout: commonconfig.MustNewDuration(5 * time.Second), }, SourcePoolAddressByChain: map[cciptypes.ChainSelector]string{ - 1: "0xabc", + 1: "0xabcd", }, } } @@ -626,6 +626,137 @@ func Test_TokenDataObserver_Validation(t *testing.T) { wantErr: true, errMsg: "Tokens not set", }, + { + name: "usdc type is set but token address is missing", + config: withBaseConfig( + TokenDataObserverConfig{ + Type: "usdc-cctp", + Version: "1.0", + USDCCCTPObserverConfig: &USDCCCTPObserverConfig{ + AttestationConfig: AttestationConfig{ + AttestationAPI: "http://localhost:8080", + AttestationAPITimeout: commonconfig.MustNewDuration(time.Second), + AttestationAPIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + AttestationAPICooldown: commonconfig.MustNewDuration(5 * time.Minute), + Tokens: map[cciptypes.ChainSelector]USDCCCTPTokenConfig{ + 1: { + SourcePoolAddress: "0x1111111111111111111111111111111111111111", + SourceMessageTransmitterAddr: "", + }, + }, + }, + }), + usdcEnabled: true, + lbtcEnabled: false, + wantErr: true, + errMsg: "SourceMessageTransmitterAddress not set", + }, + { + name: "usdc type is set but token address is zero", + config: withBaseConfig( + TokenDataObserverConfig{ + Type: "usdc-cctp", + Version: "1.0", + USDCCCTPObserverConfig: &USDCCCTPObserverConfig{ + AttestationConfig: AttestationConfig{ + AttestationAPI: "http://localhost:8080", + AttestationAPITimeout: commonconfig.MustNewDuration(time.Second), + AttestationAPIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + AttestationAPICooldown: commonconfig.MustNewDuration(5 * time.Minute), + Tokens: map[cciptypes.ChainSelector]USDCCCTPTokenConfig{ + 1: { + SourcePoolAddress: "0x1234", + SourceMessageTransmitterAddr: "0x0000", + }, + }, + }, + }), + usdcEnabled: true, + lbtcEnabled: false, + wantErr: true, + errMsg: "SourceMessageTransmitterAddress not set", + }, + { + name: "usdc type is set but token address is a weird zero", + config: withBaseConfig( + TokenDataObserverConfig{ + Type: "usdc-cctp", + Version: "1.0", + USDCCCTPObserverConfig: &USDCCCTPObserverConfig{ + AttestationConfig: AttestationConfig{ + AttestationAPI: "http://localhost:8080", + AttestationAPITimeout: commonconfig.MustNewDuration(time.Second), + AttestationAPIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + AttestationAPICooldown: commonconfig.MustNewDuration(5 * time.Minute), + Tokens: map[cciptypes.ChainSelector]USDCCCTPTokenConfig{ + 1: { + SourcePoolAddress: "0x1234", + SourceMessageTransmitterAddr: "0000", + }, + }, + }, + }), + usdcEnabled: true, + lbtcEnabled: false, + wantErr: true, + errMsg: "SourceMessageTransmitterAddress not set", + }, + { + name: "usdc type is set but token address is whitespace", + config: withBaseConfig( + TokenDataObserverConfig{ + Type: "usdc-cctp", + Version: "1.0", + USDCCCTPObserverConfig: &USDCCCTPObserverConfig{ + AttestationConfig: AttestationConfig{ + AttestationAPI: "http://localhost:8080", + AttestationAPITimeout: commonconfig.MustNewDuration(time.Second), + AttestationAPIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + AttestationAPICooldown: commonconfig.MustNewDuration(5 * time.Minute), + Tokens: map[cciptypes.ChainSelector]USDCCCTPTokenConfig{ + 1: { + SourcePoolAddress: "0x1234", + SourceMessageTransmitterAddr: " ", + }, + }, + }, + }), + usdcEnabled: true, + lbtcEnabled: false, + wantErr: true, + errMsg: "SourceMessageTransmitterAddress not set", + }, + { + name: "usdc token validation error includes chain selector and pool", + config: withBaseConfig( + TokenDataObserverConfig{ + Type: "usdc-cctp", + Version: "1.0", + USDCCCTPObserverConfig: &USDCCCTPObserverConfig{ + AttestationConfig: AttestationConfig{ + AttestationAPI: "http://localhost:8080", + AttestationAPITimeout: commonconfig.MustNewDuration(time.Second), + AttestationAPIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + AttestationAPICooldown: commonconfig.MustNewDuration(5 * time.Minute), + Tokens: map[cciptypes.ChainSelector]USDCCCTPTokenConfig{ + 1: { + SourcePoolAddress: "0x1111111111111111111111111111111111111111", + }, + }, + }, + }), + usdcEnabled: true, + lbtcEnabled: false, + wantErr: true, + errMsg: "invalid usdc token config for chain selector 1, source pool " + + "\"0x1111111111111111111111111111111111111111\", and source message " + + "transmitter \"\": SourceMessageTransmitterAddress not set", + }, { name: "lbtc type is set but tokens are missing", config: withBaseConfig( @@ -645,6 +776,28 @@ func Test_TokenDataObserver_Validation(t *testing.T) { wantErr: true, errMsg: "SourcePoolAddressByChain is not set", }, + { + name: "lbtc type is set but tokens set to zero address", + config: withBaseConfig( + TokenDataObserverConfig{ + Type: "lbtc", + Version: "1.0", + LBTCObserverConfig: &LBTCObserverConfig{ + AttestationConfig: AttestationConfig{ + AttestationAPI: "http://localhost:8080", + AttestationAPITimeout: commonconfig.MustNewDuration(time.Second), + AttestationAPIInterval: commonconfig.MustNewDuration(500 * time.Millisecond), + }, + SourcePoolAddressByChain: map[cciptypes.ChainSelector]string{ + 1: "0x0000", + }, + }, + }), + usdcEnabled: false, + lbtcEnabled: true, + wantErr: true, + errMsg: "SourcePoolAddressByChain is empty", + }, { name: "the same observer can't bet set twice", config: withBaseConfig(