-
Notifications
You must be signed in to change notification settings - Fork 3
Extend cldf timelock converter logic for TON #628
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
Changes from 29 commits
3261478
ce292f9
76f7941
6b03e06
bb6622b
564f880
fa6aa6c
580e5c7
d139579
e0493eb
8c9b9ff
b703c08
a68db89
d440c1d
060c95e
4a13173
5f800a9
02d0258
588016c
3d3a6ba
95337bb
c434940
781a16b
284e77e
2e6c227
06dd941
4168b5e
120f23b
d58f919
ad23d0e
8dcbd7a
022e0df
075de4e
e5efbd0
b25e6bc
42d488c
ced4bfd
62b9727
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -13,7 +13,6 @@ import ( | |||||
| "github.com/avast/retry-go/v4" | ||||||
| "github.com/testcontainers/testcontainers-go" | ||||||
|
|
||||||
| "github.com/xssnick/tonutils-go/address" | ||||||
| "github.com/xssnick/tonutils-go/tlb" | ||||||
| "github.com/xssnick/tonutils-go/ton" | ||||||
| "github.com/xssnick/tonutils-go/ton/wallet" | ||||||
|
|
@@ -34,6 +33,9 @@ const ( | |||||
|
|
||||||
| // supportedTONImageRepository is the only supported Docker image repository for TON localnet. | ||||||
| supportedTONImageRepository = "ghcr.io/neodix42/mylocalton-docker" | ||||||
|
|
||||||
| // defaultTxTONAmount is the default amount of TON to use for transactions. | ||||||
| defaultTxTONAmount = "0.25" | ||||||
| ) | ||||||
|
|
||||||
| // CTFChainProviderConfig holds the configuration to initialize the CTFChainProvider. | ||||||
|
|
@@ -126,18 +128,13 @@ func (p *CTFChainProvider) Initialize(ctx context.Context) (chain.BlockChain, er | |||||
| return nil, fmt.Errorf("failed to create wallet: %w", err) | ||||||
| } | ||||||
|
|
||||||
| // airdrop the deployer wallet | ||||||
| ferr := fundTonWallets(ctx, nodeClient, []*address.Address{tonWallet.Address()}, []tlb.Coins{tlb.MustFromTON("1000")}) | ||||||
| if ferr != nil { | ||||||
| return nil, fmt.Errorf("failed to fund wallet: %w", ferr) | ||||||
| } | ||||||
|
Comment on lines
-129
to
-133
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Omitting fund logic from network setup makes sense to me(if I am reading it right), but just a reminder that integration tests in core and chainlink-ton are relying on the deployer wallet to fund transmitters:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, think we removed this as we discussed that the integration tests already funded the deployer wallet |
||||||
|
|
||||||
| p.chain = &cldf_ton.Chain{ | ||||||
| ChainMetadata: cldf_ton.ChainMetadata{Selector: p.selector}, | ||||||
| Client: nodeClient, | ||||||
|
huangzhen1997 marked this conversation as resolved.
|
||||||
| Wallet: tonWallet, | ||||||
| WalletAddress: tonWallet.Address(), | ||||||
| WalletAddress: tonWallet.WalletAddress(), | ||||||
|
||||||
| WalletAddress: tonWallet.WalletAddress(), | |
| WalletAddress: tonWallet.Address(), |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ import ( | |
| "strings" | ||
|
|
||
| "github.com/xssnick/tonutils-go/liteclient" | ||
| "github.com/xssnick/tonutils-go/tlb" | ||
| tonlib "github.com/xssnick/tonutils-go/ton" | ||
| "github.com/xssnick/tonutils-go/ton/wallet" | ||
|
|
||
|
|
@@ -173,17 +174,23 @@ func (p *RPCChainProvider) Initialize(ctx context.Context) (chain.BlockChain, er | |
| return nil, err | ||
| } | ||
|
|
||
| p.chain = &ton.Chain{ | ||
| p.chain = buildChain(p.selector, api, tonWallet, p.config.HTTPURL) | ||
|
|
||
| return *p.chain, nil | ||
| } | ||
|
|
||
| // buildChain creates a ton.Chain with the given parameters and default amount. | ||
| func buildChain(selector uint64, api *tonlib.APIClient, tonWallet *wallet.Wallet, httpURL string) *ton.Chain { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what's the purpose of this method, which just instantiates the type vs just doing it without the method? Then we also have a |
||
| return &ton.Chain{ | ||
| ChainMetadata: ton.ChainMetadata{ | ||
| Selector: p.selector, | ||
| Selector: selector, | ||
| }, | ||
| Client: api, | ||
| Wallet: tonWallet, | ||
| WalletAddress: tonWallet.WalletAddress(), | ||
| URL: p.config.HTTPURL, | ||
| URL: httpURL, | ||
| Amount: tlb.MustFromTON(defaultTxTONAmount), | ||
|
huangzhen1997 marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| return *p.chain, nil | ||
| } | ||
|
|
||
| // createLiteclientConnectionPool creates connection pool returning concrete type for production use | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| "github.com/stretchr/testify/assert" | ||||||||||||||||||||||||||||
| "github.com/stretchr/testify/require" | ||||||||||||||||||||||||||||
| "github.com/xssnick/tonutils-go/ton/wallet" | ||||||||||||||||||||||||||||
|
huangzhen1997 marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| tonchain "github.com/smartcontractkit/chainlink-deployments-framework/chain/ton" | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
|
|
@@ -89,6 +90,22 @@ | |||||||||||||||||||||||||||
| assert.Equal(t, existingChain.Selector, gotChain.Selector) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| func Test_RPCChainProvider_Initialize_InvalidConfig(t *testing.T) { | ||||||||||||||||||||||||||||
| t.Parallel() | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| p := &RPCChainProvider{ | ||||||||||||||||||||||||||||
| selector: 123, | ||||||||||||||||||||||||||||
| config: RPCChainProviderConfig{ | ||||||||||||||||||||||||||||
| HTTPURL: "", // invalid - missing URL | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
| HTTPURL: "", // invalid - missing URL | |
| HTTPURL: "", // invalid: empty URL to test validation of missing/empty configuration |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test uses a predictable private key pattern (sequential bytes 0-31). While acceptable for testing, consider using a more explicit construction like privateKey := [32]byte{0, 1, 2, ...} or a helper function to make the intent clearer and avoid the loop.
| // Create a test wallet using a fixed private key | |
| privateKey := make([]byte, 32) | |
| for i := range privateKey { | |
| privateKey[i] = byte(i) | |
| } | |
| // Create a test wallet using a fixed private key with explicit bytes | |
| privateKeyArray := [32]byte{ | |
| 0, 1, 2, 3, 4, 5, 6, 7, | |
| 8, 9, 10, 11, 12, 13, 14, 15, | |
| 16, 17, 18, 19, 20, 21, 22, 23, | |
| 24, 25, 26, 27, 28, 29, 30, 31, | |
| } | |
| privateKey := privateKeyArray[:] |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -5,6 +5,8 @@ import ( | |||
| "github.com/xssnick/tonutils-go/ton" | ||||
| "github.com/xssnick/tonutils-go/ton/wallet" | ||||
|
|
||||
| "github.com/xssnick/tonutils-go/tlb" | ||||
|
|
||||
| "github.com/smartcontractkit/chainlink-deployments-framework/chain/internal/common" | ||||
| ) | ||||
|
|
||||
|
|
@@ -17,4 +19,5 @@ type Chain struct { | |||
| Wallet *wallet.Wallet // Wallet abstraction (signing, sending) | ||||
|
||||
| Wallet *wallet.Wallet // Wallet abstraction (signing, sending) |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,6 +28,7 @@ import ( | |||||||||
| "github.com/smartcontractkit/mcms/sdk/evm/bindings" | ||||||||||
| "github.com/smartcontractkit/mcms/sdk/solana" | ||||||||||
| "github.com/smartcontractkit/mcms/sdk/sui" | ||||||||||
| "github.com/smartcontractkit/mcms/sdk/ton" | ||||||||||
| "github.com/smartcontractkit/mcms/types" | ||||||||||
| "github.com/spf13/cobra" | ||||||||||
| "github.com/spf13/pflag" | ||||||||||
|
|
@@ -1129,6 +1130,8 @@ func newCfgv2(lggr logger.Logger, cmd *cobra.Command, domain cldf_domain.Domain, | |||||||||
| if err != nil { | ||||||||||
| return nil, fmt.Errorf("error creating Sui timelock converter: %w", err) | ||||||||||
| } | ||||||||||
| case chainsel.FamilyTon: | ||||||||||
| converter = ton.NewTimelockConverter(ton.DefaultSendAmount) | ||||||||||
|
huangzhen1997 marked this conversation as resolved.
Outdated
huangzhen1997 marked this conversation as resolved.
Outdated
huangzhen1997 marked this conversation as resolved.
Outdated
|
||||||||||
| default: | ||||||||||
| return nil, fmt.Errorf("unsupported chain family %s", fam) | ||||||||||
| } | ||||||||||
|
|
@@ -1441,18 +1444,18 @@ func getExecutorWithChainOverride(cfg *cfgv2, chainSelector types.ChainSelector) | |||||||||
| if !ok { | ||||||||||
| return nil, fmt.Errorf("invalid encoder type: %T", encoder) | ||||||||||
| } | ||||||||||
| chain := cfg.blockchains.EVMChains()[uint64(chainSelector)] | ||||||||||
| c := cfg.blockchains.EVMChains()[uint64(chainSelector)] | ||||||||||
|
gustavogama-cll marked this conversation as resolved.
|
||||||||||
|
|
||||||||||
| return evm.NewExecutor(evmEncoder, chain.Client, chain.DeployerKey), nil | ||||||||||
| return evm.NewExecutor(evmEncoder, c.Client, c.DeployerKey), nil | ||||||||||
|
|
||||||||||
| case chainsel.FamilySolana: | ||||||||||
| solanaEncoder, ok := encoder.(*solana.Encoder) | ||||||||||
| if !ok { | ||||||||||
| return nil, fmt.Errorf("invalid encoder type: %T", encoder) | ||||||||||
| } | ||||||||||
| chain := cfg.blockchains.SolanaChains()[uint64(chainSelector)] | ||||||||||
| c := cfg.blockchains.SolanaChains()[uint64(chainSelector)] | ||||||||||
|
|
||||||||||
| return solana.NewExecutor(solanaEncoder, chain.Client, *chain.DeployerKey), nil | ||||||||||
| return solana.NewExecutor(solanaEncoder, c.Client, *c.DeployerKey), nil | ||||||||||
|
|
||||||||||
| case chainsel.FamilyAptos: | ||||||||||
| encoder, ok := encoder.(*aptos.Encoder) | ||||||||||
|
|
@@ -1463,9 +1466,9 @@ func getExecutorWithChainOverride(cfg *cfgv2, chainSelector types.ChainSelector) | |||||||||
| if err != nil { | ||||||||||
| return nil, fmt.Errorf("error getting aptos role from proposal: %w", err) | ||||||||||
| } | ||||||||||
| chain := cfg.blockchains.AptosChains()[uint64(chainSelector)] | ||||||||||
| c := cfg.blockchains.AptosChains()[uint64(chainSelector)] | ||||||||||
|
|
||||||||||
| return aptos.NewExecutor(chain.Client, chain.DeployerSigner, encoder, *role), nil | ||||||||||
| return aptos.NewExecutor(c.Client, c.DeployerSigner, encoder, *role), nil | ||||||||||
|
|
||||||||||
| case chainsel.FamilySui: | ||||||||||
| encoder, ok := encoder.(*sui.Encoder) | ||||||||||
|
|
@@ -1476,10 +1479,24 @@ func getExecutorWithChainOverride(cfg *cfgv2, chainSelector types.ChainSelector) | |||||||||
| if err != nil { | ||||||||||
| return nil, fmt.Errorf("error getting sui metadata from proposal: %w", err) | ||||||||||
| } | ||||||||||
| chain := cfg.blockchains.SuiChains()[uint64(chainSelector)] | ||||||||||
| c := cfg.blockchains.SuiChains()[uint64(chainSelector)] | ||||||||||
| entrypointEncoder := suibindings.NewCCIPEntrypointArgEncoder(metadata.RegistryObj, metadata.DeployerStateObj) | ||||||||||
|
|
||||||||||
| return sui.NewExecutor(chain.Client, chain.Signer, encoder, entrypointEncoder, metadata.McmsPackageID, metadata.Role, cfg.timelockProposal.ChainMetadata[chainSelector].MCMAddress, metadata.AccountObj, metadata.RegistryObj, metadata.TimelockObj) | ||||||||||
| return sui.NewExecutor(c.Client, c.Signer, encoder, entrypointEncoder, metadata.McmsPackageID, metadata.Role, cfg.timelockProposal.ChainMetadata[chainSelector].MCMAddress, metadata.AccountObj, metadata.RegistryObj, metadata.TimelockObj) | ||||||||||
| case chainsel.FamilyTon: | ||||||||||
| encoder, ok := encoder.(*ton.Encoder) | ||||||||||
| if !ok { | ||||||||||
| return nil, fmt.Errorf("invalid encoder type for TON chain %d: expected *ton.Encoder, got %T", chainSelector, encoder) | ||||||||||
|
||||||||||
| return nil, fmt.Errorf("invalid encoder type for TON chain %d: expected *ton.Encoder, got %T", chainSelector, encoder) | |
| return nil, fmt.Errorf("invalid encoder type: %T", encoder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new amount is used only for for executor opts for MCMS/Timelock - I think this is the wrong place to configure it, as part of chain.Amount, and especially if this is non-configurable static default.
I suggest that:
- We remove the
c.Amountfrom the chain type - Define these two configs statically in this PR - estimated amounts for MCMS/Timelock execution
0.1 TON(should be enough) - Defer gas estimation to the gas estimator component when it's implemented, then integrate it here instead of static values
-
- should support ad-hoc user overrides somehow (e.g., don't use default gas estimator but this specific value)
Just having this c.Amount set statically here is more noise than value imo, so rather remove it.
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TON executor creation in getExecutorWithChainOverride lacks test coverage. Add test cases to verify the executor is created correctly with the expected options, including the Amount field set to defaultTONExecutorAmount.
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TON timelock executor creation in getTimelockExecutorWithChainOverride lacks test coverage. Add test cases to verify the timelock executor is created correctly with the expected options, similar to other chain family test cases.
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable 'chain' should be renamed to 'c' for consistency with the naming pattern used in the same function for other chain families (lines 1535, 1539, 1542, 1545).
| chain := cfg.blockchains.TonChains()[cfg.chainSelector] | |
| inspector = ton.NewInspector(chain.Client) | |
| c := cfg.blockchains.TonChains()[cfg.chainSelector] | |
| inspector = ton.NewInspector(c.Client) |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TON inspector creation in getInspectorFromChainSelector lacks test coverage. Add test cases to verify the inspector is created correctly for TON chains.
Uh oh!
There was an error while loading. Please reload this page.