Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7075610
cre: finish aptos local CRE read and write setup parity
cawthorne Mar 28, 2026
d8bdcc0
test: use known aptos chain id in health fixture
cawthorne Mar 28, 2026
3a7fa0c
chore: trim aptos branch pin carryover
cawthorne Mar 29, 2026
f917c51
go: drop stale aptos checksum entries
cawthorne Mar 29, 2026
ec1e8f0
cre: bump aptos ledger version before read smoke
cawthorne Mar 30, 2026
727d145
deps: restore aptos relayer ledger version pin
cawthorne Mar 30, 2026
32864c5
test: drop aptos health fixture tweak
cawthorne Mar 30, 2026
28c4b2d
test: keep aptos health fixture on localnet chain id
cawthorne Mar 30, 2026
aebe98f
tests: align aptos local and ci coverage
cawthorne Mar 30, 2026
7a7f416
lint: fix aptos refactor formatting and shadowing
cawthorne Mar 30, 2026
d76523b
chore: trim aptos pr collateral changes
cawthorne Mar 30, 2026
2d2b730
docs: restore generated aptos dependency graph
cawthorne Mar 30, 2026
7e52ae2
test: drop unrelated ccip nonce tweak
cawthorne Mar 30, 2026
2804116
aptos: pin consensus to main and keep CI read-only
cawthorne Mar 30, 2026
f440911
docs: clarify aptos metadata fallback
cawthorne Mar 30, 2026
d8a4e15
tests: tighten aptos workflow names and helpers
cawthorne Mar 30, 2026
00c2969
chore: address aptos review comments
cawthorne Mar 30, 2026
190626b
refactor: use framework aptos account helper
cawthorne Mar 30, 2026
d04ec38
merge develop into aptos local cre branch
cawthorne Mar 30, 2026
c94d6c4
chore: tidy framework aptos helper sums
cawthorne Mar 30, 2026
2f813dd
test: simplify aptos scenario constants
cawthorne Mar 30, 2026
b15e7ea
fix: scope OCR signer families per capability
cawthorne Mar 30, 2026
9aaf1b5
style: fix aptos import grouping
cawthorne Mar 30, 2026
bad2208
test: migrate aptos write to capreg ocr config
cawthorne Mar 31, 2026
2d0c4aa
fix: address aptos lint issues
cawthorne Mar 31, 2026
680c73e
refactor: remove unused aptos forwarder placeholder
cawthorne Mar 31, 2026
6fbda29
test: run aptos ci on roundtrip and expected failure
cawthorne Mar 31, 2026
fdea90f
docs: clarify aptos capability config defaults
cawthorne Mar 31, 2026
fc68c50
docs: consolidate aptos changeset notes
cawthorne Mar 31, 2026
8c79697
refactor: align aptos capability naming
cawthorne Mar 31, 2026
f988108
feat: make imported aptos keys chain-aware
cawthorne Mar 31, 2026
959d292
Merge origin/develop into feature/aptos-local-cre-minimal-write-refre…
cawthorne Mar 31, 2026
8f941c7
docs: clarify imported aptos key adapter
cawthorne Mar 31, 2026
a7259f1
test: regenerate aptos config mocks
cawthorne Mar 31, 2026
61b58f1
refactor: drop read contract aptos residue
cawthorne Mar 31, 2026
b8c1d30
test: speed up aptos cre suite
cawthorne Apr 1, 2026
b8c4566
fix: address aptos lint issues
cawthorne Apr 1, 2026
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
7 changes: 7 additions & 0 deletions .changeset/aptos-local-cre-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"chainlink": patch
---

#internal

Add Aptos local CRE read/write support, including Capabilities Registry OCR config for Aptos write and CI coverage for the Aptos write roundtrip and expected-failure scenarios.
32 changes: 32 additions & 0 deletions .github/workflows/cre-system-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ jobs:

# Add list of tests with certain topologies
PER_TEST_TOPOLOGIES_JSON=${PER_TEST_TOPOLOGIES_JSON:-'{
"Test_CRE_V2_Aptos_Suite": [
{"topology":"workflow-gateway-aptos","configs":"configs/workflow-gateway-don-aptos.toml"}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how did you choose the topology, what is workflow-gateway-aptos, should we define a new topologu like for Solana?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I kept workflow-gateway-aptos here. This topology does include a gateway, and the Aptos-specific label helps distinguish it from the existing EVM-oriented workflow-gateway variants rather than implying a brand new topology family.

],
"Test_CRE_V2_Solana_Suite": [
{"topology":"workflow","configs":"configs/workflow-don-solana.toml"}
],
Expand Down Expand Up @@ -213,6 +216,35 @@ jobs:
chmod +x bin/ctf
echo "::endgroup::"

- name: Install Aptos CLI
if: ${{ matrix.tests.test_name == 'Test_CRE_V2_Aptos_Suite' }}
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APTOS_CLI_TAG: "aptos-cli-v7.8.0"
run: |
echo "::startgroup::Install Aptos CLI"
bin_dir="$HOME/.local/bin"
mkdir -p "$bin_dir"

gh release download "${APTOS_CLI_TAG}" \
--pattern "aptos-cli-*-Ubuntu-24.04-x86_64.zip" \
--clobber \
--repo aptos-labs/aptos-core \
-O aptos-cli.zip

unzip -o aptos-cli.zip -d aptos-cli-extract >/dev/null
aptos_path="$(find aptos-cli-extract -type f -name aptos | head -n1)"
if [[ -z "$aptos_path" ]]; then
echo "failed to locate aptos binary in release archive"
exit 1
fi

install -m 0755 "$aptos_path" "$bin_dir/aptos"
echo "$bin_dir" >> "$GITHUB_PATH"
"$bin_dir/aptos" --version
echo "::endgroup::"

- name: Start local CRE${{ matrix.tests.cre_version }}
shell: bash
id: start-local-cre
Expand Down
13 changes: 13 additions & 0 deletions core/cmd/shell_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,19 @@ func (s *Shell) runNode(c *cli.Context) error {
}
}
if s.Config.AptosEnabled() {
for _, k := range s.Config.ImportedAptosKeys().List() {
lggr.Debug("Importing aptos key")
_, err2 := app.GetKeyStore().Aptos().Import(rootCtx, []byte(k.JSON()), k.Password())
if err2 != nil {
if errors.Is(err2, keystore.ErrKeyExists) {
lggr.Debugf("Aptos key %s already exists for chain %v", k.JSON(), k.ChainDetails())
continue
}
return s.errorOut(fmt.Errorf("error importing aptos key: %w", err2))
}
lggr.Debugf("Imported aptos key %s for chain %v", k.JSON(), k.ChainDetails())
}

err2 := app.GetKeyStore().Aptos().EnsureKey(rootCtx)
if err2 != nil {
return fmt.Errorf("failed to ensure aptos key: %w", err2)
Expand Down
90 changes: 90 additions & 0 deletions core/config/toml/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ type Secrets struct {
Threshold ThresholdKeyShareSecrets `toml:",omitempty"`
EVM EthKeys `toml:",omitempty"` // choose EVM as the TOML field name to align with relayer config convention
Solana SolKeys `toml:",omitempty"` // choose Solana as the TOML field name to align with relayer config convention
Aptos AptosKeys `toml:",omitempty"` // choose Aptos as the TOML field name to align with relayer config convention

P2PKey P2PKey `toml:",omitempty"`
DKGRecipientKey DKGRecipientKey `toml:",omitempty"`
Expand All @@ -164,6 +165,16 @@ type SolKey struct {
Password *models.Secret
}

type AptosKeys struct {
Keys []*AptosKey
}

type AptosKey struct {
JSON *models.Secret
ID *uint64
Password *models.Secret
}

func (s *SolKeys) SetFrom(f *SolKeys) error {
err := s.validateMerge(f)
if err != nil {
Expand Down Expand Up @@ -245,6 +256,85 @@ func (e *SolKey) ValidateConfig() (err error) {
return err
}

func (a *AptosKeys) SetFrom(f *AptosKeys) error {
err := a.validateMerge(f)
if err != nil {
return err
}
if f == nil || len(f.Keys) == 0 {
return nil
}
a.Keys = make([]*AptosKey, len(f.Keys))
copy(a.Keys, f.Keys)
return nil
}

func (a *AptosKeys) validateMerge(f *AptosKeys) (err error) {
have := make(map[uint64]struct{})
if a != nil && f != nil {
for _, aptosKey := range a.Keys {
have[*aptosKey.ID] = struct{}{}
}
for _, aptosKey := range f.Keys {
if _, ok := have[*aptosKey.ID]; ok {
err = errors.Join(err, configutils.ErrOverride{Name: fmt.Sprintf("AptosKeys: %d", *aptosKey.ID)})
}
}
}
return err
}

func (a *AptosKeys) ValidateConfig() (err error) {
for i, aptosKey := range a.Keys {
if err2 := aptosKey.ValidateConfig(); err2 != nil {
err = errors.Join(err, configutils.ErrInvalid{Name: fmt.Sprintf("AptosKeys[%d]", i), Value: aptosKey, Msg: "invalid AptosKey"})
}
}
return err
}

func (p *AptosKey) SetFrom(f *AptosKey) (err error) {
err = p.validateMerge(f)
if err != nil {
return err
}
if v := f.JSON; v != nil {
p.JSON = v
}
if v := f.ID; v != nil {
p.ID = v
}
if v := f.Password; v != nil {
p.Password = v
}
return nil
}

func (p *AptosKey) validateMerge(f *AptosKey) (err error) {
if p.JSON != nil && f.JSON != nil {
err = errors.Join(err, configutils.ErrOverride{Name: "JSON"})
}
if p.ID != nil && f.ID != nil {
err = errors.Join(err, configutils.ErrOverride{Name: "ID"})
}
if p.Password != nil && f.Password != nil {
err = errors.Join(err, configutils.ErrOverride{Name: "Password"})
}
return err
}

func (p *AptosKey) ValidateConfig() (err error) {
if (p.JSON != nil) != (p.Password != nil) || (p.Password != nil) != (p.ID != nil) {
err = errors.Join(err, configutils.ErrInvalid{Name: "AptosKey", Value: p.JSON, Msg: "all fields must be nil or non-nil"})
}
if p.ID != nil {
if _, ok := chain_selectors.AptosChainIdToChainSelector()[*p.ID]; !ok {
err = errors.Join(err, configutils.ErrInvalid{Name: "ID", Value: p.ID, Msg: "invalid chain id"})
}
}
return err
}

type EthKeys struct {
Keys []*EthKey
}
Expand Down
11 changes: 11 additions & 0 deletions core/scripts/cre/environment/configs/capability_defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@
# FromAddress = "0x0000000000000000000000000000000000000000"
# ForwarderAddress = "0x0000000000000000000000000000000000000000"

# Aptos chain capability plugin (View + WriteReport). Runtime values are injected per chain.
[capability_configs.aptos]
binary_name = "aptos"

[capability_configs.aptos.values]
# These values build the Aptos capability config registered in CapReg.
# ChainID and forwarder address are injected separately at job proposal time.
# They are not emitted as top-level worker job-spec fields.
RequestTimeout = "30s"
DeltaStage = "1500ms"

[capability_configs.solana.values]
TxAcceptanceState = 3
TxRetentonTimeout = "120s"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Same as workflow-gateway-don.toml but with Aptos chain and a single Aptos capability.
# Anvil 1337: registry and gateway. Aptos: local devnet (chain_id 4). Run: env config path <this file>, then env start.

[[blockchains]]
type = "anvil"
chain_id = "1337"
container_name = "anvil-1337"
docker_cmd_params = ["-b", "0.5", "--mixed-mining"]

[[blockchains]]
type = "aptos"
chain_id = "4"

[jd]
csa_encryption_key = "d1093c0060d50a3c89c189b2e485da5a3ce57f3dcb38ab7e2c0d5f0bb2314a44"
# change to your version
image = "job-distributor:0.22.1"

#[s3provider]
# # use all defaults
# port = 9000
# console_port = 9001

[infra]
# either "docker" or "kubernetes"
type = "docker"

[[nodesets]]
nodes = 4
name = "workflow"
don_types = ["workflow"]
override_mode = "all"
http_port_range_start = 10100

# Keep the registry chain (1337) in node TOML even though this DON's chain-scoped
# capability is Aptos-only; the workflow stack still needs the EVM registry chain.
supported_evm_chains = [1337]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what does this do for aptos?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

let me respond to this one: since this don doesn't have any capability that runs on chain 1337 that's the way to explicitly instruct the code to weave 1337 RPC in node's TOML config. We need it since that's the registry chain and workflow DON needs to access it.

# Workflow runtime limits still need both the EVM registry chain and the Aptos chain selector.
env_vars = { CL_CRE_SETTINGS_DEFAULT = '{"PerWorkflow":{"CapabilityCallTimeout":"5m0s","ChainAllowed":{"Default":"false","Values":{"1337":"true","4457093679053095497":"true"}}}}' }
capabilities = ["cron", "consensus", "aptos-4"]
registry_based_launch_allowlist = ["cron-trigger@1.0.0"]

[nodesets.db]
image = "postgres:12.0"
port = 13000

[[nodesets.node_specs]]
roles = ["plugin"]
[nodesets.node_specs.node]
docker_ctx = "../../../.."
docker_file = "core/chainlink.Dockerfile"
docker_build_args = { "CL_IS_PROD_BUILD" = "false" }
# image = "chainlink-tmp:latest"
user_config_overrides = ""

[[nodesets]]
nodes = 1
name = "bootstrap-gateway"
don_types = ["bootstrap", "gateway"]
override_mode = "each"
http_port_range_start = 10300

supported_evm_chains = [1337]

[nodesets.db]
image = "postgres:12.0"
port = 13200

[[nodesets.node_specs]]
roles = ["bootstrap", "gateway"]
[nodesets.node_specs.node]
docker_ctx = "../../../.."
docker_file = "core/chainlink.Dockerfile"
docker_build_args = { "CL_IS_PROD_BUILD" = "false" }
# 5002 is the web API capabilities port for incoming requests
# 15002 is the vault port for incoming requests
custom_ports = ["5002:5002", "15002:15002"]
# image = "chainlink-tmp:latest"
user_config_overrides = ""
10 changes: 9 additions & 1 deletion core/scripts/cre/environment/environment/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,16 @@ func startCmd() *cobra.Command {
}

features := feature_set.New()
extraAllowedPorts := append([]int(nil), extraAllowedGatewayPorts...)
if in.Fake != nil {
extraAllowedPorts = append(extraAllowedPorts, in.Fake.Port)
}
if in.FakeHTTP != nil {
extraAllowedPorts = append(extraAllowedPorts, in.FakeHTTP.Port)
}

gatewayWhitelistConfig := gateway.WhitelistConfig{
ExtraAllowedPorts: append(extraAllowedGatewayPorts, in.Fake.Port, in.FakeHTTP.Port),
ExtraAllowedPorts: extraAllowedPorts,
ExtraAllowedIPsCIDR: []string{"0.0.0.0/0"},
}
output, startErr := StartCLIEnvironment(cmdContext, relativePathToRepoRoot, in, nil, features, nil, envDependencies, gatewayWhitelistConfig)
Expand Down
5 changes: 3 additions & 2 deletions core/scripts/cre/environment/mock/trigger_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
"time"

"github.com/google/uuid"
cron2 "github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron"
"google.golang.org/protobuf/types/known/anypb"

crontypedapi "github.com/smartcontractkit/chainlink-common/pkg/capabilities/v2/triggers/cron"

pb2 "github.com/smartcontractkit/chainlink/system-tests/lib/cre/mock/pb"
)

Expand All @@ -24,7 +25,7 @@ func getTriggerRequest(triggerType TriggerType) (*pb2.SendTriggerEventRequest, e
switch triggerType {
case TriggerTypeCron:
// First create the payload
payload := &cron2.LegacyPayload{ //nolint:staticcheck // legacy
payload := &crontypedapi.LegacyPayload{ //nolint:staticcheck // legacy
ScheduledExecutionTime: time.Now().Format(time.RFC3339Nano),
}

Expand Down
8 changes: 3 additions & 5 deletions core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,12 @@ require (
github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20260119171452-39c98c3b33cd
github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260326111235-8c09d1a4491f
github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0
github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.8
github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.9-0.20260330164022-15e89dd1431f
github.com/smartcontractkit/chainlink-testing-framework/framework/components/dockercompose v0.1.20
github.com/smartcontractkit/chainlink-testing-framework/lib v1.54.5
github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5
github.com/smartcontractkit/chainlink/core/scripts/cre/environment/examples/workflows/v2/proof-of-reserve/cron-based v0.0.0-00010101000000-000000000000
github.com/smartcontractkit/chainlink/system-tests/lib v0.0.0-20251020210257-0a6ec41648b4
github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.3.0
github.com/smartcontractkit/chainlink/system-tests/lib v0.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
Expand Down Expand Up @@ -483,7 +482,7 @@ require (
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/smartcontractkit/ccip-contract-examples/chains/evm v0.0.0-20260129135848-c86808ba5cb9 // indirect
github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect
github.com/smartcontractkit/chainlink-aptos v0.0.0-20260318173523-755cafb24200 // indirect
github.com/smartcontractkit/chainlink-aptos v0.0.0-20260324144720-484863604698 // indirect
github.com/smartcontractkit/chainlink-ccip/ccv/chains/evm v0.0.0-20260323224438-d819cb3228e1 // indirect
github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment v0.0.0-20260317185256-d5f7db87ae70 // indirect
github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260310183131-8d0f0e383288 // indirect
Expand Down Expand Up @@ -519,7 +518,6 @@ require (
github.com/smartcontractkit/chainlink-ton v0.0.0-20260331005855-7b5a4b3384f8 // indirect
github.com/smartcontractkit/chainlink-ton/deployment v0.0.0-20260326230916-bcfdbe85f221 // indirect
github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20260218133534-cbd44da2856b // indirect
github.com/smartcontractkit/cre-sdk-go v1.5.0 // indirect
github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect
github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect
github.com/smartcontractkit/mcms v0.38.2 // indirect
Expand Down
10 changes: 4 additions & 6 deletions core/scripts/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions core/services/chainlink/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ func (s *Secrets) SetFrom(f *Secrets) (err error) {
err = errors.Join(err, commonconfig.NamedMultiErrorList(err2, "Solana"))
}

if err2 := s.Aptos.SetFrom(&f.Aptos); err2 != nil {
err = errors.Join(err, commonconfig.NamedMultiErrorList(err2, "Aptos"))
}

if err2 := s.DKGRecipientKey.SetFrom(&f.DKGRecipientKey); err2 != nil {
err = errors.Join(err, commonconfig.NamedMultiErrorList(err2, "DKGRecipientKey"))
}
Expand Down
Loading
Loading