Skip to content

Commit

Permalink
feat(deployment): add link token transfer changeset
Browse files Browse the repository at this point in the history
Added a new changesets in shared package which transfer the link token

JIRA: https://smartcontract-it.atlassian.net/browse/DPA-1314
  • Loading branch information
graham-chainlink committed Dec 5, 2024
1 parent 07bd33c commit b08abf3
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 0 deletions.
80 changes: 80 additions & 0 deletions deployment/common/changeset/transfer_link_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package changeset

import (
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/types"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/link_token"
)

var _ deployment.ChangeSet[TransferLinkTokenConfig] = TransferLinkToken

type Transfer struct {
To common.Address
Amount *big.Int
}

type TransferLinkTokenConfig struct {
Transfers map[uint64]Transfer
}

func (c TransferLinkTokenConfig) Validate() error {
for k, v := range c.Transfers {
if err := deployment.IsValidChainSelector(k); err != nil {
return err
}

if v.To == (common.Address{}) {
return errors.New("to address must be set")
}
if v.Amount == nil || v.Amount.Sign() == -1 {
return errors.New("amount must be set and positive")
}
}
return nil
}

// TransferLinkToken transfers link token to the to address on the chain identified by the chainSelector.
func TransferLinkToken(e deployment.Environment, config TransferLinkTokenConfig) (deployment.ChangesetOutput, error) {
if err := config.Validate(); err != nil {
return deployment.ChangesetOutput{}, err
}

for chainSelector, transferConfig := range config.Transfers {
addresses, err := e.ExistingAddresses.AddressesForChain(chainSelector)
if err != nil {
return deployment.ChangesetOutput{}, err
}

for address, typeversion := range addresses {
if typeversion.Type == types.LinkToken && typeversion.Version == deployment.Version1_0_0 {
chain, ok := e.Chains[chainSelector]
if !ok {
return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment")
}
contract, err := link_token.NewLinkToken(common.HexToAddress(address), chain.Client)
if err != nil {
return deployment.ChangesetOutput{}, err
}

tx, err := contract.Transfer(chain.DeployerKey, transferConfig.To, transferConfig.Amount)
if _, err = deployment.ConfirmIfNoError(chain, tx, err); err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm transfer link token to %s: %v", transferConfig.To, err)
}
e.Logger.Infow("Transferred LINK",
"to", transferConfig.To,
"amount", transferConfig.Amount,
"txHash", tx.Hash().Hex(),
"chainSelector", chainSelector)

break
}
}
}
return deployment.ChangesetOutput{}, nil
}
138 changes: 138 additions & 0 deletions deployment/common/changeset/transfer_link_token_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package changeset_test

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
chainselectors "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/changeset"
"github.com/smartcontractkit/chainlink/deployment/common/types"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/link_token"
)

func TestTransferLinkToken_Validate(t *testing.T) {
t.Parallel()

tests := []struct {
name string
config changeset.TransferLinkTokenConfig
wantErr bool
wantErrMsg string
}{
{
name: "valid config",
config: changeset.TransferLinkTokenConfig{
Transfers: map[uint64]changeset.Transfer{
chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector: {
To: common.HexToAddress("0x1"),
Amount: big.NewInt(1),
},
},
},
wantErr: false,
},
{
name: "missing to address",
config: changeset.TransferLinkTokenConfig{
Transfers: map[uint64]changeset.Transfer{
chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector: {
Amount: big.NewInt(1),
},
},
},
wantErr: true,
wantErrMsg: "to address must be set",
},
{
name: "missing amount",
config: changeset.TransferLinkTokenConfig{
Transfers: map[uint64]changeset.Transfer{
chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector: {
To: common.HexToAddress("0x1"),
},
},
},
wantErr: true,
wantErrMsg: "amount must be set and positive",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()

err := test.config.Validate()
if test.wantErr {
if test.wantErrMsg != "" {
assert.Contains(t, err.Error(), test.wantErrMsg)
}
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

func TestTransferLinkToken(t *testing.T) {
t.Parallel()

lggr := logger.Test(t)
cfg := memory.MemoryEnvironmentConfig{
Nodes: 1,
Chains: 1,
}
env := memory.NewMemoryEnvironment(t, lggr, zapcore.DebugLevel, cfg)
chainSelectorId := env.AllChainSelectors()[0]

chain := env.Chains[chainSelectorId]
deployer := chain.DeployerKey
tokenContract, err := deployment.DeployContract(lggr, chain, env.ExistingAddresses,
func(chain deployment.Chain) deployment.ContractDeploy[*link_token.LinkToken] {
tokenAddress, tx, token, err2 := link_token.DeployLinkToken(
deployer,
chain.Client,
)
return deployment.ContractDeploy[*link_token.LinkToken]{
tokenAddress, token, tx, deployment.NewTypeAndVersion(types.LinkToken, deployment.Version1_0_0), err2,
}
})
require.NoError(t, err)

tx, err := tokenContract.Contract.GrantMintRole(deployer, deployer.From)
require.NoError(t, err)
_, err = chain.Confirm(tx)

tx, err = tokenContract.Contract.Mint(deployer, deployer.From, big.NewInt(100))
require.NoError(t, err)
_, err = chain.Confirm(tx)
require.NoError(t, err)

receiver := common.HexToAddress("0x1")
_, err = changeset.TransferLinkToken(env,
changeset.TransferLinkTokenConfig{
Transfers: map[uint64]changeset.Transfer{
chainSelectorId: {
To: receiver,
Amount: big.NewInt(30),
},
},
})
require.NoError(t, err)

balance, err := tokenContract.Contract.BalanceOf(nil, deployer.From)
require.NoError(t, err)
require.Equal(t, big.NewInt(70), balance)

balance, err = tokenContract.Contract.BalanceOf(nil, receiver)
require.NoError(t, err)
require.Equal(t, big.NewInt(30), balance)
}

0 comments on commit b08abf3

Please sign in to comment.