Skip to content

Commit cfd1e49

Browse files
authored
[CCIP-7268-pt-1] Add ConfigureTokensForTransfers to product spec (#1242)
* [CCIP-7090] Move chain family agnostic utilities to deployment module * Update top level deploy pkg * Fix, + add test workflows * Checkout * Fix * Fix again * Workflow * Add checkout * OpCount -> GetChainMetadata * Fix tests * Use mcms.Input * Move contract writes back to EVM * [CCIP-7268-pt-1] Add ConfigureTokensForTransfers to product spec * Very WIP * Rm global registry * Test * Comments
1 parent face92e commit cfd1e49

File tree

9 files changed

+964
-29
lines changed

9 files changed

+964
-29
lines changed

deployment/go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ go 1.24.6
55
require (
66
github.com/Masterminds/semver/v3 v3.4.0
77
github.com/deckarep/golang-set/v2 v2.6.0
8-
github.com/ethereum/go-ethereum v1.16.2
98
github.com/smartcontractkit/chain-selectors v1.0.71
109
github.com/smartcontractkit/chainlink-common v0.9.5-0.20250912150129-4e42c90b532e
1110
github.com/smartcontractkit/chainlink-deployments-framework v0.44.0
@@ -15,8 +14,6 @@ require (
1514

1615
require (
1716
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
18-
github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect
19-
github.com/ethereum/c-kzg-4844/v2 v2.1.0 // indirect
2017
github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2 // indirect
2118
)
2219

@@ -47,9 +44,12 @@ require (
4744
github.com/consensys/gnark-crypto v0.18.0 // indirect
4845
github.com/cosmos/go-bip39 v1.0.0 // indirect
4946
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
47+
github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect
5048
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
5149
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
5250
github.com/docker/docker v28.3.2+incompatible // indirect
51+
github.com/ethereum/c-kzg-4844 v1.0.3 // indirect
52+
github.com/ethereum/go-ethereum v1.15.7 // indirect
5353
github.com/ethereum/go-verkle v0.2.2 // indirect
5454
github.com/fatih/color v1.18.0 // indirect
5555
github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b // indirect

deployment/go.sum

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,10 @@ github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GK
149149
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
150150
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
151151
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
152-
github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI=
153-
github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
154152
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
155153
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
154+
github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
155+
github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks=
156156
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
157157
github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI=
158158
github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0=
@@ -186,25 +186,23 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
186186
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
187187
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
188188
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
189-
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
190-
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
191189
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
192190
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
193191
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
194192
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
195-
github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w=
196-
github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E=
197-
github.com/ethereum/go-ethereum v1.16.2 h1:VDHqj86DaQiMpnMgc7l0rwZTg0FRmlz74yupSG5SnzI=
198-
github.com/ethereum/go-ethereum v1.16.2/go.mod h1:X5CIOyo8SuK1Q5GnaEizQVLHT/DfsiGWuNeVdQcEMNA=
193+
github.com/ethereum/c-kzg-4844 v1.0.3 h1:IEnbOHwjixW2cTvKRUlAAUOeleV7nNM/umJR+qy4WDs=
194+
github.com/ethereum/c-kzg-4844 v1.0.3/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
195+
github.com/ethereum/go-ethereum v1.15.7 h1:vm1XXruZVnqtODBgqFaTclzP0xAvCvQIDKyFNUA1JpY=
196+
github.com/ethereum/go-ethereum v1.15.7/go.mod h1:+S9k+jFzlyVTNcYGvqFhzN/SFhI6vA+aOY4T5tLSPL0=
199197
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
200198
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
201199
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
202200
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
203201
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
204202
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
205203
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
206-
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
207-
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
204+
github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk=
205+
github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs=
208206
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
209207
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
210208
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package tokens
2+
3+
import (
4+
"fmt"
5+
6+
chain_selectors "github.com/smartcontractkit/chain-selectors"
7+
"github.com/smartcontractkit/chainlink-ccip/deployment/utils/changesets"
8+
datastore_utils "github.com/smartcontractkit/chainlink-ccip/deployment/utils/datastore"
9+
"github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms"
10+
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
11+
"github.com/smartcontractkit/chainlink-deployments-framework/deployment"
12+
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
13+
cldf_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations"
14+
mcms_types "github.com/smartcontractkit/mcms/types"
15+
)
16+
17+
// TokenTransferConfig specifies configuration for a token on one chain to enable transfers with other chains.
18+
type TokenTransferConfig struct {
19+
// ChainSelector identifies the chain on which the token lives.
20+
ChainSelector uint64
21+
// TokenPoolRef is a reference to the token pool in the datastore.
22+
// Populate the reference as needed to match the desired token pool.
23+
TokenPoolRef datastore.AddressRef
24+
// ExternalAdmin is specified when we want to propose an admin that we don't control.
25+
// Leave empty to use internal administration.
26+
ExternalAdmin string
27+
// RegistryRef is a reference to the contract on which the token pool must be registered.
28+
// Populate the reference as needed to match the desired registry.
29+
RegistryRef datastore.AddressRef
30+
// RemoteChains specifies the remote chains to configure on the token pool.
31+
RemoteChains map[uint64]RemoteChainConfig[*datastore.AddressRef, datastore.AddressRef]
32+
}
33+
34+
// ConfigureTokensForTransfersConfig is the configuration for the ConfigureTokensForTransfers changeset.
35+
type ConfigureTokensForTransfersConfig struct {
36+
// Tokens specifies the tokens to configure for cross-chain transfers.
37+
Tokens []TokenTransferConfig
38+
// MCMS configures the resulting proposal.
39+
MCMS mcms.Input
40+
}
41+
42+
// ConfigureTokensForTransfers returns a changeset that configures tokens on multiple chains for transfers with other chains.
43+
func ConfigureTokensForTransfers(tokenRegistry *TokenAdapterRegistry, mcmsRegistry *changesets.MCMSReaderRegistry) cldf.ChangeSetV2[ConfigureTokensForTransfersConfig] {
44+
return cldf.CreateChangeSet(makeApply(tokenRegistry, mcmsRegistry), makeVerify(tokenRegistry, mcmsRegistry))
45+
}
46+
47+
func makeVerify(_ *TokenAdapterRegistry, _ *changesets.MCMSReaderRegistry) func(cldf.Environment, ConfigureTokensForTransfersConfig) error {
48+
return func(e cldf.Environment, cfg ConfigureTokensForTransfersConfig) error {
49+
// TODO: implement
50+
return nil
51+
}
52+
}
53+
54+
func makeApply(tokenRegistry *TokenAdapterRegistry, mcmsRegistry *changesets.MCMSReaderRegistry) func(cldf.Environment, ConfigureTokensForTransfersConfig) (cldf.ChangesetOutput, error) {
55+
return func(e cldf.Environment, cfg ConfigureTokensForTransfersConfig) (cldf.ChangesetOutput, error) {
56+
batchOps := make([]mcms_types.BatchOperation, 0)
57+
reports := make([]cldf_ops.Report[any, any], 0)
58+
59+
for _, token := range cfg.Tokens {
60+
tokenPool, err := datastore_utils.FindAndFormatRef(e.DataStore, token.TokenPoolRef, token.ChainSelector, datastore_utils.FullRef)
61+
if err != nil {
62+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve token pool ref on chain with selector %d: %w", token.ChainSelector, err)
63+
}
64+
registry, err := datastore_utils.FindAndFormatRef(e.DataStore, token.RegistryRef, token.ChainSelector, datastore_utils.FullRef)
65+
if err != nil {
66+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to resolve registry ref on chain with selector %d: %w", token.ChainSelector, err)
67+
}
68+
69+
family, err := chain_selectors.GetSelectorFamily(token.ChainSelector)
70+
if err != nil {
71+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to get chain family for chain selector %d: %w", token.ChainSelector, err)
72+
}
73+
adapter, ok := tokenRegistry.GetTokenAdapter(family, tokenPool.Version)
74+
if !ok {
75+
return cldf.ChangesetOutput{}, fmt.Errorf("no token adapter registered for chain family '%s' and token pool version '%s'", family, tokenPool.Version)
76+
}
77+
78+
remoteChains := make(map[uint64]RemoteChainConfig[[]byte, string], len(token.RemoteChains))
79+
for remoteChainSelector, inCfg := range token.RemoteChains {
80+
remoteChains[remoteChainSelector], err = convertRemoteChainConfig(e, adapter, token.ChainSelector, remoteChainSelector, inCfg)
81+
if err != nil {
82+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to process remote chain config for remote chain selector %d: %w", remoteChainSelector, err)
83+
}
84+
}
85+
configureTokenReport, err := cldf_ops.ExecuteSequence(e.OperationsBundle, adapter.ConfigureTokenForTransfersSequence(), e.BlockChains, ConfigureTokenForTransfersInput{
86+
ChainSelector: token.ChainSelector,
87+
TokenPoolAddress: tokenPool.Address,
88+
RemoteChains: remoteChains,
89+
ExternalAdmin: token.ExternalAdmin,
90+
RegistryAddress: registry.Address,
91+
})
92+
if err != nil {
93+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to configure token pool on chain with selector %d: %w", token.ChainSelector, err)
94+
}
95+
96+
batchOps = append(batchOps, configureTokenReport.Output.BatchOps...)
97+
reports = append(reports, configureTokenReport.ExecutionReports...)
98+
}
99+
100+
return changesets.NewOutputBuilder(e, mcmsRegistry).
101+
WithReports(reports).
102+
WithBatchOps(batchOps).
103+
Build(cfg.MCMS)
104+
}
105+
}
106+
107+
func convertRemoteChainConfig(
108+
e deployment.Environment,
109+
adapter TokenAdapter,
110+
chainSelector uint64,
111+
remoteChainSelector uint64,
112+
inCfg RemoteChainConfig[*datastore.AddressRef, datastore.AddressRef],
113+
) (RemoteChainConfig[[]byte, string], error) {
114+
outCfg := RemoteChainConfig[[]byte, string]{
115+
InboundRateLimiterConfig: inCfg.InboundRateLimiterConfig,
116+
OutboundRateLimiterConfig: inCfg.OutboundRateLimiterConfig,
117+
}
118+
var err error
119+
if inCfg.RemotePool != nil {
120+
outCfg.RemotePool, err = datastore_utils.FindAndFormatRef(e.DataStore, *inCfg.RemotePool, remoteChainSelector, adapter.AddressRefToBytes)
121+
if err != nil {
122+
return outCfg, fmt.Errorf("failed to resolve remote pool ref %s: %w", datastore_utils.SprintRef(*inCfg.RemotePool), err)
123+
}
124+
// Can either provide the token reference directly or derive it from the pool reference.
125+
if inCfg.RemoteToken != nil {
126+
outCfg.RemoteToken, err = datastore_utils.FindAndFormatRef(e.DataStore, *inCfg.RemoteToken, remoteChainSelector, adapter.AddressRefToBytes)
127+
if err != nil {
128+
return outCfg, fmt.Errorf("failed to resolve remote token ref %s: %w", datastore_utils.SprintRef(*inCfg.RemoteToken), err)
129+
}
130+
} else {
131+
outCfg.RemoteToken, err = adapter.DeriveTokenAddress(e, remoteChainSelector, *inCfg.RemotePool)
132+
if err != nil {
133+
return outCfg, fmt.Errorf("failed to get remote token address via pool ref (%s) for remote chain selector %d: %w", datastore_utils.SprintRef(*inCfg.RemotePool), remoteChainSelector, err)
134+
}
135+
}
136+
}
137+
for _, ccvRef := range inCfg.OutboundCCVs {
138+
fullCCVRef, err := datastore_utils.FindAndFormatRef(e.DataStore, ccvRef, chainSelector, datastore_utils.FullRef)
139+
if err != nil {
140+
return outCfg, fmt.Errorf("failed to resolve outbound CCV ref %s: %w", datastore_utils.SprintRef(ccvRef), err)
141+
}
142+
outCfg.OutboundCCVs = append(outCfg.OutboundCCVs, fullCCVRef.Address)
143+
}
144+
for _, ccvRef := range inCfg.InboundCCVs {
145+
fullCCVRef, err := datastore_utils.FindAndFormatRef(e.DataStore, ccvRef, chainSelector, datastore_utils.FullRef)
146+
if err != nil {
147+
return outCfg, fmt.Errorf("failed to resolve inbound CCV ref %s: %w", datastore_utils.SprintRef(ccvRef), err)
148+
}
149+
outCfg.InboundCCVs = append(outCfg.InboundCCVs, fullCCVRef.Address)
150+
}
151+
return outCfg, nil
152+
}

0 commit comments

Comments
 (0)