Skip to content

Commit

Permalink
USDC token pool changesets (#16428)
Browse files Browse the repository at this point in the history
* Add changes

* Fix

* nolint
  • Loading branch information
kylesmartin authored Feb 19, 2025
1 parent ff814eb commit d69c181
Show file tree
Hide file tree
Showing 11 changed files with 977 additions and 26 deletions.
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

0 comments on commit d69c181

Please sign in to comment.