diff --git a/bindings/compile/aptos_cli.go b/bindings/compile/aptos_cli.go new file mode 100644 index 000000000..79d0bcf5d --- /dev/null +++ b/bindings/compile/aptos_cli.go @@ -0,0 +1,56 @@ +package compile + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" +) + +const aptosCLIEnvVar = "APTOS_CLI" + +// resolveAptosCLI returns the Aptos Labs CLI binary used for Move compilation. +// APTOS_CLI overrides PATH lookup when set. +func resolveAptosCLI() (string, error) { + if cli := os.Getenv(aptosCLIEnvVar); cli != "" { + if err := assertExecutable(cli); err != nil { + return "", fmt.Errorf("%s=%q: %w", aptosCLIEnvVar, cli, err) + } + return cli, nil + } + + for _, dir := range filepath.SplitList(os.Getenv("PATH")) { + if dir == "" { + continue + } + candidate := filepath.Join(dir, "aptos") + if !isAptosCLI(candidate) { + continue + } + return candidate, nil + } + + return "", fmt.Errorf( + "aptos CLI not found on PATH; set %s to the binary path)", + aptosCLIEnvVar, + ) +} + +func assertExecutable(path string) error { + info, err := os.Stat(path) + if err != nil { + return err + } + if info.IsDir() { + return fmt.Errorf("not an executable file") + } + return nil +} + +func isAptosCLI(path string) bool { + if err := assertExecutable(path); err != nil { + return false + } + cmd := exec.Command(path, "move", "--help") + return cmd.Run() == nil +} diff --git a/bindings/compile/aptos_cli_test.go b/bindings/compile/aptos_cli_test.go new file mode 100644 index 000000000..dbbf580ab --- /dev/null +++ b/bindings/compile/aptos_cli_test.go @@ -0,0 +1,73 @@ +package compile + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func writeFakeCLI(t *testing.T, dir, name string, script string) string { + t.Helper() + path := filepath.Join(dir, name) + require.NoError(t, os.WriteFile(path, []byte(script), 0o755)) + return path +} + +func Test_resolveAptosCLI_APTOS_CLIOverride(t *testing.T) { + dir := t.TempDir() + cli := writeFakeCLI(t, dir, "aptos-labs", "#!/bin/sh\nexit 0\n") + + t.Setenv(aptosCLIEnvVar, cli) + t.Setenv("PATH", "") + + got, err := resolveAptosCLI() + require.NoError(t, err) + require.Equal(t, cli, got) +} + +func Test_resolveAptosCLI_APTOS_CLIOverride_missing(t *testing.T) { + t.Setenv(aptosCLIEnvVar, filepath.Join(t.TempDir(), "missing")) + t.Setenv("PATH", "") + + _, err := resolveAptosCLI() + require.Error(t, err) + require.Contains(t, err.Error(), aptosCLIEnvVar) +} + +func Test_resolveAptosCLI_PATHPrefersLabsCLI(t *testing.T) { + loopOnlyDir := t.TempDir() + writeFakeCLI(t, loopOnlyDir, "aptos", "#!/bin/sh\nif [ \"$1\" = move ]; then exit 1; fi\nexit 0\n") + labsAsAptosDir := t.TempDir() + writeFakeCLI(t, labsAsAptosDir, "aptos", "#!/bin/sh\nif [ \"$1\" = move ] && [ \"$2\" = --help ]; then exit 0; fi\nexit 1\n") + + t.Setenv(aptosCLIEnvVar, "") + t.Setenv("PATH", loopOnlyDir+string(filepath.ListSeparator)+labsAsAptosDir) + + got, err := resolveAptosCLI() + require.NoError(t, err) + require.Equal(t, filepath.Join(labsAsAptosDir, "aptos"), got) +} + +func Test_resolveAptosCLI_noValidCLI(t *testing.T) { + dir := t.TempDir() + writeFakeCLI(t, dir, "aptos", "#!/bin/sh\nexit 1\n") + + t.Setenv(aptosCLIEnvVar, "") + t.Setenv("PATH", dir) + + _, err := resolveAptosCLI() + require.Error(t, err) + require.Contains(t, err.Error(), aptosCLIEnvVar) +} + +func Test_isAptosLabsCLI(t *testing.T) { + dir := t.TempDir() + labs := writeFakeCLI(t, dir, "labs", "#!/bin/sh\nif [ \"$1\" = move ] && [ \"$2\" = --help ]; then exit 0; fi\nexit 1\n") + loop := writeFakeCLI(t, dir, "loop", "#!/bin/sh\nif [ \"$1\" = move ]; then exit 1; fi\nexit 0\n") + + require.True(t, isAptosCLI(labs)) + require.False(t, isAptosCLI(loop)) + require.False(t, isAptosCLI(filepath.Join(dir, "missing"))) +} diff --git a/bindings/compile/compile.go b/bindings/compile/compile.go index d0cb76360..3a127ef95 100644 --- a/bindings/compile/compile.go +++ b/bindings/compile/compile.go @@ -70,7 +70,12 @@ func CompilePackage(packageName contracts.Package, namedAddresses map[string]apt args = append(args, "--named-addresses", strings.Join(namedAddr, ",")) } - cmd := exec.Command("aptos", args...) + aptosBin, err := resolveAptosCLI() + if err != nil { + return CompiledPackage{}, err + } + + cmd := exec.Command(aptosBin, args...) cmd.Dir = packageRoot // Command is run in the temporary destination directory // Buffer stdErr and stdOut stdOut := &bytes.Buffer{} diff --git a/deployment/ccip/adapters/connect_chains.go b/deployment/ccip/adapters/connect_chains.go index 72da03e16..b345157c6 100644 --- a/deployment/ccip/adapters/connect_chains.go +++ b/deployment/ccip/adapters/connect_chains.go @@ -168,6 +168,7 @@ var ConfigureLaneLegAsDest = cldf_ops.NewSequence( IsEnabled: isEnabled, TestRouter: input.TestRouter, IsRMNVerificationDisabled: !input.Source.RMNVerificationEnabled, + OnRamp: input.Source.OnRamp, }, }, }) diff --git a/deployment/ccip/operation/off_ramp.go b/deployment/ccip/operation/off_ramp.go index faf989139..b52aaf4c7 100644 --- a/deployment/ccip/operation/off_ramp.go +++ b/deployment/ccip/operation/off_ramp.go @@ -7,17 +7,12 @@ import ( mcmstypes "github.com/smartcontractkit/mcms/types" "github.com/smartcontractkit/chainlink-aptos/bindings/ccip_offramp" - aptosutils "github.com/smartcontractkit/chainlink-aptos/relayer/utils" - "github.com/smartcontractkit/chainlink-deployments-framework/operations" "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/dependency" - "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/utils" "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/internal" + "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/utils" "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/v1_6" -) - -const ( - pluginTypeCCIPCommit uint8 = 0 - pluginTypeCCIPExec uint8 = 1 + aptosutils "github.com/smartcontractkit/chainlink-aptos/relayer/utils" + "github.com/smartcontractkit/chainlink-deployments-framework/operations" ) var OffRampOperations = []*operations.Operation[any, any, any]{ @@ -56,11 +51,10 @@ func updateOffRampSources(b operations.Bundle, deps dependency.AptosDeps, in Upd sourceChainEnabled = append(sourceChainEnabled, update.IsEnabled) sourceChainRMNVerificationDisabled = append(sourceChainRMNVerificationDisabled, update.IsRMNVerificationDisabled) - onRampBytes, err := deps.CCIPOnChainState.GetOnRampAddressBytes(sourceChainSelector) - if err != nil { - return nil, fmt.Errorf("failed to get onRamp address for source chain %d: %w", sourceChainSelector, err) + if len(update.OnRamp) == 0 { + return nil, fmt.Errorf("no onramp provided for source chain %d", sourceChainSelector) } - sourceChainOnRamp = append(sourceChainOnRamp, onRampBytes) + sourceChainOnRamp = append(sourceChainOnRamp, update.OnRamp) } if len(sourceChainSelectors) == 0 { diff --git a/deployment/ccip/v1_6/types.go b/deployment/ccip/v1_6/types.go index 56bc3f530..53d4da555 100644 --- a/deployment/ccip/v1_6/types.go +++ b/deployment/ccip/v1_6/types.go @@ -18,10 +18,10 @@ type ConnectionConfig struct { // ChainDefinition defines how a chain should be configured on both remote chains and itself. type ChainDefinition struct { ConnectionConfig `json:"connectionConfig"` - Selector uint64 `json:"selector"` - GasPrice *big.Int `json:"gasPrice"` - TokenPrices map[common.Address]*big.Int `json:"tokenPrices"` - FeeQuoterDestChainConfig fee_quoter.FeeQuoterDestChainConfig `json:"feeQuoterDestChainConfig"` + Selector uint64 `json:"selector"` + GasPrice *big.Int `json:"gasPrice"` + TokenPrices map[common.Address]*big.Int `json:"tokenPrices"` + FeeQuoterDestChainConfig fee_quoter.FeeQuoterDestChainConfig `json:"feeQuoterDestChainConfig"` } type OnRampDestinationUpdate struct { @@ -39,11 +39,12 @@ type OffRampSourceUpdate struct { IsEnabled bool TestRouter bool IsRMNVerificationDisabled bool + OnRamp []byte } type UpdateOffRampSourcesConfig struct { - UpdatesByChain map[uint64]map[uint64]OffRampSourceUpdate - MCMS *cldfproposalutils.TimelockConfig + UpdatesByChain map[uint64]map[uint64]OffRampSourceUpdate + MCMS *cldfproposalutils.TimelockConfig SkipOwnershipCheck bool } diff --git a/deployment/stateview/state.go b/deployment/stateview/state.go index 4b902a615..6bf08520b 100644 --- a/deployment/stateview/state.go +++ b/deployment/stateview/state.go @@ -5,7 +5,6 @@ import ( "github.com/aptos-labs/aptos-go-sdk" chainselectors "github.com/smartcontractkit/chain-selectors" - "github.com/ethereum/go-ethereum/common" cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" @@ -47,9 +46,6 @@ func (c CCIPOnChainState) GetOnRampAddressBytes(chainSelector uint64) ([]byte, e return nil, fmt.Errorf("no ccip address found in the state for Aptos chain %d", chainSelector) } return ccipAddress[:], nil - case chainselectors.FamilyEVM: - // For integration tests, EVM onramp bytes may be supplied via mock addresses in config. - return common.HexToAddress("0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59").Bytes(), nil default: return nil, fmt.Errorf("unsupported chain family for onramp lookup: %s", family) } diff --git a/integration-tests/deployment/ccip/cs_update_aptos_lanes_test.go b/integration-tests/deployment/ccip/cs_update_aptos_lanes_test.go index f0218de96..b92a583ef 100644 --- a/integration-tests/deployment/ccip/cs_update_aptos_lanes_test.go +++ b/integration-tests/deployment/ccip/cs_update_aptos_lanes_test.go @@ -21,11 +21,11 @@ import ( _ "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v1_6_0/sequences" deployops "github.com/smartcontractkit/chainlink-ccip/deployment/deploy" "github.com/smartcontractkit/chainlink-ccip/deployment/lanes" - "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" cs_ccip "github.com/smartcontractkit/chainlink-ccip/deployment/utils/changesets" + "github.com/smartcontractkit/chainlink-ccip/deployment/utils/mcms" - "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/shared" _ "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/adapters" + "github.com/smartcontractkit/chainlink-aptos/deployment/ccip/shared" "github.com/smartcontractkit/chainlink-aptos/deployment/stateview" "github.com/smartcontractkit/chainlink-aptos/integration-tests/deployment/testutil" ) @@ -135,6 +135,21 @@ func TestUpdateAptosLanes(t *testing.T) { require.NoError(t, err) require.True(t, isSupported) + evmAdapter, ok := lanesRegistry.GetLaneAdapter(chain_selectors.FamilyEVM, toolingAPIVersion) + require.True(t, ok) + evmOnRampBytes, err := evmAdapter.GetOnRampAddress(env.DataStore, evmSelector1) + require.NoError(t, err) + evmOnRampBytes2, err := evmAdapter.GetOnRampAddress(env.DataStore, evmSelector2) + require.NoError(t, err) + + srcCfg1, err := aptosOffRamp.Offramp().GetSourceChainConfig(&bind.CallOpts{}, evmSelector1) + require.NoError(t, err) + require.Equal(t, evmOnRampBytes, srcCfg1.OnRamp) + + srcCfg2, err := aptosOffRamp.Offramp().GetSourceChainConfig(&bind.CallOpts{}, evmSelector2) + require.NoError(t, err) + require.Equal(t, evmOnRampBytes2, srcCfg2.OnRamp) + _, _, router, routerState, err := aptosOnRamp.Onramp().GetDestChainConfigV2(&bind.CallOpts{}, evmSelector1) require.NoError(t, err) require.NotEqual(t, aptos.AccountAddress{}, router)