From 51fcf313e4a712c0818e75a8c773f2628a93e487 Mon Sep 17 00:00:00 2001 From: Yvonne Zhang Date: Sun, 3 Aug 2025 21:54:18 -0700 Subject: [PATCH 1/2] Fix broken build and add useful scripts --- .github/workflows/ci.yml | 22 +- Makefile | 4 +- package.json | 5 +- packages/aptos_extensions/Move.toml | 2 +- packages/stablecoin/Move.toml | 2 +- renovate.json | 17 ++ scripts/shell/setup.sh | 60 ++-- scripts/typescript/decodeTransaction.ts | 170 ++++++++++++ scripts/typescript/deploy.ts | 90 ++++++ scripts/typescript/executeTransaction.ts | 137 ++++++++++ scripts/typescript/index.ts | 6 + .../typescript/upgradeStablecoinPackage.ts | 6 +- scripts/typescript/utils/index.ts | 8 +- test/typescript/decodeTransaction.test.ts | 258 ++++++++++++++++++ test/typescript/deploy.test.ts | 103 +++++++ test/typescript/executeTransaction.test.ts | 192 +++++++++++++ test/typescript/testUtils.ts | 17 +- yarn.lock | 36 +-- 18 files changed, 1074 insertions(+), 61 deletions(-) create mode 100644 renovate.json create mode 100644 scripts/typescript/decodeTransaction.ts create mode 100644 scripts/typescript/deploy.ts create mode 100644 scripts/typescript/executeTransaction.ts create mode 100644 test/typescript/decodeTransaction.test.ts create mode 100644 test/typescript/deploy.test.ts create mode 100644 test/typescript/executeTransaction.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88ad383..86364c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,17 @@ jobs: with: node-version: "20.14.0" + - name: Get Yarn cache directory path + id: yarn-cache-dir-path + run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT + + - name: Retrieve yarn cache + uses: actions/cache@v4 + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + restore-keys: ${{ runner.os }}-yarn- + - name: Setup CI Environment run: make setup @@ -44,19 +55,16 @@ jobs: - name: Run move tests run: make test - - name: Run move prover - run: | - export BOOGIE_EXE=/home/runner/.local/bin/boogie - export CVC5_EXE=/home/runner/.local/bin/cvc5 - export Z3_EXE=/home/runner/.local/bin/z3 - make prove - - name: Start network run: make start-network - name: Run Typescript tests run: yarn test + - name: Stop network + if: ${{ always() }} + run: make stop-network + scan: if: github.event_name == 'pull_request' uses: circlefin/circle-public-github-workflows/.github/workflows/pr-scan.yaml@v1 diff --git a/Makefile b/Makefile index 7c45505..9cdc223 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,12 @@ # === Move compiler settings === # Refer to https://github.com/aptos-labs/aptos-core/blob/687937182b30f32895d13e4384a96e03f569468c/third_party/move/move-model/src/metadata.rs#L80 -compiler_version = 1 +compiler_version = 2 # Refer to https://github.com/aptos-labs/aptos-core/blob/687937182b30f32895d13e4384a96e03f569468c/third_party/move/move-model/src/metadata.rs#L179 # NOTE: The bytecode_version used during compilation is auto-inferred from this setting. Refer to https://github.com/aptos-labs/aptos-core/blob/687937182b30f32895d13e4384a96e03f569468c/third_party/move/move-model/src/metadata.rs#L235-L241 # for more information. -language_version = 1 +language_version = 2 setup: @bash scripts/shell/setup.sh diff --git a/package.json b/package.json index 2936cad..8d62fbb 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "license": "Apache-2.0", "homepage": "https://github.com/circlefin/stablecoin-aptos#readme", "devDependencies": { - "@aptos-labs/ts-sdk": "1.29.1", + "@aptos-labs/ts-sdk": "1.33.0", "@types/mocha": "10.0.6", "@types/sinon": "17.0.3", "@typescript-eslint/eslint-plugin": "7.11.0", @@ -38,5 +38,6 @@ "engines": { "node": "20.14.0", "yarn": "1.x.x" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/packages/aptos_extensions/Move.toml b/packages/aptos_extensions/Move.toml index 50c28e3..c29cf48 100644 --- a/packages/aptos_extensions/Move.toml +++ b/packages/aptos_extensions/Move.toml @@ -13,7 +13,7 @@ deployer = "0xaa03675e2e13043f3d591804b0d8a8ffb775d14b11ca0831866ecdf48df26713" [dependencies.AptosFramework] git = "https://github.com/aptos-labs/aptos-core.git" -rev = "mainnet" +rev = "1381c93fd5a656f16fb326d4ffe371947554a330" subdir = "aptos-move/framework/aptos-framework" [dev-dependencies] diff --git a/packages/stablecoin/Move.toml b/packages/stablecoin/Move.toml index ca0bd82..27a0a95 100644 --- a/packages/stablecoin/Move.toml +++ b/packages/stablecoin/Move.toml @@ -5,7 +5,7 @@ upgrade_policy = "compatible" [dependencies.AptosFramework] git = "https://github.com/aptos-labs/aptos-core.git" -rev = "mainnet" +rev = "1381c93fd5a656f16fb326d4ffe371947554a330" subdir = "aptos-move/framework/aptos-framework" [dependencies.AptosExtensions] diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..b6202f7 --- /dev/null +++ b/renovate.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "github>circlefin/renovate-config", + "github>circlefin/renovate-config:versions.sh" + ], + "regexManagers": [ + { + "fileMatch": ["scripts/shell/setup.sh"], + "matchStrings": [ + "export (?.*?)_VERSION=\"?(?[^\"\\t\\n\\r]*)\"? +# (?.*?):(?[^\\s]*)( versioning=(?[^\\s]+))?( extractVersion=(?[^\\s]+))?", + "export (?.*?)_TAG=\"?(?[^\"\\t\\n\\r]*)\"? +# (?.*?):(?[^\\s]*)( versioning=(?[^\\s]+))?( extractVersion=(?[^\\s]+))?" + ], + "depNameTemplate": "{{{ replace '_' '-' (lowercase depName) }}}" + } + ] +} diff --git a/scripts/shell/setup.sh b/scripts/shell/setup.sh index e16d809..6cb0835 100644 --- a/scripts/shell/setup.sh +++ b/scripts/shell/setup.sh @@ -16,34 +16,50 @@ # See the License for the specific language governing permissions and # limitations under the License. +set -e + +if [[ "$CI" == true ]] +then + OS="Ubuntu-22.04-x86_64" +else + OS="macOS-arm64" +fi + echo ">> Setting up environment" + # ==== Aptos installation ==== -APTOS_CLI_VERSION="4.2.6" +APTOS_CLI_VERSION="7.5.0" +APTOS_BIN="${APTOS_BIN:-$HOME/.aptos/bin}" -if [ "$CI" == true ]; then - curl -sSfL -o /tmp/aptos.zip "https://github.com/aptos-labs/aptos-core/releases/download/aptos-cli-v$APTOS_CLI_VERSION/aptos-cli-$APTOS_CLI_VERSION-Ubuntu-22.04-x86_64.zip" - sudo unzip /tmp/aptos.zip -d /usr/local/bin - sudo chmod +x /usr/local/bin/* -else - if [ "$(brew ls --versions aptos)" != "aptos $APTOS_CLI_VERSION" ]; then - brew uninstall --force aptos && \ - # aptos 4.2.6's formula - curl -s -o aptos.rb https://raw.githubusercontent.com/Homebrew/homebrew-core/ef458cb0a2574eb7d451090cbedc3942b77a7284/Formula/a/aptos.rb - brew install --formula aptos.rb - brew pin aptos - rm aptos.rb +if ! command -v aptos &> /dev/null || ! aptos -V | grep -q "aptos $APTOS_CLI_VERSION" +then + echo "Installing Aptos binary from Github..." + echo ">> Version: '$APTOS_CLI_VERSION'" + echo ">> OS: '$OS'" + + # Download and extract Aptos binaries. + rm -rf "$APTOS_BIN" + mkdir -p "$APTOS_BIN" + curl -L -o "$APTOS_BIN/aptos-v$APTOS_CLI_VERSION.zip" "https://github.com/aptos-labs/aptos-core/releases/download/aptos-cli-v$APTOS_CLI_VERSION/aptos-cli-$APTOS_CLI_VERSION-$OS.zip" + unzip -o "$APTOS_BIN/aptos-v$APTOS_CLI_VERSION.zip" -d "$APTOS_BIN" + rm "$APTOS_BIN/aptos-v$APTOS_CLI_VERSION.zip" + + # Sanity check that the Aptos binary was installed correctly + echo "Checking aptos installation..." + if ! "$APTOS_BIN/aptos" -V | grep -q "aptos $APTOS_CLI_VERSION" + then + echo "Aptos binary was not installed correctly" + exit 1 fi -fi -# ==== Movefmt & Move prover installation ==== -if [ -z $APTOS_BIN ] -then - aptos update movefmt - aptos update prover-dependencies -else - aptos update movefmt --install-dir $APTOS_BIN - aptos update prover-dependencies --install-dir $APTOS_BIN + if [[ "$CI" == true ]] + then + echo "$APTOS_BIN" >> $GITHUB_PATH + else + echo " Aptos binary installed successfully. Run the following command to add 'aptos' to your shell" + echo " echo 'export PATH=\"$APTOS_BIN:\$PATH\"' >> ~/.zshrc" + fi fi diff --git a/scripts/typescript/decodeTransaction.ts b/scripts/typescript/decodeTransaction.ts new file mode 100644 index 0000000..58a6cf6 --- /dev/null +++ b/scripts/typescript/decodeTransaction.ts @@ -0,0 +1,170 @@ +/** + * Copyright 2024 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + AccountAddress, + Deserializable, + Deserializer, + EntryFunctionArgument, + EntryFunctionBytes, + FixedBytes, + Hex, + MoveVector, + Serializable, + SimpleTransaction, + TransactionPayloadEntryFunction, + U8 +} from "@aptos-labs/ts-sdk"; +import { program } from "commander"; +import fs from "fs"; +import path from "path"; +import { REPOSITORY_ROOT } from "./utils"; + +export default program + .createCommand("decode-transaction") + .description("Decodes transaction bytes into a human-readable format.") + .requiredOption( + "--tx-bytes ", + "Hex-encoded, BCS-serialized transaction" + ) + .requiredOption("-o, --output ", "The output file path.") + .option( + "--function-name ", + "The name of the function that is being decoded. Required for argument decoding." + ) + .action(async (options) => { + await decodeTransaction(options); + }); + +export async function decodeTransaction({ + txBytes, + output, + functionName +}: { + txBytes: string; + output: string; + functionName?: string; +}) { + // Deserialize the transaction bytes into a SimpleTransaction. + const tx = SimpleTransaction.deserialize( + new Deserializer(Hex.fromHexString(txBytes).toUint8Array()) + ); + + if (!(tx.rawTransaction.payload instanceof TransactionPayloadEntryFunction)) { + throw new Error( + "Only transactions with entry function payloads are supported." + ); + } + + const txPayload = tx.rawTransaction.payload.entryFunction; + + // Attempt to decode the arguments for the function. + const txPayloadArguments = txPayload.args as EntryFunctionBytes[]; + const isArgDecoderSupportedForFunction = + functionName != null && + Object.keys(argumentDecoders).includes(functionName); + + if (!isArgDecoderSupportedForFunction) { + console.log( + `NOTE: Argument decoding is either disabled or is unsupported for the entry function called. The decoded transaction will return the arguments as its raw BCS-serialized bytes.` + ); + } + + const args = isArgDecoderSupportedForFunction + ? txPayloadArguments.map((arg, i) => + argumentDecoders[functionName][i](arg.value) + ) + : txPayloadArguments.map((arg) => arg.bcsToHex().toString()); + + // Decode the transaction. + const decodedTx = { + sender: tx.rawTransaction.sender.toString(), + sequenceNumber: tx.rawTransaction.sequence_number.toString(), + maxGasAmount: tx.rawTransaction.max_gas_amount.toString(), + gasUnitPrice: tx.rawTransaction.gas_unit_price.toString(), + expirationTimestampSecs: + tx.rawTransaction.expiration_timestamp_secs.toString(), + chainId: tx.rawTransaction.chain_id.chainId.toString(), + feePayer: tx.feePayerAddress?.toString(), + payload: { + function: `${txPayload.module_name.address.toString()}::${txPayload.module_name.name.identifier}::${txPayload.function_name.identifier}`, + typeArgs: txPayload.type_args.map((tyArg) => tyArg.toString()), + args + } + }; + + // Write the decoded transaction to a file. + const outputFilePath = path.join(REPOSITORY_ROOT, output); + console.log( + `\u001b[32mTransaction successfully decoded and saved to: '${outputFilePath}'\u001b[0m` + ); + fs.writeFileSync(outputFilePath, JSON.stringify(decodedTx, null, 2)); +} + +// ==== Argument Decoders ==== +const argumentDecoders: Record any)[]> = { + // aptos_extensions::upgradable::upgrade_package + upgradePackage: [ + // resource_acct: address, + (arg: FixedBytes) => + AccountAddress.deserialize(new Deserializer(arg.value)).toString(), + + // metadata_serialized: vector, + (arg: FixedBytes) => { + const vector = MoveVector.deserialize(new Deserializer(arg.value), U8); + return convertMoveVectorU8ToHex(vector); + }, + + // code: vector> + (arg: FixedBytes) => { + const vector = TwoLevelMoveVector.deserialize( + new Deserializer(arg.value), + U8 + ); + return vector.values.map(convertMoveVectorU8ToHex); + } + ] +}; + +/** + * Referenced from {@link https://github.com/aptos-labs/aptos-ts-sdk/blob/34539a96eb51c2605f759ec3615624a8489988e9/src/bcs/serializable/moveStructs.ts#L289-L319} + */ +class TwoLevelMoveVector { + static deserialize( + deserializer: Deserializer, + cls: Deserializable + ): MoveVector> { + const length = deserializer.deserializeUleb128AsU32(); + const values: Array> = []; + for (let i = 0; i < length; i += 1) { + values.push(MoveVector.deserialize(deserializer, cls)); + } + return new MoveVector(values); + } +} + +/** + * Converts a MoveVector into a hex string. + * + * @param moveVector - The MoveVector to convert. + * @returns The hex string representation of the MoveVector. + */ +function convertMoveVectorU8ToHex(moveVector: MoveVector) { + const byteArray = Uint8Array.from(moveVector.values.map((u8) => u8.value)); + return `0x${Buffer.from(byteArray).toString("hex")}`; +} diff --git a/scripts/typescript/deploy.ts b/scripts/typescript/deploy.ts new file mode 100644 index 0000000..63e9a9c --- /dev/null +++ b/scripts/typescript/deploy.ts @@ -0,0 +1,90 @@ +/** + * Copyright 2024 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk"; +import { program } from "commander"; +import { getAptosClient, waitForUserConfirmation } from "./utils"; +import { + publishPackageToResourceAccount, + NamedAddress, + parseNamedAddresses +} from "./utils/deployUtils"; + +export default program + .createCommand("deploy") + .description("Builds and deploys a package") + .argument("", "Name of package to deploy") + .requiredOption("-r, --rpc-url ", "Network RPC URL") + .requiredOption("--deployer-key ", "Deployer private key") + .requiredOption( + "--seed ", + "The seed for calculating the deployment address" + ) + .requiredOption( + "--named-deps ", + "Named dependency addresses of the deployed package." + ) + .option( + "--verify-source", + "Whether source code verification is enabled", + false + ) + .action(async (packageName, options) => { + const namedDeps = parseNamedAddresses(options.namedDeps); + await deploy(packageName, { ...options, namedDeps }); + }); + +export async function deploy( + packageName: string, + { + rpcUrl, + deployerKey, + seed, + namedDeps, + verifySource + }: { + rpcUrl: string; + deployerKey: string; + seed: string; + namedDeps: NamedAddress[]; + verifySource?: boolean; + } +) { + const aptos = getAptosClient(rpcUrl); + + const deployer = Account.fromPrivateKey({ + privateKey: new Ed25519PrivateKey(deployerKey) + }); + console.log(`Deployer account: ${deployer.accountAddress}`); + + console.log(`Publishing package ${packageName}...`); + if (!(await waitForUserConfirmation())) { + process.exit(1); + } + + const [packageId] = await publishPackageToResourceAccount({ + aptos, + deployer, + packageName, + namedDeps, + seed: new Uint8Array(Buffer.from(seed)), + verifySource: !!verifySource + }); + + console.log(`Deployed package to ${packageId}`); +} diff --git a/scripts/typescript/executeTransaction.ts b/scripts/typescript/executeTransaction.ts new file mode 100644 index 0000000..a3a6bb9 --- /dev/null +++ b/scripts/typescript/executeTransaction.ts @@ -0,0 +1,137 @@ +/** + * Copyright 2024 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + AccountAuthenticator, + AccountAuthenticatorEd25519, + AccountAuthenticatorMultiKey, + Deserializer, + Ed25519PublicKey, + Ed25519Signature, + Hex, + MultiKey, + MultiKeySignature, + PublicKey, + SimpleTransaction +} from "@aptos-labs/ts-sdk"; +import { program } from "commander"; +import { inspect } from "util"; +import { getAptosClient } from "./utils"; + +export default program + .createCommand("execute-transaction") + .description("Executes a transaction") + .requiredOption("-r, --rpc-url ", "Network RPC URL") + .requiredOption( + "--tx-bytes ", + "Hex-encoded, BCS-serialized transaction" + ) + .requiredOption( + "--public-key ", + "Hex-encoded, BCS-serialized public key for the sender of the transaction. Multi-sig accounts must have the keys serialized as a MultiKey." + ) + .option( + "--signature ", + "Hex-encoded, BCS-serialized signature. Multi-sig accounts must have the signatures serialized as a MultiKeySignature. Required if --dry-run is unset" + ) + .option("--multi-sig", "Use multi-sig mode for the transaction sender if set") + .option("--dry-run", "Dry runs the transaction if set") + .action(async (options) => { + await executeTransaction(options); + }); + +export async function executeTransaction({ + rpcUrl, + txBytes, + publicKey, + signature, + multiSig, + dryRun +}: { + rpcUrl: string; + txBytes: string; + publicKey: string; + signature?: string; + multiSig?: boolean; + dryRun?: boolean; +}) { + const aptos = getAptosClient(rpcUrl); + + const transaction = SimpleTransaction.deserialize( + new Deserializer(Hex.fromHexString(txBytes).toUint8Array()) + ); + + let signerPublicKey: PublicKey; + if (multiSig) { + signerPublicKey = MultiKey.deserialize( + new Deserializer(Hex.fromHexString(publicKey).toUint8Array()) + ); + } else { + signerPublicKey = Ed25519PublicKey.deserialize( + new Deserializer(Hex.fromHexString(publicKey).toUint8Array()) + ); + } + + if (dryRun) { + console.log("Dry running transaction..."); + + const result = await aptos.transaction.simulate.simple({ + transaction, + signerPublicKey + }); + + console.log(inspect(result, false, 8, true)); + return result; + } else { + console.log("Executing transaction..."); + + if (signature == null) { + throw new Error("Missing required signature for transaction execution!"); + } + + let senderAuthenticator: AccountAuthenticator; + + if (multiSig) { + senderAuthenticator = new AccountAuthenticatorMultiKey( + signerPublicKey as MultiKey, + MultiKeySignature.deserialize( + new Deserializer(Hex.fromHexString(signature).toUint8Array()) + ) + ); + } else { + senderAuthenticator = new AccountAuthenticatorEd25519( + signerPublicKey as Ed25519PublicKey, + Ed25519Signature.deserialize( + new Deserializer(Hex.fromHexString(signature).toUint8Array()) + ) + ); + } + + const initialTxOutput = await aptos.transaction.submit.simple({ + transaction, + senderAuthenticator + }); + + const result = await aptos.waitForTransaction({ + transactionHash: initialTxOutput.hash + }); + + console.log(inspect(result, false, 8, true)); + return result; + } +} diff --git a/scripts/typescript/index.ts b/scripts/typescript/index.ts index 5bf443b..0100e02 100644 --- a/scripts/typescript/index.ts +++ b/scripts/typescript/index.ts @@ -24,7 +24,10 @@ import calculateDeploymentAddresses from "./calculateDeploymentAddresses"; import changeAdmin from "./changeAdmin"; import configureController from "./configureController"; import configureMinter from "./configureMinter"; +import decodeTransaction from "./decodeTransaction"; +import deploy from "./deploy"; import deployAndInitializeToken from "./deployAndInitializeToken"; +import executeTransaction from "./executeTransaction"; import generateKeypair from "./generateKeypair"; import removeController from "./removeController"; import removeMinter from "./removeMinter"; @@ -47,7 +50,10 @@ program .addCommand(changeAdmin) .addCommand(configureController) .addCommand(configureMinter) + .addCommand(decodeTransaction) + .addCommand(deploy) .addCommand(deployAndInitializeToken) + .addCommand(executeTransaction) .addCommand(generateKeypair) .addCommand(removeController) .addCommand(removeMinter) diff --git a/scripts/typescript/upgradeStablecoinPackage.ts b/scripts/typescript/upgradeStablecoinPackage.ts index dde6d23..39f46a9 100644 --- a/scripts/typescript/upgradeStablecoinPackage.ts +++ b/scripts/typescript/upgradeStablecoinPackage.ts @@ -17,7 +17,6 @@ */ import { Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk"; import { program } from "commander"; -import { inspect } from "util"; import { AptosExtensionsPackage } from "./packages/aptosExtensionsPackage"; import { getAptosClient, @@ -73,10 +72,7 @@ export async function upgradeStablecoinPackage({ console.log(`Admin account: ${admin.accountAddress}`); const payload = readPublishPayload(payloadFilePath); - console.log( - "Updating package using payload", - inspect(payload, false, 8, true) - ); + console.log(`Updating package using payload from ${payloadFilePath}`); if (!(await waitForUserConfirmation())) { process.exit(1); diff --git a/scripts/typescript/utils/index.ts b/scripts/typescript/utils/index.ts index 9f4457d..d283f63 100644 --- a/scripts/typescript/utils/index.ts +++ b/scripts/typescript/utils/index.ts @@ -131,7 +131,8 @@ export function getEventByType( * Reformat address to ensure it conforms to AIP-40. */ export function normalizeAddress(address: string): string { - return AccountAddress.from(address).toString(); + const paddedAddress = `0x${address.replace("0x", "").padStart(64, "0")}`; + return AccountAddress.from(paddedAddress).toString(); } /** @@ -201,8 +202,9 @@ export function yupAptosAddress() { export function isAptosAddress(value: any) { return ( - typeof value === "string" && - AccountAddress.isValid({ input: value, strict: true }).valid + !value || + (typeof value === "string" && + AccountAddress.isValid({ input: value, strict: true }).valid) ); } diff --git a/test/typescript/decodeTransaction.test.ts b/test/typescript/decodeTransaction.test.ts new file mode 100644 index 0000000..225bc01 --- /dev/null +++ b/test/typescript/decodeTransaction.test.ts @@ -0,0 +1,258 @@ +/** + * Copyright 2024 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + AccountAddress, + Ed25519Account, + MoveFunctionId, + MoveVector, + TransactionPayloadEntryFunction +} from "@aptos-labs/ts-sdk"; +import { strict as assert } from "assert"; +import { randomBytes } from "crypto"; +import fs from "fs"; +import sinon, { SinonStub } from "sinon"; +import { decodeTransaction } from "../../scripts/typescript/decodeTransaction"; +import { deployAndInitializeToken } from "../../scripts/typescript/deployAndInitializeToken"; +import { generateKeypair } from "../../scripts/typescript/generateKeypair"; +import { getAptosClient, LOCAL_RPC_URL } from "../../scripts/typescript/utils"; +import { TokenConfig } from "../../scripts/typescript/utils/tokenConfig"; + +describe("decodeTransaction", () => { + const DECODED_TX_OUTPUT_FILEPATH = "path/to/decoded_transaction.json"; + + const aptos = getAptosClient(LOCAL_RPC_URL); + + let deployer: Ed25519Account; + let aptosExtensionsPackageId: string; + let stablecoinPackageId: string; + + let writeFileSyncStub: SinonStub; + + before(async () => { + deployer = await generateKeypair({ prefund: true }); + const tokenConfig: TokenConfig = { + name: "USDC", + symbol: "USDC", + decimals: 6, + iconUri: "https://circle.com/usdc-icon", + projectUri: "https://circle.com/usdc", + + admin: deployer.accountAddress.toString(), + blocklister: deployer.accountAddress.toString(), + masterMinter: deployer.accountAddress.toString(), + metadataUpdater: deployer.accountAddress.toString(), + owner: deployer.accountAddress.toString(), + pauser: deployer.accountAddress.toString(), + controllers: {}, + minters: {} + }; + + const testTokenConfigPath = "path/to/token_config.json"; + + // Temporarily stub the fs methods for the deploy script. + const existsSyncStub = sinon.stub(fs, "existsSync"); + existsSyncStub.callThrough(); + existsSyncStub.withArgs(testTokenConfigPath).returns(true); + + const readFileSyncStub = sinon.stub(fs, "readFileSync"); + readFileSyncStub.callThrough(); + readFileSyncStub + .withArgs(testTokenConfigPath) + .returns(JSON.stringify(tokenConfig)); + + ({ aptosExtensionsPackageId, stablecoinPackageId } = + await deployAndInitializeToken({ + deployerKey: deployer.privateKey.toString(), + rpcUrl: LOCAL_RPC_URL, + verifySource: true, + tokenConfigPath: testTokenConfigPath + })); + + sinon.restore(); + }); + + beforeEach(() => { + writeFileSyncStub = sinon.stub(fs, "writeFileSync"); + }); + + afterEach(() => { + sinon.restore(); + }); + + async function fixture(feePayer?: Ed25519Account) { + const txInputs = { + functionTarget: + `${aptosExtensionsPackageId}::upgradable::upgrade_package` as MoveFunctionId, + rawFunctionArguments: [ + // resource_acct: address, + stablecoinPackageId, + // metadata_serialized: vector, + `0x${randomBytes(100).toString("hex")}`, + // code: vector> + [ + `0x${randomBytes(100).toString("hex")}`, + `0x${randomBytes(100).toString("hex")}`, + `0x${randomBytes(100).toString("hex")}`, + `0x${randomBytes(100).toString("hex")}`, + `0x${randomBytes(100).toString("hex")}` + ] + ] as [string, string, string[]], + + gasUnitPrice: 500, + maxGasAmount: 1_000_000, + sequenceNumber: 20, + expireTimestamp: Date.now(), + + sender: deployer, + feePayer + }; + + // Build the transaction based on the inputs. + const transaction = await aptos.transaction.build.simple({ + data: { + function: txInputs.functionTarget, + functionArguments: [ + AccountAddress.fromStrict(txInputs.rawFunctionArguments[0]), + MoveVector.U8(txInputs.rawFunctionArguments[1]), + new MoveVector(txInputs.rawFunctionArguments[2].map(MoveVector.U8)) + ] + }, + sender: txInputs.sender.accountAddress, + options: { + gasUnitPrice: txInputs.gasUnitPrice, + maxGasAmount: txInputs.maxGasAmount, + accountSequenceNumber: txInputs.sequenceNumber, + expireTimestamp: txInputs.expireTimestamp + }, + withFeePayer: txInputs.feePayer != null + }); + + // Simulate signing the transaction, as the TS SDK manipulates the + // transaction when signing. Discard the signatures + // since it is not necessary for the test. + aptos.transaction.sign({ + signer: txInputs.sender, + transaction + }); + + if (txInputs.feePayer) { + aptos.transaction.signAsFeePayer({ + signer: txInputs.feePayer, + transaction + }); + } + + const txBytes = transaction.bcsToHex().toString(); + + const rawTxPayloadArgs = ( + transaction.rawTransaction.payload as TransactionPayloadEntryFunction + ).entryFunction.args; + + return { + txInputs, + txBytes, + rawTxPayloadArgs, + chainId: await aptos.getChainId() + }; + } + + it("should successfully decode a transaction and its payload's arguments", async () => { + const functionName = "upgradePackage"; + const { txInputs, txBytes, chainId } = await fixture(); + + await decodeTransaction({ + txBytes, + output: DECODED_TX_OUTPUT_FILEPATH, + functionName + }); + + sinon.assert.calledOnce(writeFileSyncStub); + + const decodedTx = JSON.parse(writeFileSyncStub.getCall(0).args[1]); + assert.deepEqual(decodedTx, { + sender: txInputs.sender.accountAddress.toString(), + sequenceNumber: txInputs.sequenceNumber.toString(), + maxGasAmount: txInputs.maxGasAmount.toString(), + gasUnitPrice: txInputs.gasUnitPrice.toString(), + expirationTimestampSecs: txInputs.expireTimestamp.toString(), + chainId: chainId.toString(), + payload: { + function: txInputs.functionTarget, + typeArgs: [], + args: txInputs.rawFunctionArguments + } + }); + }); + + it("should successfully decode a transaction with fee payer", async () => { + const functionName = "upgradePackage"; + const feePayer = await generateKeypair({ prefund: true }); + const { txInputs, txBytes, chainId } = await fixture(feePayer); + + await decodeTransaction({ + txBytes, + output: DECODED_TX_OUTPUT_FILEPATH, + functionName + }); + + sinon.assert.calledOnce(writeFileSyncStub); + + const decodedTx = JSON.parse(writeFileSyncStub.getCall(0).args[1]); + assert.deepEqual(decodedTx, { + sender: txInputs.sender.accountAddress.toString(), + feePayer: feePayer.accountAddress.toString(), + sequenceNumber: txInputs.sequenceNumber.toString(), + maxGasAmount: txInputs.maxGasAmount.toString(), + gasUnitPrice: txInputs.gasUnitPrice.toString(), + expirationTimestampSecs: txInputs.expireTimestamp.toString(), + chainId: chainId.toString(), + payload: { + function: txInputs.functionTarget, + typeArgs: [], + args: txInputs.rawFunctionArguments + } + }); + }); + + it("should successfully decode a transaction with no argument decoder support", async () => { + const { txInputs, txBytes, rawTxPayloadArgs, chainId } = await fixture(); + + await decodeTransaction({ + txBytes, + output: DECODED_TX_OUTPUT_FILEPATH + }); + + sinon.assert.calledOnce(writeFileSyncStub); + + const decodedTx = JSON.parse(writeFileSyncStub.getCall(0).args[1]); + assert.deepEqual(decodedTx, { + sender: txInputs.sender.accountAddress.toString(), + sequenceNumber: txInputs.sequenceNumber.toString(), + maxGasAmount: txInputs.maxGasAmount.toString(), + gasUnitPrice: txInputs.gasUnitPrice.toString(), + expirationTimestampSecs: txInputs.expireTimestamp.toString(), + chainId: chainId.toString(), + payload: { + function: txInputs.functionTarget, + typeArgs: [], + args: rawTxPayloadArgs.map((arg) => arg.bcsToHex().toString()) + } + }); + }); +}); diff --git a/test/typescript/deploy.test.ts b/test/typescript/deploy.test.ts new file mode 100644 index 0000000..9b5f91d --- /dev/null +++ b/test/typescript/deploy.test.ts @@ -0,0 +1,103 @@ +/** + * Copyright 2024 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createResourceAddress, Ed25519Account } from "@aptos-labs/ts-sdk"; +import { strict as assert } from "assert"; +import { deploy } from "../../scripts/typescript/deploy"; +import { generateKeypair } from "../../scripts/typescript/generateKeypair"; +import { + getAptosClient, + getPackageMetadata, + LOCAL_FAUCET_URL, + LOCAL_RPC_URL +} from "../../scripts/typescript/utils"; +import { validateSourceCodeExistence } from "./testUtils"; + +describe("deploy E2E test", () => { + const aptos = getAptosClient(LOCAL_RPC_URL); + + let deployer: Ed25519Account; + + beforeEach(async () => { + deployer = await generateKeypair({ + rpcUrl: LOCAL_RPC_URL, + faucetUrl: LOCAL_FAUCET_URL, + prefund: true + }); + }); + + it("should successfully deploy a package with source code verification enabled", async () => { + const seed = "my seed"; + + await deploy("aptos_extensions", { + rpcUrl: LOCAL_RPC_URL, + deployerKey: deployer.privateKey.toString(), + namedDeps: [ + { name: "deployer", address: deployer.accountAddress.toString() } + ], + seed, + verifySource: true + }); + + // Package should be deployed to the correct address. + const packageId = createResourceAddress( + deployer.accountAddress, + seed + ).toString(); + + const packageMetadata = await getPackageMetadata( + aptos, + packageId, + "AptosExtensions" + ); + assert(packageMetadata != null); + + // Source code should be uploaded. + validateSourceCodeExistence(packageMetadata, true); + }); + + it("should successfully deploy a package with source code verification disabled", async () => { + const seed = "my seed"; + + await deploy("aptos_extensions", { + rpcUrl: LOCAL_RPC_URL, + deployerKey: deployer.privateKey.toString(), + namedDeps: [ + { name: "deployer", address: deployer.accountAddress.toString() } + ], + seed, + verifySource: false + }); + + // Package should be deployed to the correct address. + const packageId = createResourceAddress( + deployer.accountAddress, + seed + ).toString(); + + const packageMetadata = await getPackageMetadata( + aptos, + packageId, + "AptosExtensions" + ); + assert(packageMetadata != null); + + // Source code should not be uploaded. + validateSourceCodeExistence(packageMetadata, false); + }); +}); diff --git a/test/typescript/executeTransaction.test.ts b/test/typescript/executeTransaction.test.ts new file mode 100644 index 0000000..6a459ac --- /dev/null +++ b/test/typescript/executeTransaction.test.ts @@ -0,0 +1,192 @@ +/** + * Copyright 2024 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + CommittedTransactionResponse, + U64, + UserTransactionResponse +} from "@aptos-labs/ts-sdk"; +import { strict as assert } from "assert"; +import { executeTransaction } from "../../scripts/typescript/executeTransaction"; +import { generateKeypair } from "../../scripts/typescript/generateKeypair"; +import { + getAptosClient, + LOCAL_FAUCET_URL, + LOCAL_RPC_URL +} from "../../scripts/typescript/utils"; +import { generateKOfNMultiKeyAccount } from "./testUtils"; +import sinon, { SinonStub } from "sinon"; +import { inspect } from "util"; + +describe("executeTransaction E2E test", () => { + const aptos = getAptosClient(LOCAL_RPC_URL); + + let consoleLogStub: SinonStub; + + beforeEach(() => { + consoleLogStub = sinon.stub(console, "log"); + }); + + afterEach(() => { + sinon.restore(); + }); + + async function fixture(useMultiSigTxSender: boolean) { + const multiSigAccount = await generateKOfNMultiKeyAccount(2, 3); + await aptos.fundAccount({ + accountAddress: multiSigAccount.accountAddress, + amount: 10 * 10 ** 8, + options: { waitForIndexer: false } + }); + + const singleSigAccount = await generateKeypair({ + rpcUrl: LOCAL_RPC_URL, + faucetUrl: LOCAL_FAUCET_URL, + prefund: true + }); + + const sender = useMultiSigTxSender ? multiSigAccount : singleSigAccount; + const recipient = useMultiSigTxSender ? singleSigAccount : multiSigAccount; + + const transaction = await aptos.transaction.build.simple({ + data: { + function: "0x1::coin::transfer", + typeArguments: ["0x1::aptos_coin::AptosCoin"], + functionArguments: [recipient.accountAddress, new U64(1000)] + }, + sender: sender.accountAddress + }); + + const hexEncodedTxBytes = transaction.bcsToHex().toString(); + const hexEncodedPublicKey = sender.publicKey.bcsToHex().toString(); + const hexEncodedSignature = sender + .signTransaction(transaction) + .bcsToHex() + .toString(); + + return { + hexEncodedTxBytes, + hexEncodedPublicKey, + hexEncodedSignature + }; + } + + it("should succeed when dry running a single-sig transaction", async () => { + const useMultiSigTxSender = false; + const dryRun = true; + + const { hexEncodedTxBytes, hexEncodedPublicKey } = + await fixture(useMultiSigTxSender); + const result = (await executeTransaction({ + rpcUrl: LOCAL_RPC_URL, + txBytes: hexEncodedTxBytes, + publicKey: hexEncodedPublicKey, + dryRun, + multiSig: useMultiSigTxSender + })) as UserTransactionResponse[]; + + assert.equal(result[0].success, true); + await assert.rejects( + () => aptos.getTransactionByHash({ transactionHash: result[0].hash }), + /AptosApiError.*Transaction not found.*/ + ); + assert.equal( + consoleLogStub.calledWith(inspect(result, false, 8, true)), + true + ); + }); + + it("should succeed when dry running a multi-sig transaction", async () => { + const useMultiSigTxSender = true; + const dryRun = true; + + const { hexEncodedTxBytes, hexEncodedPublicKey } = + await fixture(useMultiSigTxSender); + const result = (await executeTransaction({ + rpcUrl: LOCAL_RPC_URL, + txBytes: hexEncodedTxBytes, + publicKey: hexEncodedPublicKey, + dryRun, + multiSig: useMultiSigTxSender + })) as UserTransactionResponse[]; + + assert.equal(result[0].success, true); + await assert.rejects( + () => aptos.getTransactionByHash({ transactionHash: result[0].hash }), + /AptosApiError.*Transaction not found.*/ + ); + assert.equal( + consoleLogStub.calledWith(inspect(result, false, 8, true)), + true + ); + }); + + it("should succeed when executing a single-sig transaction", async () => { + const useMultiSigTxSender = false; + const dryRun = false; + + const { hexEncodedTxBytes, hexEncodedPublicKey, hexEncodedSignature } = + await fixture(useMultiSigTxSender); + + const result = (await executeTransaction({ + rpcUrl: LOCAL_RPC_URL, + txBytes: hexEncodedTxBytes, + publicKey: hexEncodedPublicKey, + signature: hexEncodedSignature, + dryRun, + multiSig: useMultiSigTxSender + })) as CommittedTransactionResponse; + + assert.equal(result.success, true); + assert( + (await aptos.getTransactionByHash({ transactionHash: result.hash })) != + null + ); + assert.equal( + consoleLogStub.calledWith(inspect(result, false, 8, true)), + true + ); + }); + + it("should succeed when executing a multi-sig transaction", async () => { + const useMultiSigTxSender = true; + const dryRun = false; + + const { hexEncodedTxBytes, hexEncodedPublicKey, hexEncodedSignature } = + await fixture(useMultiSigTxSender); + + const result = (await executeTransaction({ + rpcUrl: LOCAL_RPC_URL, + txBytes: hexEncodedTxBytes, + publicKey: hexEncodedPublicKey, + signature: hexEncodedSignature, + dryRun, + multiSig: useMultiSigTxSender + })) as CommittedTransactionResponse; + + assert.equal(result.success, true); + assert( + (await aptos.getTransactionByHash({ transactionHash: result.hash })) != + null + ); + assert.equal( + consoleLogStub.calledWith(inspect(result, false, 8, true)), + true + ); + }); +}); diff --git a/test/typescript/testUtils.ts b/test/typescript/testUtils.ts index 2953b67..1b0a654 100644 --- a/test/typescript/testUtils.ts +++ b/test/typescript/testUtils.ts @@ -17,7 +17,7 @@ */ import { strict as assert } from "assert"; -import { Ed25519Account } from "@aptos-labs/ts-sdk"; +import { Ed25519Account, MultiKey, MultiKeyAccount } from "@aptos-labs/ts-sdk"; import { generateKeypair } from "../../scripts/typescript/generateKeypair"; import { checkSourceCodeExistence, @@ -51,3 +51,18 @@ export async function generateKeypairs( ); return keypairs as RepeatTuple; } + +export async function generateKOfNMultiKeyAccount( + k: number, + n: number +): Promise { + const keypairs = (await generateKeypairs(n, false)) as Array; + + return new MultiKeyAccount({ + multiKey: new MultiKey({ + publicKeys: keypairs.map((keypair) => keypair.publicKey), + signaturesRequired: k + }), + signers: keypairs + }); +} diff --git a/yarn.lock b/yarn.lock index 7a30d7c..733d125 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,12 @@ # yarn lockfile v1 -"@aptos-labs/aptos-cli@^0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@aptos-labs/aptos-cli/-/aptos-cli-0.2.0.tgz" - integrity sha512-6kljJFRsTLXCvgkNhBoOLhVyo7rmih+8+XAtdeciIXkZYwzwVS3TFPLMqBUO2HcY6gYtQQRmTG52R5ihyi/bXA== +"@aptos-labs/aptos-cli@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@aptos-labs/aptos-cli/-/aptos-cli-1.0.2.tgz#91bd1368cf808f715d102822b7d4032388cdad79" + integrity sha512-PYPsd0Kk3ynkxNfe3S4fanI3DiUICCoh4ibQderbvjPFL5A0oK6F4lPEO2t0MDsQySTk2t4vh99Xjy6Bd9y+aQ== + dependencies: + commander "^12.1.0" "@aptos-labs/aptos-client@^0.1.1": version "0.1.1" @@ -15,12 +17,12 @@ axios "1.7.4" got "^11.8.6" -"@aptos-labs/ts-sdk@1.29.1": - version "1.29.1" - resolved "https://registry.npmjs.org/@aptos-labs/ts-sdk/-/ts-sdk-1.29.1.tgz" - integrity sha512-cQsXBTbSG5XzrulzEgSMc7BPUl6jw257b248nBDYkUv6tMHkWlxuFKF6F2l+6Z8/buNIQKNPZnaZKT26DjVKug== +"@aptos-labs/ts-sdk@1.33.0": + version "1.33.0" + resolved "https://registry.yarnpkg.com/@aptos-labs/ts-sdk/-/ts-sdk-1.33.0.tgz#4da4501d04ad38381630b06d7d8a29bc4b3f1855" + integrity sha512-svdlPH5r2dlSue2D9WXaaTslsmX18WLytAho6IRZJxQjEssglk64I6c1G9S8BTjRQj/ug6ahTwp6lx3eWuyd8Q== dependencies: - "@aptos-labs/aptos-cli" "^0.2.0" + "@aptos-labs/aptos-cli" "^1.0.2" "@aptos-labs/aptos-client" "^0.1.1" "@noble/curves" "^1.4.0" "@noble/hashes" "^1.4.0" @@ -111,9 +113,9 @@ integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== "@eslint/plugin-kit@^0.2.0": - version "0.2.2" - resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz" - integrity sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw== + version "0.2.3" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz#812980a6a41ecf3a8341719f92a6d1e784a2e0e8" + integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA== dependencies: levn "^0.4.1" @@ -759,9 +761,9 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@12.1.0: +commander@12.1.0, commander@^12.1.0: version "12.1.0" - resolved "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== common-tags@^1.4.0: @@ -780,9 +782,9 @@ create-require@^1.1.0: integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" From 713b6d5ee4403eaba3b820f52aa7b84e82e3896f Mon Sep 17 00:00:00 2001 From: Yvonne Zhang Date: Mon, 4 Aug 2025 14:50:18 -0700 Subject: [PATCH 2/2] Remove renovate.json --- renovate.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 renovate.json diff --git a/renovate.json b/renovate.json deleted file mode 100644 index b6202f7..0000000 --- a/renovate.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "github>circlefin/renovate-config", - "github>circlefin/renovate-config:versions.sh" - ], - "regexManagers": [ - { - "fileMatch": ["scripts/shell/setup.sh"], - "matchStrings": [ - "export (?.*?)_VERSION=\"?(?[^\"\\t\\n\\r]*)\"? +# (?.*?):(?[^\\s]*)( versioning=(?[^\\s]+))?( extractVersion=(?[^\\s]+))?", - "export (?.*?)_TAG=\"?(?[^\"\\t\\n\\r]*)\"? +# (?.*?):(?[^\\s]*)( versioning=(?[^\\s]+))?( extractVersion=(?[^\\s]+))?" - ], - "depNameTemplate": "{{{ replace '_' '-' (lowercase depName) }}}" - } - ] -}