Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USDC token pool changesets #16428

Merged
merged 3 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions deployment/ccip/changeset/cs_ccip_home.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,13 @@ func validateUSDCConfig(usdcConfig *pluginconfig.USDCCCTPObserverConfig, state C
if !ok {
return fmt.Errorf("chain %d does not exist in state but provided in USDCCCTPObserverConfig", sel)
}
if onchainState.USDCTokenPool == nil {
return fmt.Errorf("chain %d does not have USDC token pool deployed", sel)
if onchainState.USDCTokenPools == nil || onchainState.USDCTokenPools[deployment.Version1_5_1] == nil {
return fmt.Errorf("chain %d does not have USDC token pool deployed with version %s", sel, deployment.Version1_5_1)
}
if common.HexToAddress(token.SourcePoolAddress) != onchainState.USDCTokenPool.Address() {
if common.HexToAddress(token.SourcePoolAddress) != onchainState.USDCTokenPools[deployment.Version1_5_1].Address() {
return fmt.Errorf("chain %d has USDC token pool deployed at %s, "+
"but SourcePoolAddress %s is provided in USDCCCTPObserverConfig",
sel, onchainState.USDCTokenPool.Address().String(), token.SourcePoolAddress)
sel, onchainState.USDCTokenPools[deployment.Version1_5_1].Address().String(), token.SourcePoolAddress)
}
}
return nil
Expand Down
155 changes: 155 additions & 0 deletions deployment/ccip/changeset/cs_deploy_usdc_token_pools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package changeset

import (
"context"
"errors"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"

"github.com/smartcontractkit/chainlink-integrations/evm/utils"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/latest/mock_usdc_token_messenger"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_5_1/usdc_token_pool"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/erc20"
)

var _ deployment.ChangeSet[DeployUSDCTokenPoolContractsConfig] = DeployUSDCTokenPoolContractsChangeset

// DeployUSDCTokenPoolInput defines all information required of the user to deploy a new USDC token pool contract.
type DeployUSDCTokenPoolInput struct {
// TokenMessenger is the address of the USDC token messenger contract.
TokenMessenger common.Address
// USDCTokenAddress is the address of the USDC token for which we are deploying a token pool.
TokenAddress common.Address
// AllowList is the optional list of addresses permitted to initiate a token transfer.
// If omitted, all addresses will be permitted to transfer the token.
AllowList []common.Address
}

func (i DeployUSDCTokenPoolInput) Validate(ctx context.Context, chain deployment.Chain, state CCIPChainState) error {
// Ensure that required fields are populated
if i.TokenAddress == utils.ZeroAddress {
return errors.New("token address must be defined")
}
if i.TokenMessenger == utils.ZeroAddress {
return errors.New("token messenger must be defined")
}

// Validate the token exists and matches the USDC symbol
token, err := erc20.NewERC20(i.TokenAddress, chain.Client)
if err != nil {
return fmt.Errorf("failed to connect address %s with erc20 bindings: %w", i.TokenAddress, err)
}
symbol, err := token.Symbol(&bind.CallOpts{Context: ctx})
if err != nil {
return fmt.Errorf("failed to fetch symbol from token with address %s: %w", i.TokenAddress, err)
}
if symbol != string(USDCSymbol) {
return fmt.Errorf("symbol of token with address %s (%s) is not USDC", i.TokenAddress, symbol)
}

// Check if a USDC token pool with the given version already exists
fmt.Println(state.USDCTokenPools[deployment.Version1_5_1])
if _, ok := state.USDCTokenPools[deployment.Version1_5_1]; ok {
return fmt.Errorf("USDC token pool with version %s already exists on %s", deployment.Version1_5_1, chain)
}

// Perform USDC checks (i.e. make sure we can call the required functions)
// LocalMessageTransmitter and MessageBodyVersion are called in the contract constructor:
// https://github.com/smartcontractkit/chainlink/blob/f52a57762643b9cdc8e9241737e13501a4278716/contracts/src/v0.8/ccip/pools/USDC/USDCTokenPool.sol#L83
messenger, err := mock_usdc_token_messenger.NewMockE2EUSDCTokenMessenger(i.TokenMessenger, chain.Client)
if err != nil {
return fmt.Errorf("failed to connect address %s on %s with token messenger bindings: %w", i.TokenMessenger, chain, err)
}
_, err = messenger.LocalMessageTransmitter(&bind.CallOpts{Context: ctx})
if err != nil {
return fmt.Errorf("failed to fetch local message transmitter from address %s on %s: %w", i.TokenMessenger, chain, err)
}
_, err = messenger.MessageBodyVersion(&bind.CallOpts{Context: ctx})
if err != nil {
return fmt.Errorf("failed to fetch message body version from address %s on %s: %w", i.TokenMessenger, chain, err)
}

return nil
}

// DeployUSDCTokenPoolContractsConfig defines the USDC token pool contracts that need to be deployed on each chain.
type DeployUSDCTokenPoolContractsConfig struct {
// USDCPools defines the per-chain configuration of each new USDC pool.
USDCPools map[uint64]DeployUSDCTokenPoolInput
}

func (c DeployUSDCTokenPoolContractsConfig) Validate(env deployment.Environment) error {
state, err := LoadOnchainState(env)
if err != nil {
return fmt.Errorf("failed to load onchain state: %w", err)
}
for chainSelector, poolConfig := range c.USDCPools {
err := deployment.IsValidChainSelector(chainSelector)
if err != nil {
return fmt.Errorf("failed to validate chain selector %d: %w", chainSelector, err)
}
chain, ok := env.Chains[chainSelector]
if !ok {
return fmt.Errorf("chain with selector %d does not exist in environment", chainSelector)
}
chainState, ok := state.Chains[chainSelector]
if !ok {
return fmt.Errorf("chain with selector %d does not exist in state", chainSelector)
}
if chainState.Router == nil {
return fmt.Errorf("missing router on %s", chain)
}
if chainState.RMNProxy == nil {
return fmt.Errorf("missing rmnProxy on %s", chain)
}
err = poolConfig.Validate(env.GetContext(), chain, chainState)
if err != nil {
return fmt.Errorf("failed to validate USDC token pool config for chain selector %d: %w", chainSelector, err)
}
}
return nil
}

// DeployUSDCTokenPoolContractsChangeset deploys new USDC pools across multiple chains.
func DeployUSDCTokenPoolContractsChangeset(env deployment.Environment, c DeployUSDCTokenPoolContractsConfig) (deployment.ChangesetOutput, error) {
if err := c.Validate(env); err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployUSDCTokenPoolContractsConfig: %w", err)
}
newAddresses := deployment.NewMemoryAddressBook()

state, err := LoadOnchainState(env)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to load onchain state: %w", err)
}

for chainSelector, poolConfig := range c.USDCPools {
chain := env.Chains[chainSelector]
chainState := state.Chains[chainSelector]

_, err := deployment.DeployContract(env.Logger, chain, newAddresses,
func(chain deployment.Chain) deployment.ContractDeploy[*usdc_token_pool.USDCTokenPool] {
poolAddress, tx, usdcTokenPool, err := usdc_token_pool.DeployUSDCTokenPool(
chain.DeployerKey, chain.Client, poolConfig.TokenMessenger, poolConfig.TokenAddress,
poolConfig.AllowList, chainState.RMNProxy.Address(), chainState.Router.Address(),
)
return deployment.ContractDeploy[*usdc_token_pool.USDCTokenPool]{
Address: poolAddress,
Contract: usdcTokenPool,
Tv: deployment.NewTypeAndVersion(USDCTokenPool, deployment.Version1_5_1),
Tx: tx,
Err: err,
}
},
)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to deploy USDC token pool on %s: %w", chain, err)
}
}

return deployment.ChangesetOutput{
AddressBook: newAddresses,
}, nil
}
Loading
Loading