-
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 changesets in shared package which transfer the link token JIRA: https://smartcontract-it.atlassian.net/browse/DPA-1314
- Loading branch information
1 parent
07bd33c
commit b08abf3
Showing
2 changed files
with
218 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,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
138
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,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) | ||
} |