From 0929599eb8ecb4763fa6263b0384e42a7de4d462 Mon Sep 17 00:00:00 2001 From: Greg Nazario Date: Mon, 30 Oct 2023 23:06:52 -0700 Subject: [PATCH] [examples] Add examples running in CI This runs all of the tests against local testnet, while defaulting examples for users to devnet --- .github/actions/run-examples/action.yaml | 58 +++++++++++++++++++ .github/workflows/run-examples.yaml | 19 ++++++ examples/typescript/custom_client.ts | 17 ++++-- examples/typescript/mint_nft.ts | 8 ++- examples/typescript/multi_agent_transfer.ts | 8 ++- examples/typescript/package.json | 1 + examples/typescript/pnpm-lock.yaml | 8 +++ .../publish_package_from_filepath.ts | 7 ++- .../simple_sponsored_transaction.ts | 8 ++- examples/typescript/simple_transfer.ts | 7 ++- examples/typescript/swap.ts | 25 ++++++-- src/utils/apiEndpoints.ts | 17 ++++++ 12 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 .github/actions/run-examples/action.yaml create mode 100644 .github/workflows/run-examples.yaml diff --git a/.github/actions/run-examples/action.yaml b/.github/actions/run-examples/action.yaml new file mode 100644 index 000000000..9cd70d705 --- /dev/null +++ b/.github/actions/run-examples/action.yaml @@ -0,0 +1,58 @@ +name: "Run SDK examples" +description: | + Run the SDK Examples tests against local testnet + +runs: + using: composite + steps: + # Install node and pnpm. + - uses: actions/setup-node@v3 + with: + node-version-file: .node-version + registry-url: "https://registry.npmjs.org" + - uses: pnpm/action-setup@v2 + with: + version: 8.9.0 + + # Run package install. If install fails, it probably means the updated lockfile was + # not included in the commit. + - run: pnpm install --frozen-lockfile + shell: bash + # Build the SDk for use in the examples + - run: pnpm build + shell: bash + + # Install the CLI. + - run: pnpm install -g @aptos-labs/aptos-cli + shell: bash + + # Run a local testnet in the background. + - run: aptos node run-local-testnet --force-restart --assume-yes --with-indexer-api --log-to-stdout >& ${{ runner.temp }}/local-testnet-logs.txt & + shell: bash + + # Wait for the local testnet to be ready by hitting the readiness endpoint. + # We give it a while because the CLI will have to download some images before + # actually running the local testnet, which can take a while. + - run: pnpm install -g wait-on + shell: bash + - run: wait-on --verbose --interval 1500 --timeout 120000 --httpTimeout 120000 http-get://127.0.0.1:8070 + shell: bash + + # Change into the typescript examples folder + - run: cd examples/typescript + shell: bash + + # Run the examples + - uses: nick-fields/retry@7f8f3d9f0f62fe5925341be21c2e8314fd4f7c7c # pin@v2 + name: sdk-pnpm-examples + env: + # This is important, it ensures that the tempdir we create for cloning the ANS + # repo and mounting it into the CLI container is created in a location that + # actually supports mounting. Learn more here: https://stackoverflow.com/a/76523941/3846032. + TMPDIR: ${{ runner.temp }} + APTOS_NETWORK: local + with: + max_attempts: 3 + timeout_minutes: 25 + # This runs all of the examples + command: pnpm test diff --git a/.github/workflows/run-examples.yaml b/.github/workflows/run-examples.yaml new file mode 100644 index 000000000..300ef261f --- /dev/null +++ b/.github/workflows/run-examples.yaml @@ -0,0 +1,19 @@ +env: + GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + +name: "Run examples against local testnet" +on: + pull_request: + types: [labeled, opened, synchronize, reopened, auto_merge_enabled] + push: + branches: + - main + +jobs: + run-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ env.GIT_SHA }} + - uses: ./.github/actions/run-examples diff --git a/examples/typescript/custom_client.ts b/examples/typescript/custom_client.ts index e1192858e..e7682ce27 100644 --- a/examples/typescript/custom_client.ts +++ b/examples/typescript/custom_client.ts @@ -10,8 +10,13 @@ * `(requestOptions: ClientRequest): Promise>;` * */ -import { Aptos, AptosConfig, ClientResponse, ClientRequest } from "aptos"; -import { get as superAgentGet } from "superagent"; +import "dotenv"; +import { Aptos, AptosConfig, ClientResponse, ClientRequest, Network, toNetwork } from "aptos"; +// eslint-disable-next-line import/no-commonjs +const superagent = require("superagent"); + +// Default to devnet, but allow for overriding +const APTOS_NETWORK: Network = toNetwork(process.env.APTOS_NETWORK) || Network.DEVNET; export async function fetchCustomClient(requestOptions: ClientRequest): Promise> { const { params, method, url, headers, body } = requestOptions; @@ -55,7 +60,7 @@ export async function superagentCustomClient( method, }; - const response = await superAgentGet(`${url}?${params}`, request); + const response = await superagent.get(`${url}?${params}`, request); return { status: response.status, statusText: response.statusText, @@ -67,10 +72,10 @@ export async function superagentCustomClient( } const example = async () => { - console.log("This example demonstrate how one can config for a custom client ot be used by the SDK"); + console.log("This example demonstrate how one can config for a custom client to be used by the SDK"); async function withSuperagentClient() { - const config = new AptosConfig({ client: { provider: superagentCustomClient } }); + const config = new AptosConfig({ network: APTOS_NETWORK, client: { provider: superagentCustomClient } }); const aptos = new Aptos(config); console.log(`\nclient being used ${config.client.provider.name}`); @@ -80,7 +85,7 @@ const example = async () => { } async function withFetchClient() { - const config = new AptosConfig({ client: { provider: fetchCustomClient } }); + const config = new AptosConfig({ network: APTOS_NETWORK, client: { provider: fetchCustomClient } }); const aptos = new Aptos(config); console.log(`\nclient being used ${config.client.provider.name}`); diff --git a/examples/typescript/mint_nft.ts b/examples/typescript/mint_nft.ts index b2093a3bf..fa4aa2ebb 100644 --- a/examples/typescript/mint_nft.ts +++ b/examples/typescript/mint_nft.ts @@ -5,10 +5,14 @@ * This example shows how to use the Aptos client to mint a NFT. */ -import { Account, Aptos, AptosConfig } from "aptos"; +import "dotenv"; +import { Account, Aptos, AptosConfig, Network, toNetwork } from "aptos"; const ALICE_INITIAL_BALANCE = 100_000_000; +// Default to devnet, but allow for overriding +const APTOS_NETWORK: Network = toNetwork(process.env.APTOS_NETWORK) || Network.DEVNET; + /** * Prints the balance of an account * @param aptos @@ -40,7 +44,7 @@ const example = async () => { ); // Setup the client - const config = new AptosConfig(); + const config = new AptosConfig({ network: APTOS_NETWORK }); const aptos = new Aptos(config); // Create the account diff --git a/examples/typescript/multi_agent_transfer.ts b/examples/typescript/multi_agent_transfer.ts index b2912b011..9fbc0cb86 100644 --- a/examples/typescript/multi_agent_transfer.ts +++ b/examples/typescript/multi_agent_transfer.ts @@ -4,8 +4,8 @@ /** * This example shows how to use the Aptos client to create accounts, fund them, and transfer between them. */ - -import { Account, AccountAddress, Aptos, AptosConfig, U64, parseTypeTag } from "aptos"; +import "dotenv"; +import { Account, AccountAddress, Aptos, AptosConfig, U64, parseTypeTag, Network, toNetwork } from "aptos"; // TODO: There currently isn't a way to use the APTOS_COIN in the COIN_STORE due to a regex const APTOS_COIN = "0x1::aptos_coin::AptosCoin"; @@ -13,6 +13,8 @@ const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"; const ALICE_INITIAL_BALANCE = 100_000_000; const BOB_INITIAL_BALANCE = 100_000_000; const TRANSFER_AMOUNT = 10; +// Default to devnet, but allow for overriding +const APTOS_NETWORK: Network = toNetwork(process.env.APTOS_NETWORK) || Network.DEVNET; /** * Prints the balance of an account @@ -40,7 +42,7 @@ const example = async () => { ); // Setup the client - const config = new AptosConfig(); + const config = new AptosConfig({ network: APTOS_NETWORK }); const aptos = new Aptos(config); // Create two accounts diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 5d24a2235..9b035b90b 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -18,6 +18,7 @@ "author": "", "license": "ISC", "dependencies": { + "dotenv": "^16.3.1", "npm-run-all": "latest", "superagent": "^8.1.2" }, diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml index 421b8a92c..697a3daab 100644 --- a/examples/typescript/pnpm-lock.yaml +++ b/examples/typescript/pnpm-lock.yaml @@ -8,6 +8,9 @@ dependencies: aptos: specifier: link:../.. version: link:../.. + dotenv: + specifier: ^16.3.1 + version: 16.3.1 npm-run-all: specifier: latest version: 4.1.5 @@ -247,6 +250,11 @@ packages: engines: {node: '>=0.3.1'} dev: true + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: false + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: diff --git a/examples/typescript/publish_package_from_filepath.ts b/examples/typescript/publish_package_from_filepath.ts index 35f9d09a0..bc00ac726 100644 --- a/examples/typescript/publish_package_from_filepath.ts +++ b/examples/typescript/publish_package_from_filepath.ts @@ -7,13 +7,16 @@ * 2. cd `~/aptos-ts-sdk/examples/typescript` * 3. Run `pnpm run publish_package_from_filepath` and follow the prompt */ +/* eslint-disable no-console */ +/* eslint-disable max-len */ import assert from "assert"; import fs from "fs"; import path from "path"; import { Account, Aptos, Hex } from "aptos"; +import { createInterface } from "readline"; -const readline = require("readline").createInterface({ +const readline = createInterface({ input: process.stdin, output: process.stdout, }); @@ -69,7 +72,7 @@ async function main() { accountAddress: alice.accountAddress.toString(), }); // published 2 modules - assert(accountModules.length == 2); + assert(accountModules.length === 2); // first account's module bytecode equals the published bytecode assert(accountModules[0].bytecode === `${Hex.fromHexInput(byteCode[0]).toString()}`); // second account's module bytecode equals the published bytecode diff --git a/examples/typescript/simple_sponsored_transaction.ts b/examples/typescript/simple_sponsored_transaction.ts index dcfbb4a23..5ce756449 100644 --- a/examples/typescript/simple_sponsored_transaction.ts +++ b/examples/typescript/simple_sponsored_transaction.ts @@ -5,12 +5,15 @@ * Example to submit a simple sponsored transaction where Alice transfers APT coin to Bob * with a sponsor account to pay for the gas fee */ -import { Account, Aptos, U64 } from "aptos"; +import "dotenv"; +import { Account, Aptos, AptosConfig, Network, toNetwork, U64 } from "aptos"; const ALICE_INITIAL_BALANCE = 100_000_000; const SPONSOR_INITIAL_BALANCE = 100_000_000; const BOB_INITIAL_BALANCE = 0; const TRANSFER_AMOUNT = 10; +// Default to devnet, but allow for overriding +const APTOS_NETWORK: Network = toNetwork(process.env.APTOS_NETWORK) || Network.DEVNET; const example = async () => { console.log( @@ -18,7 +21,8 @@ const example = async () => { ); // Setup the client - const aptos = new Aptos(); + const aptosConfig = new AptosConfig({ network: APTOS_NETWORK }); + const aptos = new Aptos(aptosConfig); // Create three accounts const alice = Account.generate(); diff --git a/examples/typescript/simple_transfer.ts b/examples/typescript/simple_transfer.ts index d2b6f6359..c77d40b30 100644 --- a/examples/typescript/simple_transfer.ts +++ b/examples/typescript/simple_transfer.ts @@ -4,7 +4,7 @@ * This example shows how to use the Aptos client to create accounts, fund them, and transfer between them. */ -import { Account, AccountAddress, Aptos, AptosConfig, U64, parseTypeTag } from "aptos"; +import { Account, AccountAddress, Aptos, AptosConfig, Network, parseTypeTag, toNetwork, U64 } from "aptos"; // TODO: There currently isn't a way to use the APTOS_COIN in the COIN_STORE due to a regex const APTOS_COIN = "0x1::aptos_coin::AptosCoin"; @@ -13,6 +13,9 @@ const ALICE_INITIAL_BALANCE = 100_000_000; const BOB_INITIAL_BALANCE = 100; const TRANSFER_AMOUNT = 100; +// Default to devnet, but allow for overriding +const APTOS_NETWORK: Network = toNetwork(process.env.APTOS_NETWORK) || Network.DEVNET; + /** * Prints the balance of an account * @param aptos @@ -37,7 +40,7 @@ const example = async () => { console.log("This example will create two accounts (Alice and Bob), fund them, and transfer between them."); // Setup the client - const config = new AptosConfig(); + const config = new AptosConfig({ network: APTOS_NETWORK }); const aptos = new Aptos(config); // Create two accounts diff --git a/examples/typescript/swap.ts b/examples/typescript/swap.ts index 5542a55e2..6992d2970 100644 --- a/examples/typescript/swap.ts +++ b/examples/typescript/swap.ts @@ -12,9 +12,22 @@ * 5. Create a liquidity pool with Alice's FA and Bob's FA * 6. Swap between Alice's FA and Bob's FA */ - -import { Account, AccountAddress, Aptos, Bool, Ed25519PrivateKey, U64, ViewRequestData } from "aptos"; +import "dotenv"; +import { + Account, + AccountAddress, + Aptos, + AptosConfig, + Bool, + Ed25519PrivateKey, + Network, + toNetwork, + U64, + ViewRequestData, +} from "aptos"; import { createInterface } from "readline"; +// Default to devnet, but allow for overriding +const APTOS_NETWORK: Network = toNetwork(process.env.APTOS_NETWORK) || Network.DEVNET; const readline = createInterface({ input: process.stdin, @@ -199,10 +212,14 @@ const example = async () => { process.exit(1); } - const aptos = new Aptos(); + const aptosConfig = new AptosConfig({ network: APTOS_NETWORK }); + const aptos = new Aptos(aptosConfig); // Create three accounts - const admin = Account.fromPrivateKeyAndAddress(new Ed25519PrivateKey(process.argv[3])); const swapAddress = AccountAddress.fromHexInput(process.argv[2]); + const admin = Account.fromPrivateKeyAndAddress({ + privateKey: new Ed25519PrivateKey(process.argv[3]), + address: swapAddress, + }); console.log("====== Account info ======\n"); console.log(`Admin's address is: ${admin.accountAddress.toString()}`); diff --git a/src/utils/apiEndpoints.ts b/src/utils/apiEndpoints.ts index 484bf8ac1..b5ac81582 100644 --- a/src/utils/apiEndpoints.ts +++ b/src/utils/apiEndpoints.ts @@ -34,3 +34,20 @@ export const NetworkToChainId: Record = { mainnet: 1, testnet: 2, }; + +export function toNetwork(network: string): Network | undefined { + switch (network) { + case "mainnet": + return Network.MAINNET; + case "testnet": + return Network.TESTNET; + case "devnet": + return Network.DEVNET; + case "local": + return Network.LOCAL; + case "custom": + return Network.CUSTOM; + default: + return undefined; + } +}