Conversation
There was a problem hiding this comment.
Pull request overview
This PR advances “Token Expansion v2” by wiring EVM token adapters into the TokenExpansion flow, adding support for an additional-CCV threshold parameter, and migrating/removing legacy “deploy token + pool” sequences/changesets in v1.7.0 in favor of TokenExpansion-driven deployments.
Changes:
- Add
ThresholdForAdditionalCCVsto token-pool deployment input and pass it through TokenExpansion into EVM 2.0.0+ pool deployments. - Implement missing EVM token adapter methods for v1.6.1 and v1.7.0 (2.0.0 pools), plus update v1.7.0 tests to deploy via TokenExpansion.
- Remove legacy v1.7.0 deploy-token-and-pool sequences/changesets and adjust devenv Go module metadata accordingly.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| devenv/go.mod | Removes replace-block; keeps gogo/protobuf replace and updates indirect deps. |
| devenv/go.sum | Updates dependency checksums after module graph changes. |
| deployment/tokens/token_expansion.go | Adds threshold field and ref-merging simplification for token/pool deployment. |
| chains/evm/deployment/v1_6_1/adapters/tokens.go | Implements DeployToken/Verify + DeployTokenPoolForToken wrapper and role-grant logic. |
| ccv/chains/evm/deployment/v1_7_0/sequences/tokens/test_helpers_test.go | New helper to deploy token+pool via TokenExpansion for tests. |
| ccv/chains/evm/deployment/v1_7_0/sequences/tokens/deploy_token_and_pool.go | Removes legacy deploy-token-and-pool sequence (now replaced by TokenExpansion in tests). |
| ccv/chains/evm/deployment/v1_7_0/sequences/tokens/deploy_token_and_pool_test.go | Removes legacy tests for removed sequence. |
| ccv/chains/evm/deployment/v1_7_0/sequences/tokens/configure_token_pool_test.go | Migrates to TokenExpansion-based deployment helper. |
| ccv/chains/evm/deployment/v1_7_0/sequences/tokens/configure_token_pool_for_remote_chain_test.go | Migrates to TokenExpansion-based deployment helper and datastore lookups. |
| ccv/chains/evm/deployment/v1_7_0/sequences/tokens/configure_token_for_transfers_test.go | Migrates to TokenExpansion-based deployment helper and datastore lookups. |
| ccv/chains/evm/deployment/v1_7_0/changesets/deploy_token_and_pool.go | Removes legacy changeset. |
| ccv/chains/evm/deployment/v1_7_0/changesets/deploy_token_and_pool_test.go | Removes legacy changeset tests. |
| ccv/chains/evm/deployment/v1_7_0/adapters/tokens.go | Implements 2.0.0 token adapter deploy/pool deploy, manual registration, rate limits, and authority updates. |
| ccv/chains/evm/deployment/v1_7_0/adapters/tokens_test.go | Updates adapter tests to rely on init-registered adapter and adds TokenExpansion E2E test. |
| ccv/chains/evm/deployment/v1_7_0/adapters/init.go | Registers the v1.7.0 EVM TokenAdapter in the global registry at init. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
If I'm not mistaken, the current v1.6.0 adapter is capable of dealing with v1.6.1, so is it possible to replace all the duplicated code with something like this?
var _ tokens.TokenAdapter = &TokenAdapter{}
// TokenAdapter is the adapter for EVM tokens using 1.6.1 token pools.
type TokenAdapter struct {
v1_6 v1_6_0_seq.EVMAdapter
}
func (t *TokenAdapter) ConfigureTokenForTransfersSequence() *cldf_ops.Sequence[tokens.ConfigureTokenForTransfersInput, sequences.OnChainOutput, cldf_chain.BlockChains] {
return t.v1_6.ConfigureTokenForTransfersSequence()
}
func (t *TokenAdapter) AddressRefToBytes(ref datastore.AddressRef) ([]byte, error) {
return t.v1_6.AddressRefToBytes(ref)
}
func (t *TokenAdapter) DeriveTokenAddress(e deployment.Environment, chainSelector uint64, poolRef datastore.AddressRef) ([]byte, error) {
return t.v1_6.DeriveTokenAddress(e, chainSelector, poolRef)
}
func (t *TokenAdapter) DeployToken() *cldf_ops.Sequence[tokens.DeployTokenInput, sequences.OnChainOutput, cldf_chain.BlockChains] {
return t.v1_6.DeployToken()
}
func (t *TokenAdapter) DeployTokenVerify(e deployment.Environment, input tokens.DeployTokenInput) error {
return t.v1_6.DeployTokenVerify(e, input)
}
//...There was a problem hiding this comment.
Same comment as v1.6.1 adapter - would it be worth it / make sense to inject the v1.6.0 adapter and reuse some of its methods to reduce duplication? It looks like there's an opportunity to re-use these:
DeployToken<- the seq for this is in the v1.6 folder, but seems like it should be moved to the v1.0 folderDeployTokenVerifyAddressRefToBytesDeriveTokenAddressDeriveTokenDecimalsManualRegistrationUpdateAuthorities
Or maybe it makes sense to approach this a different way? During the develop > main merge, token expansion was modified such that it loads adapters based on the token pool version rather than the chain adapter version:
chainlink-ccip/deployment/tokens/configure_tokens_for_transfers.go
Lines 276 to 291 in c43ece7
So maybe each adapter should purely be responsible for managing pool bindings for a single version? I think that would remove the need for a switch clause on the version from the v1.6.0 adapter and make things a bit more straightforward unless I'm missing something 👀
| DeployTokenPoolInput: &tokenscore.DeployTokenPoolInput{ | ||
| PoolType: string(burn_mint_token_pool.ContractType), | ||
| TokenPoolQualifier: "TEST", | ||
| ThresholdForAdditionalCCVs: thresholdAmountForAdditionalCCVs.String(), |
There was a problem hiding this comment.
nit: Should this also be called ThresholdAmount?
| // ThresholdForAdditionalCCVs is the transfer amount (in base units, as a decimal string) | ||
| // above which additional CCVs are required. Applicable to EVM 2.0.0+ token pools. | ||
| // If empty or "0", no threshold is set. | ||
| ThresholdForAdditionalCCVs string `yaml:"thresholdForAdditionalCCVs,omitempty" json:"thresholdForAdditionalCCVs,omitempty"` |
There was a problem hiding this comment.
I am wondering if it's worth moving all the version specific field under a property
type V2Fields struct {
{
// ThresholdForAdditionalCCVs is the transfer amount (in base units, as a decimal string)
// above which additional CCVs are required. Applicable to EVM 2.0.0+ token pools.
// If empty or "0", no threshold is set.
ThresholdForAdditionalCCVs string `yaml:"thresholdForAdditionalCCVs,omitempty" json:"thresholdForAdditionalCCVs,omitempty"`
}
}
v1_6Only *V1_6Fields
v2Only *V2FieldsThis would make it more explicit from the input and could allow us to enforce only one of the field group is set at once?
There was a problem hiding this comment.
Will handle in a followup
|
| return poolOwner, rlAdmin, nil | ||
| } | ||
|
|
||
| func (a *EVMAdapter) FindLatestAddressRef(ds datastore.DataStore, ref datastore.AddressRef) (common.Address, error) { |
There was a problem hiding this comment.
non-blocking: might be worth it to extract some of this logic to datastore_utils 👀
| // 1. if it is already present in the TokenRef, then skip the datastore altogether and simply use the given address | ||
| // 2. if the token address is not present, then use whatever fields are defined in TokenRef to lookup the address in the DS | ||
| // 3. if step #2 produces multiple tokens or an error, then attempt to resolve the token address from the TokenPoolRef | ||
| // 4. if we still can't derive it from the TokenPoolRef, then give up |
There was a problem hiding this comment.
Is it okay if we retain some of these comments in the v1.0.0 adapter?
Some of them could help clarify the code/logic a bit more
| // Helper methods // | ||
| //////////////////// | ||
|
|
||
| func (a *EVMAdapter) GetTokenAdminRegistryAddress(ds datastore.DataStore, selector uint64) (common.Address, error) { |
There was a problem hiding this comment.
Seems duplicated with the new helper introduced in the v1.0.0 adapter - any chance we can remove this and re-use some more things?
| // AddressRefToBytes converts a hex-encoded address to its EVM byte representation. | ||
| func (a *EVMAdapter) AddressRefToBytes(ref datastore.AddressRef) ([]byte, error) { | ||
| if !common.IsHexAddress(ref.Address) { | ||
| return nil, fmt.Errorf("address %q is not a valid hex address", ref.Address) | ||
| } | ||
|
|
||
| return common.HexToAddress(ref.Address).Bytes(), nil | ||
| } |
There was a problem hiding this comment.
I believe v1.0.0 offers an impl for AddressRefToBytes - can this be removed?
| func StripPatchVersion(version *semver.Version) *semver.Version { | ||
| return semver.New(version.Major(), version.Minor(), 0, version.Prerelease(), version.Metadata()) | ||
| } |
There was a problem hiding this comment.
Is it okay if we keep this around? Wanna use it to refactor the fees adapter:
| return &TokenAdapter{ | ||
| EVMPoolAdapter: evm1_0_0.EVMPoolAdapter{ | ||
| Ops: &poolOpsV151{}, | ||
| DeployTokenPoolSeq: v1_6_0_seq.DeployTokenPool, |
There was a problem hiding this comment.
non-blocking: for later we may want to refactor DeployTokenPool so that v1.5.1 has its own sequence and the v1.6.0 sequence is only responsible for its own bindings
| v1_6_0_scenarios = semver.MustParse("1.6.0") | ||
| v1_5_1_scenarios = semver.MustParse("1.5.1") |
There was a problem hiding this comment.
non-blocking (and unrelated): might be worth it to start using the predefined constants moving forward
chainlink-ccip/deployment/utils/common.go
Lines 103 to 110 in 0ec22f0
| deployTokenPoolInput.TokenPoolVersion = input.TokenPoolVersion | ||
| deployTokenPoolInput.ExistingDataStore = e.DataStore | ||
| deployTokenPoolInput.ChainSelector = selector | ||
| deployTokenPoolReport, err := cldf_ops.ExecuteSequence(e.OperationsBundle, tokenPoolAdapter.DeployTokenPoolForToken(), e.BlockChains, deployTokenPoolInput) |
There was a problem hiding this comment.
On this line, we are using the tokenPoolAdapter variable which is tied to the ChainAdapterVersion - instead the adapter should be fetched based on the TokenPoolVersion right?
No description provided.