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

feat: link transfer mcms changesets #15512

Merged
merged 47 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
a6910ae
feat: link transfer with timelock changeset
ecPablo Dec 3, 2024
c46c5d4
feat: link transfer and approval integration tests and changesets.
ecPablo Dec 4, 2024
869cf6a
feat: rename files
ecPablo Dec 4, 2024
c0f3969
fix: use deployment.SimTransactOpts() to get tx data
ecPablo Dec 4, 2024
239d7b7
fix: link contract creation
ecPablo Dec 5, 2024
9060af1
fix: remove approval changeset, not necessary for sending directly fr…
ecPablo Dec 5, 2024
a1eaa94
feat: make config accept a map of chain selectors for the proposal ge…
ecPablo Dec 5, 2024
9897c40
Merge branch 'refs/heads/develop' into ecPablo/link-transfer-mcms
ecPablo Dec 5, 2024
f9fd24c
fix: params on deploy link
ecPablo Dec 5, 2024
b5d343a
fix: simplify config args by using state helper functions.
ecPablo Dec 5, 2024
c54218f
fix: use pointer for value
ecPablo Dec 5, 2024
9dca3be
feat: add mint permissions and minting link changeset
ecPablo Dec 6, 2024
4d007c3
Deploy call proxy instead of using deployer executor keys
akhilchainani Dec 6, 2024
101b9fd
inject call proxies in execution methods
akhilchainani Dec 8, 2024
663f8b0
skip call proxy when loading chain state
akhilchainani Dec 8, 2024
c17911e
revert all changes
akhilchainani Dec 8, 2024
5b96629
Revert "revert all changes"
akhilchainani Dec 8, 2024
aa75a47
Merge branch 'refs/heads/fix/deployment-call-proxy' into ecPablo/link…
ecPablo Dec 9, 2024
b6cc93f
Merge branch 'refs/heads/develop' into ecPablo/link-transfer-mcms
ecPablo Dec 9, 2024
a51f09a
chore: rename load state funcs
ecPablo Dec 9, 2024
898a20d
feat: add mcms config flag
ecPablo Dec 10, 2024
7d2203c
Merge branch 'refs/heads/develop' into ecPablo/link-transfer-mcms
ecPablo Dec 10, 2024
96d2a8f
fix: integration tests after merging develop
ecPablo Dec 10, 2024
c3e528c
fix: use contracts from states and code improvements
ecPablo Dec 10, 2024
127aa4d
fix: cs deploy chain args
ecPablo Dec 10, 2024
c898322
fix: params ccip boosting
ecPablo Dec 10, 2024
ba6b57d
fix: bundle mcms config into single struct
ecPablo Dec 10, 2024
e6ac21a
fix: add more validations for config
ecPablo Dec 10, 2024
001b002
fix: remove startingOpCount and use proposal utils to derive it
ecPablo Dec 10, 2024
6641f4f
fix: adjust variable names, remove boolean for mcms config, add const…
ecPablo Dec 11, 2024
f889dfc
feat: add tests for non mcms case, improve validations, and wait for …
ecPablo Dec 11, 2024
b9479fc
feat: check valid until is in future
ecPablo Dec 11, 2024
03e9382
feat: add tests for Validate() and small validation fixes
ecPablo Dec 11, 2024
3d2dad1
fix: rename MaybeLoadLinkTokenState to MaybeLoadLinkTokenChainState t…
ecPablo Dec 11, 2024
911130a
Update deployment/common/changeset/example/link_transfer.go
ecPablo Dec 11, 2024
7dd0fdd
fix: error handling and validations
ecPablo Dec 11, 2024
9bc7467
fix: use getDeployer helper
ecPablo Dec 11, 2024
c8f9036
feat: split mint burners into a separate changeset
ecPablo Dec 11, 2024
8a25dbc
fix: name TestMintLink on unit test
ecPablo Dec 11, 2024
fd9fab4
Update deployment/common/changeset/example/add_mint_burners_link.go
ecPablo Dec 12, 2024
4c68ec8
Update deployment/common/changeset/example/add_mint_burners_link.go
ecPablo Dec 12, 2024
9d855b5
Merge branch 'refs/heads/develop' into ecPablo/link-transfer-mcms
ecPablo Dec 12, 2024
9412602
fix: use changeset apply for unit tests environment setup
ecPablo Dec 12, 2024
6fd93b9
fix: linting errors
ecPablo Dec 12, 2024
f6596c4
Merge branch 'refs/heads/develop' into ecPablo/link-transfer-mcms
ecPablo Dec 12, 2024
f0aa7da
fix: merge conflicts
ecPablo Dec 12, 2024
7c2ecf0
fix: remove valid unit to reuse util for proposal creation
ecPablo Dec 13, 2024
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
102 changes: 102 additions & 0 deletions deployment/common/changeset/link_transfer_timelock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package changeset

import (
"fmt"
"math/big"
"time"

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

"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"

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

type LinkTransfer struct {
To common.Address
Value *big.Int
}
type LinkTransferTimelockRequest struct {
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
Transfers map[uint64][]LinkTransfer
ValidUntil uint32 // unix time until the proposal will be valid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akhilchainani do we usually specify this or can it just be standardized i.e. now() + 24hr?

MinDelay time.Duration // delay for timelock worker to execute the transfers.
OverrideRoot bool
StartingOpCount map[uint64]uint64
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
}
ecPablo marked this conversation as resolved.
Show resolved Hide resolved

var _ deployment.ChangeSet[*LinkTransferTimelockRequest] = LinkTransferTimelock

// LinkTransferTimelock takes the given link transfers and creates an MCMS proposal for them.
func LinkTransferTimelock(e deployment.Environment, req *LinkTransferTimelockRequest) (deployment.ChangesetOutput, error) {
chainMetadata := map[mcms.ChainIdentifier]mcms.ChainMetadata{}
timelockAddresses := map[mcms.ChainIdentifier]common.Address{}
allBatches := []timelock.BatchChainOperation{}
for chainSelector := range req.Transfers {
chainID := mcms.ChainIdentifier(chainSelector)
chain := e.Chains[chainSelector]
addrs, err := e.ExistingAddresses.AddressesForChain(chainSelector)
linkState, err := LoadLinkTokenState(chain, addrs)
if err != nil {
return deployment.ChangesetOutput{}, err
}
linkAddress := linkState.LinkToken.Address()
mcmsState, err := LoadMCMSWithTimelockState(chain, addrs)
if err != nil {
return deployment.ChangesetOutput{}, err
}
mcmAddress := mcmsState.ProposerMcm.Address()
timelockAddress := mcmsState.Timelock.Address()

linkContract, err := link_token.NewLinkToken(linkAddress, chain.Client)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to get link contract: %w", err)
}
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
chainMetadata[chainID] = mcms.ChainMetadata{
MCMAddress: mcmAddress,
StartingOpCount: req.StartingOpCount[chainSelector],
}
timelockAddresses[chainID] = timelockAddress
batch := timelock.BatchChainOperation{
ChainIdentifier: chainID,
Batch: []mcms.Operation{},
}

for _, transfer := range req.Transfers[chainSelector] {
tx, err := linkContract.Transfer(deployment.SimTransactOpts(), transfer.To, transfer.Value)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("error packing transfer tx data: %w", err)
}
op := mcms.Operation{
To: linkAddress,
Data: tx.Data(),
Value: big.NewInt(0),
ContractType: "LinkToken",
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
}
batch.Batch = append(batch.Batch, op)

}
allBatches = append(allBatches, batch)
}

proposal, err := timelock.NewMCMSWithTimelockProposal(
"1",
req.ValidUntil,
[]mcms.Signature{},
req.OverrideRoot,
chainMetadata,
timelockAddresses,
"Value transfer proposal",
allBatches,
timelock.Schedule,
req.MinDelay.String(),
)
if err != nil {
return deployment.ChangesetOutput{}, err
}
return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{*proposal},
}, nil

}
124 changes: 124 additions & 0 deletions deployment/common/changeset/link_transfer_timelock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package changeset_test

import (
"context"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"

"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"

"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"
)

// TestLinkTransferTimelock tests the LinkTransferTimelock changeset by
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
func TestLinkTransferTimelock(t *testing.T) {
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
t.Parallel()
ctx := context.Background()
lggr := logger.Test(t)
cfg := memory.MemoryEnvironmentConfig{
Nodes: 1,
Chains: 2,
}
env := memory.NewMemoryEnvironment(t, lggr, zapcore.DebugLevel, cfg)
chainSelector := env.AllChainSelectors()[0]
chain := env.Chains[chainSelector]
// Deploy Value Token
resp, err := changeset.DeployLinkToken(env, []uint64{chainSelector})
require.NoError(t, err)
require.NotNil(t, resp)
require.NoError(t, env.ExistingAddresses.Merge(resp.AddressBook))

// Deploy MCMS and Timelock
config := changeset.SingleGroupMCMS(t)
respTimelock, err := changeset.DeployMCMSWithTimelock(env, map[uint64]types.MCMSWithTimelockConfig{
chainSelector: {
Canceller: config,
Bypasser: config,
Proposer: config,
TimelockExecutors: []common.Address{chain.DeployerKey.From},
TimelockMinDelay: big.NewInt(0),
},
})
require.NoError(t, env.ExistingAddresses.Merge(respTimelock.AddressBook))
require.NoError(t, err)
ecPablo marked this conversation as resolved.
Show resolved Hide resolved

addrs, err := env.ExistingAddresses.AddressesForChain(chainSelector)
require.NoError(t, err)
require.Len(t, addrs, 5)

mcmsState, err := changeset.LoadMCMSWithTimelockState(chain, addrs)
require.NoError(t, err)
linkState, err := changeset.LoadLinkTokenState(chain, addrs)
require.NoError(t, err)
linkAddress := linkState.LinkToken.Address()
timelockAddress := mcmsState.Timelock.Address()

linkContract, err := link_token.NewLinkToken(linkAddress, chain.Client)
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)

// Mint some funds
// grant minter permissions
tx, err := linkContract.GrantMintRole(chain.DeployerKey, chain.DeployerKey.From)
require.NoError(t, err)
_, err = deployment.ConfirmIfNoError(chain, tx, err)
require.NoError(t, err)

tx, err = linkContract.Mint(chain.DeployerKey, timelockAddress, big.NewInt(750))
require.NoError(t, err)
_, err = deployment.ConfirmIfNoError(chain, tx, err)
require.NoError(t, err)

timelockContract, err := gethwrappers.NewRBACTimelock(timelockAddress, chain.Client)
ecPablo marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)
timelocks := map[uint64]*gethwrappers.RBACTimelock{
chainSelector: timelockContract,
}

// Apply the changeset
_, err = changeset.ApplyChangesets(t, env, timelocks, []changeset.ChangesetApplication{
// the changeset produces proposals, ApplyChangesets will sign & execute them.
// in practice, signing and executing are separated processes.
{
Changeset: changeset.WrapChangeSet(changeset.LinkTransferTimelock),
Config: &changeset.LinkTransferTimelockRequest{
Transfers: map[uint64][]changeset.LinkTransfer{
chainSelector: {
{
To: chain.DeployerKey.From,
Value: big.NewInt(500),
},
},
},
ValidUntil: 4131638958,
MinDelay: 0,
OverrideRoot: true,
StartingOpCount: map[uint64]uint64{
chainSelector: 0,
},
},
},
})
require.NoError(t, err)

// Check new balances
endBalance, err := linkContract.BalanceOf(&bind.CallOpts{Context: ctx}, chain.DeployerKey.From)
require.NoError(t, err)
expectedBalance := big.NewInt(500)
require.Equal(t, expectedBalance, endBalance)

// check timelock balance
endBalance, err = linkContract.BalanceOf(&bind.CallOpts{Context: ctx}, timelockAddress)
require.NoError(t, err)
expectedBalance = big.NewInt(250)
require.Equal(t, expectedBalance, endBalance)
}
2 changes: 1 addition & 1 deletion deployment/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func MaybeDataErr(err error) error {
var d rpc.DataError
ok := errors.As(err, &d)
if ok {
return d
return fmt.Errorf("%s: %v", d.Error(), d.ErrorData())
}
return err
}
Expand Down
Loading