-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(deployment): add link token transfer changeset
Added a new changeset in shared package which transfer the link token. JIRA: https://smartcontract-it.atlassian.net/browse/DPA-1314
- Loading branch information
1 parent
5633382
commit 4ee2d85
Showing
2 changed files
with
247 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package changeset | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/common" | ||
gethtypes "github.com/ethereum/go-ethereum/core/types" | ||
|
||
"github.com/smartcontractkit/chainlink/deployment" | ||
) | ||
|
||
var _ deployment.ChangeSet[TransferLinkTokenBatchConfig] = TransferLinkTokenBatch | ||
|
||
type TokenTransferer interface { | ||
Transfer(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*gethtypes.Transaction, error) | ||
} | ||
|
||
type TransferLinkTokenConfig struct { | ||
To common.Address | ||
Amount *big.Int | ||
Transferer TokenTransferer | ||
} | ||
|
||
type TransferLinkTokenBatchConfig struct { | ||
Transfers map[uint64]TransferLinkTokenConfig | ||
} | ||
|
||
func (cfg TransferLinkTokenBatchConfig) Validate() error { | ||
for k, c := range cfg.Transfers { | ||
if err := deployment.IsValidChainSelector(k); err != nil { | ||
return err | ||
} | ||
if c.To == (common.Address{}) { | ||
return errors.New("to address must be set") | ||
} | ||
if c.Amount == nil || c.Amount.Sign() == -1 { | ||
return errors.New("amount must be set and positive") | ||
} | ||
if c.Transferer == nil { | ||
return errors.New("transferer must be set") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// TransferLinkTokenBatch transfers link token to the to address from the transferer in a batch. | ||
func TransferLinkTokenBatch(e deployment.Environment, config TransferLinkTokenBatchConfig) (deployment.ChangesetOutput, error) { | ||
if err := config.Validate(); err != nil { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("invalid transfer link token config: %v", err) | ||
} | ||
|
||
for chainSelector, transferConfig := range config.Transfers { | ||
chain, ok := e.Chains[chainSelector] | ||
if !ok { | ||
return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment") | ||
} | ||
tx, err := transferConfig.Transferer.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()) | ||
} | ||
return deployment.ChangesetOutput{}, nil | ||
} |
179 changes: 179 additions & 0 deletions
179
deployment/common/changeset/transfer_link_token_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
package changeset_test | ||
|
||
import ( | ||
"math/big" | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/common" | ||
gethtypes "github.com/ethereum/go-ethereum/core/types" | ||
chainselectors "github.com/smartcontractkit/chain-selectors" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/zap/zapcore" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
|
||
"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.TransferLinkTokenBatchConfig | ||
wantErr bool | ||
wantErrMsg string | ||
}{ | ||
{ | ||
name: "valid config", | ||
config: changeset.TransferLinkTokenBatchConfig{ | ||
Transfers: map[uint64]changeset.TransferLinkTokenConfig{ | ||
chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector: { | ||
To: common.HexToAddress("0x1"), | ||
Amount: big.NewInt(1), | ||
Transferer: mockTokenTransferer{}, | ||
}, | ||
}, | ||
}, | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "missing to address", | ||
config: changeset.TransferLinkTokenBatchConfig{ | ||
Transfers: map[uint64]changeset.TransferLinkTokenConfig{ | ||
chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector: { | ||
Amount: big.NewInt(1), | ||
Transferer: mockTokenTransferer{}, | ||
}, | ||
}, | ||
}, | ||
wantErr: true, | ||
wantErrMsg: "to address must be set", | ||
}, | ||
{ | ||
name: "missing amount", | ||
config: changeset.TransferLinkTokenBatchConfig{ | ||
Transfers: map[uint64]changeset.TransferLinkTokenConfig{ | ||
chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector: { | ||
To: common.HexToAddress("0x1"), | ||
Transferer: mockTokenTransferer{}, | ||
}, | ||
}, | ||
}, | ||
wantErr: true, | ||
wantErrMsg: "amount must be set and positive", | ||
}, | ||
{ | ||
name: "missing transferer", | ||
config: changeset.TransferLinkTokenBatchConfig{ | ||
Transfers: map[uint64]changeset.TransferLinkTokenConfig{ | ||
chainselectors.ETHEREUM_TESTNET_SEPOLIA.Selector: { | ||
To: common.HexToAddress("0x1"), | ||
Amount: big.NewInt(1), | ||
Transferer: nil, | ||
}, | ||
}, | ||
}, | ||
wantErr: true, | ||
wantErrMsg: "transferer must be set", | ||
}, | ||
{ | ||
name: "invalid chain selector", | ||
config: changeset.TransferLinkTokenBatchConfig{ | ||
Transfers: map[uint64]changeset.TransferLinkTokenConfig{ | ||
0: { | ||
To: common.HexToAddress("0x1"), | ||
Amount: big.NewInt(1), | ||
Transferer: mockTokenTransferer{}, | ||
}, | ||
}, | ||
}, | ||
wantErr: true, | ||
wantErrMsg: "chain selector must be set", | ||
}, | ||
} | ||
|
||
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.TransferLinkTokenBatch(env, changeset.TransferLinkTokenBatchConfig{ | ||
Transfers: map[uint64]changeset.TransferLinkTokenConfig{ | ||
chainSelectorId: { | ||
To: receiver, | ||
Amount: big.NewInt(30), | ||
Transferer: tokenContract.Contract, | ||
}, | ||
}, | ||
}) | ||
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) | ||
} | ||
|
||
type mockTokenTransferer struct{} | ||
|
||
func (m mockTokenTransferer) Transfer(opts *bind.TransactOpts, | ||
to common.Address, amount *big.Int) (*gethtypes.Transaction, error) { | ||
return nil, nil | ||
} |