diff --git a/.gitignore b/.gitignore index 3aac66927..81dc5b770 100644 --- a/.gitignore +++ b/.gitignore @@ -139,3 +139,6 @@ docs/ # pnpm typescript/.pnpm-store typescript/.pnp.* + +# Near keytore +.near-credentials diff --git a/typescript/.changeset/mean-cooks-tan.md b/typescript/.changeset/mean-cooks-tan.md new file mode 100644 index 000000000..e19968d00 --- /dev/null +++ b/typescript/.changeset/mean-cooks-tan.md @@ -0,0 +1,5 @@ +--- +"@coinbase/agentkit": minor +--- + +Added NEAR action provider to interact with NEAR chain signatures diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json index 7725f3536..3dd9a85f7 100644 --- a/typescript/agentkit/package.json +++ b/typescript/agentkit/package.json @@ -44,18 +44,29 @@ "@coinbase/cdp-sdk": "^1.3.0", "@coinbase/coinbase-sdk": "^0.20.0", "@jup-ag/api": "^6.0.39", + "@near-js/accounts": "^1.3.1", + "@near-js/crypto": "^1.4.2", + "@near-js/keystores": "^0.2.1", + "@near-js/providers": "^1.0.1", + "@near-js/signers": "^0.2.1", + "@near-js/transactions": "^1.3.1", + "@near-js/types": "^0.3.1", + "@near-js/utils": "^1.0.1", "@privy-io/public-api": "^2.18.5", "@privy-io/server-auth": "^1.18.4", "@solana/spl-token": "^0.4.12", "@solana/web3.js": "^1.98.0", + "@zerodev/ecdsa-validator": "^5.4.5", + "@zerodev/intent": "^0.0.24", + "@zerodev/sdk": "^5.4.28", "bs58": "^4.0.1", "canonicalize": "^2.1.0", "decimal.js": "^10.5.0", + "elliptic": "^6.6.1", "ethers": "^6.13.5", - "@zerodev/ecdsa-validator": "^5.4.5", - "@zerodev/intent": "^0.0.24", - "@zerodev/sdk": "^5.4.28", + "js-sha3": "^0.9.3", "md5": "^2.3.0", + "near-api-js": "^5.0.1", "opensea-js": "^7.1.18", "reflect-metadata": "^0.2.2", "twitter-api-v2": "^1.18.2", diff --git a/typescript/agentkit/src/action-providers/index.ts b/typescript/agentkit/src/action-providers/index.ts index 13856ca53..01cae1798 100644 --- a/typescript/agentkit/src/action-providers/index.ts +++ b/typescript/agentkit/src/action-providers/index.ts @@ -29,3 +29,4 @@ export * from "./flaunch"; export * from "./onramp"; export * from "./vaultsfyi"; export * from "./zerodev"; +export * from "./near"; diff --git a/typescript/agentkit/src/action-providers/near/README.md b/typescript/agentkit/src/action-providers/near/README.md new file mode 100644 index 000000000..d67f40cfb --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/README.md @@ -0,0 +1,42 @@ +# NEAR Action Provider + +This directory contains the **NearActionProvider** implementation, which provides actions to interact with the [NEAR Protocol Chain Signatures](https://docs.near.org/chain-abstraction/chain-signatures), that allows a NEAR protocol account to control other accounts/addresses across multiple chains. + +## Directory Structure + +``` +near/ +├── nearActionProvider.ts # Main provider with Chain signatures functionality +├── nearActionProvider.test.ts # Test file for Near action provider +├── constants.ts # Constants and addresses of the MPC signer +├── schemas.ts # Action schemas +├── types.ts # Types +├── utils/address.ts # Address utilities for chain signatures +├── utils/mpcContract.ts # Utilities to interact with the MPC contract +├── utils/nearChainSignature.ts # Utilities for deriving addreses +├── utils/nearChainSignature.test.ts # Test file for Near chain signature utilities +├── index.ts # Main exports +└── README.md # This file +``` + +## Actions + +- `get_cross_chain_address`: Compute a cross chain address +- `get_cross_chain_public_key`: Compute a cross chain public key +- `sign_payload`: Signs a transaction payload + +## Adding New Actions + +To add new NEAR chain signatures actions: + +1. Define your action schema in `schemas.ts` +2. Implement the action in `nearActionProvider.ts` +3. Add tests in `nearActionProvider.test.ts` + +## Network Support + +The Morpho provider supports `near-testnet` and `near-mainnet`. + +## Notes + +For more information about **Chain Signatures**, visit [Chain Signatures](https://docs.near.org/chain-abstraction/chain-signatures) diff --git a/typescript/agentkit/src/action-providers/near/constants.ts b/typescript/agentkit/src/action-providers/near/constants.ts new file mode 100644 index 000000000..6a645e72a --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/constants.ts @@ -0,0 +1,32 @@ +import { NEAR_MAINNET_NETWORK_ID, NEAR_TESTNET_NETWORK_ID } from "../../network/near"; + +export const SUPPORTED_NETWORKS = [NEAR_MAINNET_NETWORK_ID, NEAR_TESTNET_NETWORK_ID]; + +export const SUPPORTED_ADDRESS_TYPES = [ + "evm", + "bitcoin-mainnet-legacy", + "bitcoin-mainnet-segwit", + "bitcoin-testnet-legacy", + "bitcoin-testnet-segwit", +]; + +export const DEFAULT_PATH = "account-1"; + +export const DEFAULT_KEY_VERSION = 0; + +// https://docs.near.org/build/chain-abstraction/chain-signatures/#1-deriving-the-foreign-address +export const MPC_SIGNER_TESTNET = "v1.signer-prod.testnet"; + +export const MPC_SIGNER_MAINNET = "v1.signer"; + +export const ROOT_PUBLIC_KEY_TESTNET = + "secp256k1:4NfTiv3UsGahebgTaHyD9vF8KYKMBnfd6kh94mK6xv8fGBiJB8TBtFMP5WWXz6B89Ac1fbpzPwAvoyQebemHFwx3"; + +export const ROOT_PUBLIC_KEY_MAINNET = + "secp256k1:3tFRbMqmoa6AAALMrEFAYCEoHcqKxeW38YptwowBVBtXK1vo36HDbUWuR6EZmoK4JcH6HDkNMGGqP1ouV7VZUWya"; + +export const TGAS = 1000000000000n; + +export const NEAR_MAX_GAS = 300000000000000n; + +export const NO_DEPOSIT = "0"; diff --git a/typescript/agentkit/src/action-providers/near/index.ts b/typescript/agentkit/src/action-providers/near/index.ts new file mode 100644 index 000000000..c119fc7c7 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/index.ts @@ -0,0 +1,2 @@ +export * from "./nearActionProvider"; +export * from "./schemas"; diff --git a/typescript/agentkit/src/action-providers/near/nearActionProvider.test.ts b/typescript/agentkit/src/action-providers/near/nearActionProvider.test.ts new file mode 100644 index 000000000..b52e80ac4 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/nearActionProvider.test.ts @@ -0,0 +1,177 @@ +import { Connection } from "@near-js/accounts"; +import { JsonRpcProvider } from "@near-js/providers"; +import { InMemoryKeyStore } from "@near-js/keyStores"; +import { NEAR_MAINNET_NETWORK, NEAR_MAINNET_NETWORK_ID, NEAR_NETWORK_ID } from "../../network"; +import { NEARWalletProvider } from "../../wallet-providers"; +import { NearActionProvider } from "./nearActionProvider"; +import { MpcContract } from "./utils"; + +jest.mock("@near-js/utils", () => ({ + ...jest.requireActual("@near-js/utils"), + getTransactionLastResult: jest.fn().mockReturnValue({ + big_r: { + affine_point: "02ACE91E6368E5859640CB8E988D70E6C1551E3B8AEC897084C5A2797EF606CCE8", + }, + s: { + scalar: "3A84B3C0C157FFE0AC271A07F6ABB4BA8821E010F359FAEE05D82796122926F2", + }, + recovery_id: 0, + }), +})); + +describe("NearActionProvider", () => { + const actionProvider = new NearActionProvider(); + let mockWallet: jest.Mocked; + let mockProvider: jest.Mocked; + let mockConnection: jest.Mocked; + let mockSigner: jest.Mocked; + + const MOCK_ADDRESS = "wallet.near"; + const MOCK_CONTRACT = "contract.near"; + const MOCK_DESTINATION = "destination.near"; + const MOCK_TX_HASH = "5j2XGJZXq8McE9x4Y8EJ3f9tVQvFfY6zK7k1d9QXp6Bq"; + const ACCOUNT_ID = "jsvm.testnet"; + const MOCK_SIGNATURE = { + big_r: { + affine_point: "02ACE91E6368E5859640CB8E988D70E6C1551E3B8AEC897084C5A2797EF606CCE8", + }, + s: { + scalar: "3A84B3C0C157FFE0AC271A07F6ABB4BA8821E010F359FAEE05D82796122926F2", + }, + recovery_id: 0, + }; + + beforeEach(() => { + mockProvider = new JsonRpcProvider({ + url: "https://rpc.testnet.near.org", + }) as jest.Mocked; + mockSigner = new InMemoryKeyStore(); + mockConnection = new Connection(NEAR_MAINNET_NETWORK, mockProvider, mockSigner, ACCOUNT_ID); + + mockWallet = { + getAddress: jest.fn().mockReturnValue(MOCK_ADDRESS), + getNetwork: jest.fn().mockReturnValue(NEAR_MAINNET_NETWORK), + getName: jest.fn().mockReturnValue("NEAR Wallet"), + getBalance: jest.fn().mockResolvedValue(BigInt(100000000000000000000)), + nativeTransfer: jest.fn().mockResolvedValue(MOCK_TX_HASH as `0x${string}`), + getAccount: jest.fn().mockReturnValue({ + accountId: MOCK_ADDRESS, + connection: mockConnection, + contract: MOCK_CONTRACT, + destination: MOCK_DESTINATION, + signAndSendTransaction: jest.fn().mockResolvedValue(MOCK_TX_HASH as `0x${string}`), + }), + getConnection: jest.fn().mockReturnValue(mockConnection), + getPublicKey: jest.fn().mockReturnValue("0494da"), + signAndSendTransaction: jest.fn().mockResolvedValue(MOCK_TX_HASH as `0x${string}`), + } as unknown as jest.Mocked; + }); + + describe("getCrossChainAddress", () => { + it("should return the crosschain address when using default values", async () => { + const args = { + accountId: undefined, + networkId: undefined, + path: undefined, + addressType: "evm", + }; + + const response = await actionProvider.getCrossChainAddress(mockWallet, args); + + expect(response).toEqual( + "Generated cross chain address of type evm for account id wallet.near, network near-mainnet and derivation path account-1 is 0x5cf7ac588d5cdb35d8a9ed3d884f1a4245338db7", + ); + }); + + it("should return the crosschain address when using defined values", async () => { + const args = { + accountId: "omnitester.near", + networkId: NEAR_MAINNET_NETWORK_ID as NEAR_NETWORK_ID, + path: "account-1", + addressType: "evm", + }; + + const response = await actionProvider.getCrossChainAddress(mockWallet, args); + + expect(response).toEqual( + "Generated cross chain address of type evm for account id omnitester.near, network near-mainnet and derivation path account-1 is 0x9ee8197e1a04cc53ee976894082449d2f450ae34", + ); + }); + }); + + describe("getCrossChainPublicKey", () => { + it("should return the crosschain public key when using default values", async () => { + const args = { + accountId: undefined, + networkId: undefined, + path: undefined, + }; + + const response = await actionProvider.getCrossChainPublicKey(mockWallet, args); + + const expectedMessagePart = + "Computed public key for account id wallet.near, network near-mainnet and derivation path account-1 is"; + const expectedPublicKey = + "04360c67764d827f09b08e8749eb4d7362ca825176f9d67c233b63aff64f4a6b947aee76cda76f1645952ed6e259bb270a76853da16d2601e4bf2e0c60b852c66d"; + + expect(response).toEqual(`${expectedMessagePart} ${expectedPublicKey}`); + }); + + it("should return the crosschain public key when using defined values", async () => { + const args = { + accountId: "omnitester.near", + networkId: NEAR_MAINNET_NETWORK_ID as NEAR_NETWORK_ID, + path: "account-1", + }; + + const response = await actionProvider.getCrossChainPublicKey(mockWallet, args); + + const expectedMessagePart = + "Computed public key for account id omnitester.near, network near-mainnet and derivation path account-1 is"; + const expectedPublicKey = + "0494da94af0a6b62a8d247e4a7915018cb5ce069bd2e81f90cf6b8351f8d34645ca78cfafdaeb2b1879f2501581500899de03a3b8b4f345278525206b21eaa8735"; + + expect(response).toEqual(`${expectedMessagePart} ${expectedPublicKey}`); + }); + }); + + describe("signPayload", () => { + const big_r = MOCK_SIGNATURE.big_r.affine_point; + const big_s = MOCK_SIGNATURE.s.scalar; + const recoveryId = MOCK_SIGNATURE.recovery_id; + + beforeEach(() => { + jest.spyOn(MpcContract.prototype, "getExperimentalSignatureDeposit").mockResolvedValue("1"); + }); + + it("should sign a payload when passing non mandatory fields", async () => { + const args = { + path: undefined, + payload: "", + }; + + const response = await actionProvider.signPayload(mockWallet, args); + + expect(response).toEqual( + `The signature result is big_r: ${big_r}, big_s: ${big_s} and recovery_id: ${recoveryId}`, + ); + }); + + it("should sign a payload when passing all fields", async () => { + const args = { + path: "account-1", + payload: "470637f6dcc98931d6d22afa2b491c20caf0c3ba595d707606fe7915c30ef0a7", + }; + + const response = await actionProvider.signPayload(mockWallet, args); + + expect(response).toEqual( + `The signature result is big_r: ${big_r}, big_s: ${big_s} and recovery_id: ${recoveryId}`, + ); + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); +}); diff --git a/typescript/agentkit/src/action-providers/near/nearActionProvider.ts b/typescript/agentkit/src/action-providers/near/nearActionProvider.ts new file mode 100644 index 000000000..18b32b248 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/nearActionProvider.ts @@ -0,0 +1,182 @@ +import { z } from "zod"; +import { toBytes } from "viem"; +import { getTransactionLastResult } from "@near-js/utils"; + +import { Network, NEAR_PROTOCOL_FAMILY, NEAR_NETWORK_ID } from "../../network"; +import { NEARWalletProvider } from "../../wallet-providers/nearWalletProvider"; +import { ActionProvider } from "../actionProvider"; +import { CreateAction } from "../actionDecorator"; + +import { + generateAddress, + deriveChildPublicKey, + getRootPublicKey, + getMpcAccountIdByNetwork, + MpcContract, + AddressType, + SignArgs, +} from "./utils"; +import { + GetCrossChainAddressInput, + GetCrossChainPublicKeyInput, + SignPayloadInput, +} from "./schemas"; +import { DEFAULT_KEY_VERSION, DEFAULT_PATH, SUPPORTED_NETWORKS } from "./constants"; +import { MPCSignature } from "./types"; + +/** + * The NearActionProvider class provides actions for the NEAR protocol family + */ +export class NearActionProvider extends ActionProvider { + /** + * Creates an instance of NearActionProvider + */ + constructor() { + super("near", []); + } + + /** + * Returns the cross chain address for the given account id, network id, path and address type + * + * @param walletProvider - The wallet provider + * @param args - The get cross chain address input arguments + * + * @returns The cross chain address + */ + @CreateAction({ + name: "get_cross_chain_address", + description: ` +This tool computes a cross chain address of a particular type using the derivation path, network, NEAR account id and the type of address, returning the result in hex string format. + +The derived address is compatible with ECDSA and can be used to interact with contracts or perform transactions on the specified chain. + +# Inputs: +- account_id (string and optional): The NEAR account id. Default is the wallet's default address. +- network_id (string and optional): The NEAR network, either near- mainnet or near - testnet. Default is "near - mainnet". +- path (string and optional): The derivation path. Default is "account - 1". +- address type (string): The type of address based on the target chain and type of address for networks like Bitcoin (e.g., "evm" or "bitcoin - mainnet - legacy"). + +# Output: +- Returns the ECDSA-compatible address (string) for the specific address type. +`, + schema: GetCrossChainAddressInput, + }) + async getCrossChainAddress( + walletProvider: NEARWalletProvider, + args: z.infer, + ): Promise { + const accountId = args.accountId || (await walletProvider.getAccount()).accountId; + const networkId = (args.networkId || walletProvider.getNetwork().networkId) as NEAR_NETWORK_ID; + const path = (args.path || DEFAULT_PATH) as string; + const addressType = args.addressType as AddressType; + const rootPublicKey = getRootPublicKey(networkId); + + const generatedAddress = generateAddress(rootPublicKey, accountId, path, addressType); + + return `Generated cross chain address of type ${addressType} for account id ${accountId}, network ${networkId} and derivation path ${path} is ${generatedAddress.address}`; + } + + /** + * Returns the cross chain public key for the given account id, network id and path. + * + * @param walletProvider - The wallet provider + * @param args - The get cross chain public key input arguments + * + * @returns The cross chain public key + */ + @CreateAction({ + name: "get_cross_chain_public_key", + description: ` +This tool computes a public key using the chain signature key derivation function, a given derivation path, network and a NEAR account id, returning the result in hex string format. + +The resulted public key is the key the user can sign for via chain signatures and can be further converted into a valid ECDSA address for any supported chain. + +# Inputs: +- account_id (string and optional): The NEAR account id. Default is the wallet's default address. +- network_id (string and optional): The NEAR network, either "near-mainnet" or "near-testnet". Default is "near-mainnet". +- path (string and optional): The derivation path. Default is "account-1". + +# Output: +- Returns a public key (hex string) that can be converted into a valid ECDSA address for supported chains. +`, + schema: GetCrossChainPublicKeyInput, + }) + async getCrossChainPublicKey( + walletProvider: NEARWalletProvider, + args: z.infer, + ): Promise { + const accountId = args.accountId || (await walletProvider.getAccount()).accountId; + const networkId = (args.networkId || walletProvider.getNetwork().networkId) as NEAR_NETWORK_ID; + const path = (args.path || DEFAULT_PATH) as string; + const rootPublicKey = getRootPublicKey(networkId); + + const publicKey = deriveChildPublicKey(rootPublicKey, accountId, path); + + return `Computed public key for account id ${accountId}, network ${networkId} and derivation path ${path} is ${publicKey}`; + } + + /** + * Signs the given payload using the MPC contract. + * + * @param walletProvider - The wallet provider + * @param args - The sign payload input arguments + * @returns The signature result + */ + @CreateAction({ + name: "sign_payload", + description: ` +This tool signs a payload using the derivation path and produces a signed transaction in hex string format. + +The payload can represent transaction data or a message, which is signed using chain signatures. + +# Inputs: +- network_id (string and optional): The NEAR network, either "near-mainnet" or "near-testnet". Default is "near-mainnet". +- path (string): The derivation path. +- payload (string): The transaction data or message to be signed. + +# Output: +- Returns a signed transaction (hex string) that can be used on NEAR or other supported chains that include EVM Chains and Bitcoin. +`, + schema: SignPayloadInput, + }) + async signPayload( + walletProvider: NEARWalletProvider, + args: z.infer, + ): Promise { + const path = (args.path || DEFAULT_PATH) as string; + const keyVersion = (args.keyVersion || DEFAULT_KEY_VERSION) as number; + const payload = args.payload as string; + + const networkId = (args.networkId || walletProvider.getNetwork().networkId) as NEAR_NETWORK_ID; + + const connection = await walletProvider.getConnection(); + const mpcAccountId = getMpcAccountIdByNetwork(networkId); + + const signatureRequestArgs: SignArgs = { + payload: Array.from(toBytes(payload)), + path, + key_version: keyVersion, + }; + + const mpcContractInstance = new MpcContract(connection, mpcAccountId); + const signAction = await mpcContractInstance.getSignAction(signatureRequestArgs); + + const result = await walletProvider.signAndSendTransaction({ + receiverId: mpcAccountId, + actions: [signAction], + }); + + const signature = getTransactionLastResult(result) as MPCSignature; + + const { s, recovery_id: recoveryId, big_r } = signature; + + return `The signature result is big_r: ${big_r.affine_point}, big_s: ${s.scalar} and recovery_id: ${recoveryId}`; + } + + // Define if the action provider supports the given network + supportsNetwork = (network: Network) => + network.protocolFamily === NEAR_PROTOCOL_FAMILY && + SUPPORTED_NETWORKS.includes(network.networkId!); +} + +export const nearActionProvider = () => new NearActionProvider(); diff --git a/typescript/agentkit/src/action-providers/near/schemas.ts b/typescript/agentkit/src/action-providers/near/schemas.ts new file mode 100644 index 000000000..a2f9ebdae --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/schemas.ts @@ -0,0 +1,72 @@ +import { z } from "zod"; + +import { NEAR_MAINNET_NETWORK_ID, NEAR_TESTNET_NETWORK_ID } from "../../network/near"; +import { DEFAULT_KEY_VERSION, DEFAULT_PATH, SUPPORTED_ADDRESS_TYPES } from "./constants"; + +export const GetCrossChainAddressInput = z.object({ + accountId: z + .string() + .optional() + .describe("The NEAR account id. If not provided, uses the wallet's default address."), + networkId: z + .enum([NEAR_MAINNET_NETWORK_ID, NEAR_TESTNET_NETWORK_ID]) + .optional() + .describe( + `The NEAR network. If not provided, uses the wallet's network id, which defaults to ${NEAR_MAINNET_NETWORK_ID}.`, + ), + path: z + .string() + .optional() + .describe( + `The derivation path to compute the public key, e.g. "Ethereum-1". If not provided, uses the default derivation path: "${DEFAULT_PATH}"`, + ), + addressType: z + .string() + .describe( + "The address type based on the target chain and type of address for networks like Bitcoin and Ethereum (e.g., 'evm' or 'bitcoin-mainnet-legacy').", + ) + .refine(val => SUPPORTED_ADDRESS_TYPES.includes(val), { + message: `Unsupported address type. Supported address types are: ${SUPPORTED_ADDRESS_TYPES.join(", ")}`, + }), +}); + +export const GetCrossChainPublicKeyInput = z.object({ + accountId: z + .string() + .optional() + .describe("The NEAR account id. If not provided, uses the wallet's default address."), + networkId: z + .enum([NEAR_MAINNET_NETWORK_ID, NEAR_TESTNET_NETWORK_ID]) + .optional() + .describe( + `The NEAR network. If not provided, uses the wallet's network id, which defaults to ${NEAR_MAINNET_NETWORK_ID}.`, + ), + path: z + .string() + .optional() + .describe( + `The derivation path to compute the public key, e.g. "Ethereum-1". If not provided, uses the default derivation path: "${DEFAULT_PATH}"`, + ), +}); + +export const SignPayloadInput = z.object({ + networkId: z + .enum([NEAR_MAINNET_NETWORK_ID, NEAR_TESTNET_NETWORK_ID]) + .optional() + .describe( + `The NEAR network. If not provided, uses the wallet's network id, which defaults to ${NEAR_MAINNET_NETWORK_ID}.`, + ), + path: z + .string() + .optional() + .describe( + `The derivation path used to sign the payload. If not provided, uses the default derivation path: "${DEFAULT_PATH}"`, + ), + keyVersion: z + .number() + .optional() + .describe( + `The key version used to sign the payload. If not provided, uses the default key version: "${DEFAULT_KEY_VERSION}"`, + ), + payload: z.string().describe("The transaction data or message to be signed."), +}); diff --git a/typescript/agentkit/src/action-providers/near/types.ts b/typescript/agentkit/src/action-providers/near/types.ts new file mode 100644 index 000000000..78ff0d0be --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/types.ts @@ -0,0 +1,9 @@ +export interface MPCSignature { + big_r: { + affine_point: string; + }; + s: { + scalar: string; + }; + recovery_id: number; +} diff --git a/typescript/agentkit/src/action-providers/near/utils/address.ts b/typescript/agentkit/src/action-providers/near/utils/address.ts new file mode 100644 index 000000000..7ed591033 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/utils/address.ts @@ -0,0 +1,17 @@ +import { keccak256 } from "viem"; + +/** + * Convert an uncompressed hex point to an EVM address + * + * @param uncompressedHexPoint - Uncompressed hex point (64 characters) as a string + * + * @returns EVM address as a string + */ +export function uncompressedHexPointToEvmAddress(uncompressedHexPoint) { + const data = Buffer.from(uncompressedHexPoint.substring(2), "hex"); + + const hash = keccak256(data); + + // Evm address is last 20 bytes of hash (40 characters), prefixed with 0x + return `0x${hash.slice(-40)}`; +} diff --git a/typescript/agentkit/src/action-providers/near/utils/index.ts b/typescript/agentkit/src/action-providers/near/utils/index.ts new file mode 100644 index 000000000..066c049e9 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/utils/index.ts @@ -0,0 +1,3 @@ +export * from "./address"; +export * from "./nearChainSignature"; +export * from "./mpcContract"; diff --git a/typescript/agentkit/src/action-providers/near/utils/mpcContract.ts b/typescript/agentkit/src/action-providers/near/utils/mpcContract.ts new file mode 100644 index 000000000..600bb97f0 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/utils/mpcContract.ts @@ -0,0 +1,89 @@ +import { Hex } from "viem"; +import { Contract, Connection, Account } from "@near-js/accounts"; +import { actionCreators, Action as NearAction } from "@near-js/transactions"; + +import { NEAR_MAX_GAS } from "../constants"; + +export interface SignArgs { + payload: number[]; + path: string; + key_version: number; +} + +export interface MPCSignature { + big_r: string; + big_s: string; + recoveryId: number; +} + +export interface TransactionWithSignature { + transaction: Hex; + signature: MPCSignature; +} + +export interface ChangeMethodArgs { + args: T; + gas: string; + attachedDeposit: string; +} + +export interface MpcContractInterface extends Contract { + experimental_signature_deposit: () => Promise; + sign: (args: ChangeMethodArgs) => Promise; +} + +/** + * + */ +export class MpcContract { + contract: MpcContractInterface; + connectedAccount: Account; + contractId: string; + + /** + * Create a new MPC contract instance + * + * @param connection - Near connection + * @param contractId - The MPC contract ID + * + * @returns A new MPC contract instance + */ + constructor(connection: Connection, contractId: string) { + this.connectedAccount = new Account(connection, contractId); + this.contractId = contractId; + this.contract = new Contract(connection, contractId, { + changeMethods: ["sign"], + viewMethods: ["experimental_signature_deposit"], + useLocalViewExecution: false, + }) as MpcContractInterface; + } + + /** + * Get the experimental signature deposit from the MPC contract instance + * + * @returns The experimental signature deposit + */ + public async getExperimentalSignatureDeposit(): Promise { + return this.contract.experimental_signature_deposit(); + } + + /** + * Get the sign action from the MPC contract instance + * + * @param signArgs - The sign arguments + * + * @returns The sign action + */ + public async getSignAction(signArgs: SignArgs): Promise { + const signatureDeposit = await this.getExperimentalSignatureDeposit(); + + return actionCreators.functionCall( + "sign", + { + request: signArgs, + }, + NEAR_MAX_GAS.toString(), + signatureDeposit, + ); + } +} diff --git a/typescript/agentkit/src/action-providers/near/utils/nearChainSignature.test.ts b/typescript/agentkit/src/action-providers/near/utils/nearChainSignature.test.ts new file mode 100644 index 000000000..670b0a230 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/utils/nearChainSignature.test.ts @@ -0,0 +1,135 @@ +import { NEAR_MAINNET_NETWORK_ID, NEAR_TESTNET_NETWORK_ID } from "../../../network"; +import { + MPC_SIGNER_MAINNET, + MPC_SIGNER_TESTNET, + ROOT_PUBLIC_KEY_MAINNET, + ROOT_PUBLIC_KEY_TESTNET, +} from "../constants"; +import { + AddressType, + deriveChildPublicKey, + generateAddress, + getMpcAccountIdByNetwork, + getRootPublicKey, +} from "./nearChainSignature"; + +describe("Chain Signature Utils", () => { + describe("deriveChildPublicKey", () => { + it("should derive a valid child public key", () => { + const accountId = "omnitester.testnet"; + const derivedKey = deriveChildPublicKey( + "secp256k1:4NfTiv3UsGahebgTaHyD9vF8KYKMBnfd6kh94mK6xv8fGBiJB8TBtFMP5WWXz6B89Ac1fbpzPwAvoyQebemHFwx3", + accountId, + "ethereum-1", + ); + + expect(derivedKey).toEqual( + "046a99e52f042a96ea2293a69f3154b63c06cd0770cf89a46d0d1ab331dab64d8b1472638899edf8904e1c06ac7fce3e47ea1403279341c147303fd6c5df806623", + ); + }); + + it("should derive a valid child public key [against signet]", async () => { + const accountId = "omnitester.testnet"; + const derivedKey = deriveChildPublicKey( + "secp256k1:4NfTiv3UsGahebgTaHyD9vF8KYKMBnfd6kh94mK6xv8fGBiJB8TBtFMP5WWXz6B89Ac1fbpzPwAvoyQebemHFwx3", + accountId, + "ethereum-1", + ); + + expect(derivedKey).toEqual( + "046a99e52f042a96ea2293a69f3154b63c06cd0770cf89a46d0d1ab331dab64d8b1472638899edf8904e1c06ac7fce3e47ea1403279341c147303fd6c5df806623", + ); + }); + }); + + describe("generateAddress", () => { + it("should generate a valid EVM address", () => { + const cases = [ + { + publicKey: + "secp256k1:4NfTiv3UsGahebgTaHyD9vF8KYKMBnfd6kh94mK6xv8fGBiJB8TBtFMP5WWXz6B89Ac1fbpzPwAvoyQebemHFwx3", + accountId: "omnitester.testnet", + path: "ethereum-1", + addressType: AddressType.EVM, + expectedAddress: "0xd8d25820c9b9e2aa9cce55504355e500efcce715", + expectedPublicKey: + "04e612e7650febebc50b448bf790f6bdd70a8a6ce3b111a1d7e72c87afe84be776e36226e3f89de1ba3cbb62c0f3fc05bffae672c9c59d5fa8a4737b6547c64eb7", + }, + { + publicKey: + "secp256k1:3tFRbMqmoa6AAALMrEFAYCEoHcqKxeW38YptwowBVBtXK1vo36HDbUWuR6EZmoK4JcH6HDkNMGGqP1ouV7VZUWya", + accountId: "example.near", + path: "ethereum-1", + addressType: AddressType.EVM, + expectedAddress: "0xef4ea05e5c11057decc8e1bd6f5369f2acd91fa5", + expectedPublicKey: + "04f981e58fa26199180ea71b627362c7d1a6bebd73f61fedbe2e0acea5c2e75128a327822ee298967e485d787e0e98163b7d385516811f913cb7256182f5a20394", + }, + { + publicKey: + "secp256k1:3tFRbMqmoa6AAALMrEFAYCEoHcqKxeW38YptwowBVBtXK1vo36HDbUWuR6EZmoK4JcH6HDkNMGGqP1ouV7VZUWya", + accountId: "example.near", + path: "ethereum-2", + addressType: AddressType.EVM, + expectedAddress: "0x273eeb4d7651d9296ec5bd9a6ceebca219101cb8", + expectedPublicKey: + "04599038b49d32f1f345e4321dca5f3619d96100126ef940f904c1f7cb0ada3ad8f2bb44672ba5b686940230505d0bdaf4764ee7df58a5300691f60eeff095e9fc", + }, + ]; + + cases.forEach( + ({ publicKey, accountId, path, addressType, expectedAddress, expectedPublicKey }) => { + const result = generateAddress(publicKey, accountId, path, addressType); + + expect(result).toHaveProperty("address", expectedAddress); + expect(result).toHaveProperty("publicKey", expectedPublicKey); + }, + ); + }); + + it("should throw an error for unsupported address types", () => { + const publicKey = + "secp256k1:4NfTiv3UsGahebgTaHyD9vF8KYKMBnfd6kh94mK6xv8fGBiJB8TBtFMP5WWXz6B89Ac1fbpzPwAvoyQebemHFwx3"; + const accountId = "omnitester.testnet"; + const path = "0"; + const addressType = AddressType.BITCOIN_MAINNET_LEGACY; + expect(() => generateAddress(publicKey, accountId, path, addressType)).toThrow( + "Unsupported address type: bitcoin-mainnet-legacy", + ); + }); + }); + + describe("getRootPublicKey", () => { + it("should return the correct root public key for testnet", () => { + expect(getRootPublicKey(NEAR_TESTNET_NETWORK_ID)).toEqual(ROOT_PUBLIC_KEY_TESTNET); + }); + + it("should return the correct root public key for mainnet", () => { + expect(getRootPublicKey(NEAR_MAINNET_NETWORK_ID)).toEqual(ROOT_PUBLIC_KEY_MAINNET); + }); + + it("should throw an error for unsupported networks", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(() => getRootPublicKey("invalid-network" as any)).toThrow( + "Unsupported network: invalid-network", + ); + }); + }); + + describe("getMpcAccountIdByNetwork", () => { + it("should return the correct MPC signer for testnet", () => { + expect(getMpcAccountIdByNetwork(NEAR_TESTNET_NETWORK_ID)).toEqual(MPC_SIGNER_TESTNET); + }); + + it("should return the correct MPC signer for mainnet", () => { + expect(getMpcAccountIdByNetwork(NEAR_MAINNET_NETWORK_ID)).toEqual(MPC_SIGNER_MAINNET); + }); + + it("should throw an error for unsupported networks", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(() => getMpcAccountIdByNetwork("invalid-network" as any)).toThrow( + "Unsupported network: invalid-network", + ); + }); + }); +}); diff --git a/typescript/agentkit/src/action-providers/near/utils/nearChainSignature.ts b/typescript/agentkit/src/action-providers/near/utils/nearChainSignature.ts new file mode 100644 index 000000000..9803c5c27 --- /dev/null +++ b/typescript/agentkit/src/action-providers/near/utils/nearChainSignature.ts @@ -0,0 +1,148 @@ +import { baseDecode } from "@near-js/utils"; +import { ec as EC } from "elliptic"; +import { sha3_256 } from "js-sha3"; + +import { uncompressedHexPointToEvmAddress } from "./address"; +import { + NEAR_MAINNET_NETWORK_ID, + NEAR_NETWORK_ID, + NEAR_TESTNET_NETWORK_ID, +} from "../../../network"; +import { + MPC_SIGNER_MAINNET, + MPC_SIGNER_TESTNET, + ROOT_PUBLIC_KEY_MAINNET, + ROOT_PUBLIC_KEY_TESTNET, +} from "../constants"; + +/** + * Convert a najPublicKeyStr to an uncompressed hex point. + * + * @param najPublicKeyStr - The najPublicKeyStr to convert. + * @returns The uncompressed hex point. + */ +function najPublicKeyStrToUncompressedHexPoint(najPublicKeyStr: string): string { + const decodedKey = baseDecode(najPublicKeyStr.split(":")[1]!); + return "04" + Buffer.from(decodedKey).toString("hex"); +} + +/** + * Derive a child public key from a parent public key + * + * @param parentUncompressedPublicKeyHex - The parent public key + * @param accountId - The account ID + * @param path - The path + * @returns The child public key + */ +export function deriveChildPublicKey( + parentUncompressedPublicKeyHex: string, + accountId: string, + path: string = "", +): string { + const ec = new EC("secp256k1"); + const scalarHex = sha3_256(`near-mpc-recovery v0.1.0 epsilon derivation:${accountId},${path}`); + + const x = parentUncompressedPublicKeyHex.substring(2, 66); + const y = parentUncompressedPublicKeyHex.substring(66); + + // Create a point object from X and Y coordinates + const oldPublicKeyPoint = ec.curve.point(x, y); + + // Multiply the scalar by the generator point G + const scalarTimesG = ec.g.mul(scalarHex); + + // Add the result to the old public key point + const newPublicKeyPoint = oldPublicKeyPoint.add(scalarTimesG); + const newX = newPublicKeyPoint.getX().toString("hex").padStart(64, "0"); + const newY = newPublicKeyPoint.getY().toString("hex").padStart(64, "0"); + return "04" + newX + newY; +} + +export enum AddressType { + EVM = "evm", + BITCOIN_MAINNET_LEGACY = "bitcoin-mainnet-legacy", + BITCOIN_MAINNET_SEGWIT = "bitcoin-mainnet-segwit", + BITCOIN_TESTNET_LEGACY = "bitcoin-testnet-legacy", + BITCOIN_TESTNET_SEGWIT = "bitcoin-testnet-segwit", +} + +/** + * Generate an address + * + * @param publicKey - The public key + * @param accountId - The account ID + * @param path - The path + * @param addressType - The address type + * @returns The generated address + */ +export function generateAddress( + publicKey: string, + accountId: string, + path: string, + addressType: AddressType, +) { + const childPublicKey = deriveChildPublicKey( + najPublicKeyStrToUncompressedHexPoint(publicKey), + accountId, + path, + ); + + if (!addressType) throw new Error("addressType is required"); + + let address; + + switch (addressType) { + case AddressType.EVM: + address = uncompressedHexPointToEvmAddress(childPublicKey); + break; + case AddressType.BITCOIN_MAINNET_LEGACY: + throw new Error(`Unsupported address type: ${addressType}`); + case AddressType.BITCOIN_MAINNET_SEGWIT: + throw new Error(`Unsupported address type: ${addressType}`); + case AddressType.BITCOIN_TESTNET_LEGACY: + throw new Error(`Unsupported address type: ${addressType}`); + case AddressType.BITCOIN_TESTNET_SEGWIT: + throw new Error(`Unsupported address type: ${addressType}`); + default: + throw new Error(`Unsupported address type: ${addressType}`); + } + + return { + address, + publicKey: childPublicKey, + }; +} + +/** + * Get the root public key by network + * + * @param network - The NEAR network ID. + * @returns The root public key. + */ +export function getRootPublicKey(network: NEAR_NETWORK_ID): string { + switch (network) { + case NEAR_TESTNET_NETWORK_ID: + return ROOT_PUBLIC_KEY_TESTNET; + case NEAR_MAINNET_NETWORK_ID: + return ROOT_PUBLIC_KEY_MAINNET; + default: + throw new Error(`Unsupported network: ${network}`); + } +} + +/** + * Get the MPC account ID by network + * + * @param network - The NEAR network ID. + * @returns The MPC account ID. + */ +export function getMpcAccountIdByNetwork(network: NEAR_NETWORK_ID): string { + switch (network) { + case NEAR_TESTNET_NETWORK_ID: + return MPC_SIGNER_TESTNET; + case NEAR_MAINNET_NETWORK_ID: + return MPC_SIGNER_MAINNET; + default: + throw new Error(`Unsupported network: ${network}`); + } +} diff --git a/typescript/agentkit/src/network/index.ts b/typescript/agentkit/src/network/index.ts index 1d1fed354..486fbbd6a 100644 --- a/typescript/agentkit/src/network/index.ts +++ b/typescript/agentkit/src/network/index.ts @@ -1,3 +1,4 @@ export * from "./network"; export * from "./svm"; export * from "./types"; +export * from "./near"; diff --git a/typescript/agentkit/src/network/near.ts b/typescript/agentkit/src/network/near.ts new file mode 100644 index 000000000..8ca093b22 --- /dev/null +++ b/typescript/agentkit/src/network/near.ts @@ -0,0 +1,26 @@ +import { Network } from "./types"; + +// CDP Network IDs +export const NEAR_MAINNET_NETWORK_ID = "near-mainnet"; +export const NEAR_TESTNET_NETWORK_ID = "near-testnet"; +export type NEAR_NETWORK_ID = typeof NEAR_MAINNET_NETWORK_ID | typeof NEAR_TESTNET_NETWORK_ID; + +// AgentKit Protocol Family +export const NEAR_PROTOCOL_FAMILY = "near"; + +export const NEAR_MAINNET_NETWORK: Network = { + protocolFamily: NEAR_PROTOCOL_FAMILY, + chainId: undefined, + networkId: NEAR_MAINNET_NETWORK_ID, +}; + +export const NEAR_TESTNET_NETWORK: Network = { + protocolFamily: NEAR_PROTOCOL_FAMILY, + chainId: undefined, + networkId: NEAR_TESTNET_NETWORK_ID, +}; + +export const NEAR_NETWORKS: Record = { + [NEAR_MAINNET_NETWORK_ID]: NEAR_MAINNET_NETWORK, + [NEAR_TESTNET_NETWORK_ID]: NEAR_TESTNET_NETWORK, +}; diff --git a/typescript/agentkit/src/wallet-providers/index.ts b/typescript/agentkit/src/wallet-providers/index.ts index 49fd82735..8ab94f1f3 100644 --- a/typescript/agentkit/src/wallet-providers/index.ts +++ b/typescript/agentkit/src/wallet-providers/index.ts @@ -14,3 +14,5 @@ export * from "./privyEvmWalletProvider"; export * from "./privySvmWalletProvider"; export * from "./privyEvmDelegatedEmbeddedWalletProvider"; export * from "./zeroDevWalletProvider"; +export * from "./nearKeypairWalletProvider"; +export * from "./nearWalletProvider"; diff --git a/typescript/agentkit/src/wallet-providers/nearKeypairWalletProvider.test.ts b/typescript/agentkit/src/wallet-providers/nearKeypairWalletProvider.test.ts new file mode 100644 index 000000000..56d793fef --- /dev/null +++ b/typescript/agentkit/src/wallet-providers/nearKeypairWalletProvider.test.ts @@ -0,0 +1,41 @@ +import { KeyPair } from "@near-js/crypto"; + +import { NEAR_TESTNET_NETWORK_ID } from "../network"; +import { NearKeypairWalletProvider } from "./nearKeypairWalletProvider"; + +describe("Near Keypair Wallet", () => { + const privateKey = + "ed25519:4PqsZQyshimjoyHu9vHCQVb3GpFLXKUd5hx4RXrw6q8nbnEDR5repFcP58BEzYWgeA3VjsmuqToRRCmpWFQBbE7n"; + const accountId = "account.near"; + const rpcUrl = "https://near.dev"; + const networkId = NEAR_TESTNET_NETWORK_ID; + const network = { + chainId: undefined, + protocolFamily: "near", + networkId: "near-testnet", + }; + + let wallet: NearKeypairWalletProvider; + let keypair: KeyPair; + + beforeEach(() => { + keypair = KeyPair.fromString(privateKey); + + wallet = new NearKeypairWalletProvider(keypair, accountId, rpcUrl, networkId); + }); + + it("should initialize correctly", async () => { + expect(wallet.getPublicKey()).toEqual(keypair.getPublicKey().toString()); + expect(wallet.getAddress()).toEqual(accountId); + expect(wallet.getNetwork()).toEqual(network); + expect(wallet.getName()).toEqual("near_keypair_wallet_provider"); + + const account = await wallet.getAccount(); + expect(account.accountId).toEqual(accountId); + + const connection = await wallet.getConnection(); + expect(connection.networkId).toEqual("testnet"); + expect(connection.provider.connection.url).toEqual(rpcUrl); + expect(connection.jsvmAccountId).toEqual(accountId); + }); +}); diff --git a/typescript/agentkit/src/wallet-providers/nearKeypairWalletProvider.ts b/typescript/agentkit/src/wallet-providers/nearKeypairWalletProvider.ts new file mode 100644 index 000000000..d590af1e5 --- /dev/null +++ b/typescript/agentkit/src/wallet-providers/nearKeypairWalletProvider.ts @@ -0,0 +1,270 @@ +import { withRetry } from "viem"; + +import { Account } from "@near-js/accounts"; +import { connect, Near, transactions, utils as nearUtils } from "near-api-js"; +import { KeyPair } from "@near-js/crypto"; +import { Connection, ConnectionConfig } from "@near-js/accounts"; +import { InMemoryKeyStore } from "@near-js/keyStores"; +import type { TxExecutionStatus, FinalExecutionOutcome } from "@near-js/types"; +import { actionCreators } from "@near-js/transactions"; + +import { NEAR_MAINNET_NETWORK_ID, NEAR_NETWORK_ID, NEAR_NETWORKS, Network } from "../network"; +import { NEARWalletProvider, TransactionSenderParams } from "./nearWalletProvider"; + +export interface SendTransactionOptions { + until: TxExecutionStatus; + retryCount: number; + delay: number; + nodeUrl: string; +} + +export interface AccessKeyInfo { + block_hash: string; + block_height: number; + nonce: number; + permission: string; +} + +const DEFAULT_OPTIONS: SendTransactionOptions = { + until: "EXECUTED_OPTIMISTIC", + retryCount: 3, + delay: 5000, // Near RPC timeout + nodeUrl: "https://test.rpc.fastnear.com", // defaults to testnet +}; + +/** + * NeaKeypairWalletProvider is a wallet provider that uses a local Near keypair. + * + * @augments NEARWalletProvider + */ +export class NearKeypairWalletProvider extends NEARWalletProvider { + keypair: KeyPair; + rpcProviderUrl: string; + network: NEAR_NETWORK_ID; + accountId: string; + connection: Connection; + keyStore: InMemoryKeyStore; + connectionConfig: ConnectionConfig; + + near: Near | null = null; + account: Account | null = null; + + /** + * Creates a new NearKeypairWalletProvider. + * + * @param keypair - The keypair to use for signing transactions. + * @param accountId - The account ID to use for signing transactions. + * @param rpcProviderUrl - The RPC provider URL. + * @param network - The network ID. + * + * @returns A new NearKeypairWalletProvider. + */ + constructor( + keypair: KeyPair, + accountId: string, + rpcProviderUrl: string, + network: NEAR_NETWORK_ID, + ) { + super(); + + this.keypair = keypair; + this.rpcProviderUrl = rpcProviderUrl; + this.accountId = accountId; + this.network = network; + + this.keyStore = new InMemoryKeyStore(); + + this.connectionConfig = { + networkId: this.network === NEAR_MAINNET_NETWORK_ID ? "mainnet" : "testnet", + nodeUrl: this.rpcProviderUrl, + keyStore: this.keyStore, + jsvmAccountId: this.accountId, + }; + } + + /** + * Get the public key of the keypair. + * + * @returns The public key of the keypair. + */ + getPublicKey(): string { + return this.keypair.getPublicKey().toString(); + } + + /** + * Get the address of the wallet provider. + * + * @returns The address of the wallet provider. + */ + getAddress(): string { + return this.accountId; + } + + /** + * Get the network of the wallet provider. + * + * @returns The network of the wallet provider. + */ + getNetwork(): Network { + return NEAR_NETWORKS[this.network]; + } + + /** + * Get the name of the wallet provider. + * + * @returns The name of the wallet provider. + */ + getName(): string { + return "near_keypair_wallet_provider"; + } + + /** + * Get the connection of the wallet provider. + * + * @returns The connection of the wallet + */ + async getConnection(): Promise { + await this.connectIfNeeded(); + return this.connection; + } + + /** + * Get the NEAR account of the wallet provider. + * + * @returns The NEAR account of the wallet provider. + */ + async getAccount(): Promise { + await this.connectIfNeeded(); + return this.account!; + } + + /** + * Get the balance of the native asset of the network. + * + * @returns The balance of the native asset of the network. + */ + async getBalance(): Promise { + await this.connectIfNeeded(); + const balance = await this.account.getAccountBalance(); + return BigInt(balance.available); + } + + /** + * Transfer the native asset of the network. + * + * @param to - The destination address. + * @param value - The amount to transfer in whole units (e.g. ETH) + * @returns The transaction hash + */ + async nativeTransfer(to: string, value: string): Promise { + await this.connectIfNeeded(); + const amountYocto = nearUtils.format.parseNearAmount(value); + if (!amountYocto) { + throw new Error("Error converting amount to yoctonear"); + } + const action = actionCreators.transfer(BigInt(amountYocto)); + const outcome = await this.account.signAndSendTransaction({ + receiverId: to, + actions: [action], + }); + return outcome.transaction.hash; + } + + /** + * Sign and send a transaction. + * + * @param args - The transaction sender parameters. + * @param options - The send transaction options. + * + * @returns The final execution outcome + */ + async signAndSendTransaction( + args: TransactionSenderParams, + options: SendTransactionOptions = DEFAULT_OPTIONS, + ): Promise { + await this.connectIfNeeded(); + const accountId = this.accountId; + + const { signer } = this.near.connection; + const connection = this.near.connection; + + const publicKey = await signer.getPublicKey(accountId, connection.networkId); + + const accessKey = (await connection.provider.query( + `access_key/${accountId}/${publicKey.toString()}`, + "", + )) as AccessKeyInfo; + + if (!accessKey) { + throw new Error("Access key not found"); + } + + const recentBlockHash = nearUtils.serialize.base_decode(accessKey.block_hash); + + const tx = transactions.createTransaction( + accountId, + publicKey, + args.receiverId, + ++accessKey.nonce, + args.actions, + recentBlockHash, + ); + + const serializedTx = nearUtils.serialize.serialize(transactions.SCHEMA.Transaction, tx); + + const nearTransactionSignature = await signer.signMessage( + serializedTx, + accountId, + connection.networkId, + ); + + const signedTransaction = new transactions.SignedTransaction({ + transaction: tx, + signature: new transactions.Signature({ + keyType: tx.publicKey.keyType, + data: nearTransactionSignature.signature, + }), + }); + const { transaction } = await connection.provider.sendTransactionUntil( + signedTransaction, + "INCLUDED_FINAL", + ); + + const txHash = transaction.hash as string | undefined; + + if (!txHash) { + throw new Error("No transaction hash found"); + } + + return await withRetry( + async () => { + const txOutcome = await connection.provider.txStatus(txHash, accountId, options.until); + + if (txOutcome) { + return txOutcome; + } + + throw new Error("Transaction not found"); + }, + { + retryCount: options.retryCount, + delay: options.delay, + }, + ); + } + + /** + * Connect to the Near network if needed. + * + * @returns A Promise that resolves when the connection is established. + */ + async connectIfNeeded() { + if (!this.near) { + const key = this.network === NEAR_MAINNET_NETWORK_ID ? "mainnet" : "testnet"; + await this.keyStore.setKey(key, this.accountId, this.keypair); + this.near = await connect(this.connectionConfig); + this.connection = this.near.connection; + this.account = await this.near.account(this.accountId); + } + } +} diff --git a/typescript/agentkit/src/wallet-providers/nearWalletProvider.ts b/typescript/agentkit/src/wallet-providers/nearWalletProvider.ts new file mode 100644 index 000000000..b2a5e9359 --- /dev/null +++ b/typescript/agentkit/src/wallet-providers/nearWalletProvider.ts @@ -0,0 +1,87 @@ +import { Connection, Account, SignAndSendTransactionOptions } from "@near-js/accounts"; +import type { FinalExecutionOutcome } from "@near-js/types"; +import type { Action as TransactionAction } from "@near-js/transactions"; + +import { Network } from "../network"; +import { WalletProvider } from "./walletProvider"; + +export interface TransactionSenderParams { + receiverId: string; + actions: TransactionAction[]; +} + +/** + * NEAR Wallet Provider is the abstract base class for all NEAR wallet providers (non browsers). + * + * @abstract + */ +export abstract class NEARWalletProvider extends WalletProvider { + /** + * Get the address of the wallet provider. + * + * @returns The address of the wallet provider. + */ + abstract getAddress(): string; + + /** + * Get the network of the wallet provider. + * + * @returns The network of the wallet provider. + */ + abstract getNetwork(): Network; + + /** + * Get the name of the wallet provider. + * + * @returns The name of the wallet provider. + */ + abstract getName(): string; + + /** + * Get the balance of the native asset of the network. + * + * @returns The balance of the native asset of the network. + */ + abstract getBalance(): Promise; + + /** + * Transfer the native asset of the network. + * + * @param to - The destination address. + * @param value - The amount to transfer in whole units (e.g. ETH) + * @returns The transaction hash. + */ + abstract nativeTransfer(to: string, value: string): Promise; + + /** + * Get the connection of the wallet provider. + * + * @returns The connection of the wallet provider. + */ + abstract getConnection(): Connection; + + /** + * Get the NEAR account of the wallet provider. + * + * @returns The NEAR account of the wallet provider. + */ + abstract getAccount(): Promise; + + /** + * Get the public key of the wallet. + * + * @returns The wallet's public key. + */ + abstract getPublicKey(): string; + + /** + * Sign and send a transaction. + * + * @param values - The options for signing and sending the transaction. + * + * @returns The signature and transaction hash. + */ + abstract signAndSendTransaction( + values: SignAndSendTransactionOptions, + ): Promise; +} diff --git a/typescript/examples/langchain-near-chatbot/.env.local b/typescript/examples/langchain-near-chatbot/.env.local new file mode 100644 index 000000000..ab68f8a39 --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/.env.local @@ -0,0 +1,5 @@ +OPENAI_API_KEY= +NETWORK_ID= +NEAR_RPC_URL= +NEAR_ACCOUNT_ID= +NEAR_PRIVATE_KEY= \ No newline at end of file diff --git a/typescript/examples/langchain-near-chatbot/.eslintrc.json b/typescript/examples/langchain-near-chatbot/.eslintrc.json new file mode 100644 index 000000000..91571ba7a --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": ["../../.eslintrc.base.json"] +} diff --git a/typescript/examples/langchain-near-chatbot/.prettierignore b/typescript/examples/langchain-near-chatbot/.prettierignore new file mode 100644 index 000000000..20de531f4 --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/.prettierignore @@ -0,0 +1,7 @@ +docs/ +dist/ +coverage/ +.github/ +src/client +**/**/*.json +*.md diff --git a/typescript/examples/langchain-near-chatbot/.prettierrc b/typescript/examples/langchain-near-chatbot/.prettierrc new file mode 100644 index 000000000..fae203e56 --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/.prettierrc @@ -0,0 +1,11 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 100, + "proseWrap": "never" +} \ No newline at end of file diff --git a/typescript/examples/langchain-near-chatbot/README.md b/typescript/examples/langchain-near-chatbot/README.md new file mode 100644 index 000000000..97a85e6fd --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/README.md @@ -0,0 +1,79 @@ +# NEAR AgentKit LangChain Extension Examples - Chatbot Typescript + +This example demonstrates an agent setup as a terminal style chatbot with access to the full set of NEAR AgentKit actions. + +## Ask the chatbot to engage in the world of cross chain interactions ! + +- "What is your address and balance?" +- "What is your cross chain public key?" +- "What is your cross chain address for a particular chain?" +- "Sign a transaccion payload" + +## Prerequisites + +### Checking Node Version + +Before using the example, ensure that you have the correct version of Node.js installed. The example requires Node.js 18 or higher. You can check your Node version by running: + +```bash +node --version +``` + +If you don't have the correct version, you can install it using [nvm](https://github.com/nvm-sh/nvm): + +```bash +nvm install node +``` + +This will automatically install and use the latest version of Node. + +### API Keys + +You'll need the following API keys: +- [OpenAI API Key](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) + +Once you have them, rename the `.env-local` file to `.env` and make sure you set the API keys to their corresponding environment variables: + +- "OPENAI_API_KEY" **(required)** +- "NEAR_ACCOUNT_ID" *(optional)* +- "NEAR_PRIVATE_KEY" *(optional)* +- "NEAR_RPC_URL" *(optional)* +- "NETWORK_ID" *(optional)* + +#### Network Selection + +The supported networks are `near-mainnet` and `near-testnet`. + +Network selection follows this priority: +1. **Explicit RPC URL** – If `NEAR_RPC_URL` is set in your `.env`, this RPC URL is used, and the network is inferred from it. +2. **Network ID** – If `NEAR_RPC_URL` is not set but `NETWORK_ID` is, the network is determined by `NETWORK_ID`, and a default RPC URL is assigned accordingly. +3. **Fallback** – If neither variable is set, the default network is `near-testnet` with a default RPC URL. + +#### Keypair Selection + +The keypair is determined by the `NEAR_PRIVATE_KEY` in your `.env``. + +If no keypair is provided, a new one will be generated, and the private key will be displayed in the console. To reuse it in future runs, save the displayed key to `NEAR_PRIVATE_KEY` in your `.env`. + +## Running the example + +From the root directory, run: + +```bash +npm install +npm run build +``` + +This will install the dependencies and build the packages locally. The chatbot example uses the local `@coinbase/agentkit-langchain` and `@coinbase/agentkit` packages. If you make changes to the packages, you can run `npm run build` from root again to rebuild the packages, and your changes will be reflected in the chatbot example. + +Now from the `typescript/examples/langchain-near-chatbot` directory, run: + +```bash +npm start +``` + +Select "1. chat mode" and start telling your Agent to do things onchain! + +## License + +Apache-2.0 diff --git a/typescript/examples/langchain-near-chatbot/chatbot-utils.ts b/typescript/examples/langchain-near-chatbot/chatbot-utils.ts new file mode 100644 index 000000000..cad84b15d --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/chatbot-utils.ts @@ -0,0 +1,169 @@ +import { getTestnetRpcProvider, getAccountState, getEndpointsByNetwork } from "@near-js/client"; +import type { FinalExecutionOutcome } from "@near-js/types"; +import { NEAR_MAINNET_NETWORK_ID, NEAR_NETWORK_ID } from "@coinbase/agentkit"; + +import * as readline from "readline"; +import crypto from "node:crypto"; + +/** + * Generates a random NEAR account ID. + * + * @returns Random NEAR account ID + */ +export function generateNearAccountId(): string { + // Generate a random length between 10 and 32 using a cryptographically secure source. + const length = getRandomInt(10, 32); + const allowedChars = "abcdefghijklmnopqrstuvwxyz0123456789"; + + // Function to get a random character from the given charset using crypto.getRandomValues. + const getRandomChar = (charset: string) => { + const randomBuffer = new Uint32Array(1); + crypto.getRandomValues(randomBuffer); + return charset[Math.floor((randomBuffer[0] / 2 ** 32) * charset.length)]; + }; + + // Ensure the first character is a letter, the last is alphanumeric, + // and generate the middle characters from the allowed set. + const firstChar = getRandomChar("abcdefghijklmnopqrstuvwxyz"); + const middleChars = Array.from({ length: length - 2 }, () => getRandomChar(allowedChars)).join( + "", + ); + const lastChar = getRandomChar("abcdefghijklmnopqrstuvwxyz0123456789"); + + return `${firstChar}${middleChars}${lastChar}.testnet`; +} + +/** + * Generates a random integer between min and max (inclusive) using crypto.getRandomValues. + * + * @param min - Minimum value + * @param max - Maximum value + * @returns A random integer between min and max + */ +function getRandomInt(min: number, max: number): number { + const range = max - min + 1; + const randomBuffer = new Uint32Array(1); + crypto.getRandomValues(randomBuffer); + return min + (randomBuffer[0] % range); +} + +/** + * Get the NEAR network endpoints by network ID + * + * @param network - NEAR network ID + * + * @returns NEAR network first endpoint + */ +export function getEndpointsByNetworkId(network: NEAR_NETWORK_ID): string { + const nearNetwork = network === NEAR_MAINNET_NETWORK_ID ? "mainnet" : "testnet"; + return getEndpointsByNetwork(nearNetwork)[0]; +} + +/** + * Get the key store key by network ID + * + * @param network - NEAR network ID + * + * @returns NEAR network key store key + */ +export function getNearKeyStoreKey(network: NEAR_NETWORK_ID): string { + return network === NEAR_MAINNET_NETWORK_ID ? "mainnet" : "testnet"; +} + +/** + * Choose the account to use for the chatbot + * + * @returns NEAR account ID + */ +export async function chooseAccount(): Promise { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const question = (prompt: string): Promise => + new Promise(resolve => rl.question(prompt, resolve)); + + // eslint-disable-next-line no-constant-condition + while (true) { + const accountId = await question("Enter your NEAR account ID (leave blank for a random one): "); + + if (accountId.trim().length === 0) { + let generatedId: string; + let exists: boolean; + + do { + generatedId = generateNearAccountId(); + exists = await isRegisteredAccount(generatedId); + } while (exists); + + console.log(`✅ No account ID provided. Generated NEAR account: ${generatedId}`); + rl.close(); + return generatedId; + } else { + const exists = await isRegisteredAccount(accountId); + if (!exists) { + console.log(`✅ The account "${accountId}" is available and can be used.`); + rl.close(); + return accountId; + } else { + console.log( + `❌ The account "${accountId}" is already taken. Please enter a different account.`, + ); + } + } + } +} + +/** + * Check if an account is already registered + * + * @param account - NEAR account ID + * + * @returns True if the account is already registered, false otherwise + */ +export async function isRegisteredAccount(account: string) { + try { + await getAccountState({ + account, + deps: { + rpcProvider: getTestnetRpcProvider(), + }, + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + if (e.type === "AccountDoesNotExist" || e.type === "RetriesExceeded") { + return false; + } + } + + return true; +} + +/** + * Create a funded testnet account + * + * @param newAccountId - the new account ID + * @param newPublicKey - the public key of the new account + * + * @returns the final outcome of the transaction + */ +export async function createFundedTestnetAccount(newAccountId: string, newPublicKey: string) { + const NEAR_WALLET_FAUCET_URL = "https://near-faucet.io/api/faucet/accounts"; + + const res = await fetch(NEAR_WALLET_FAUCET_URL, { + method: "POST", + body: JSON.stringify({ + newAccountId, + newPublicKey, + }), + headers: { "Content-Type": "application/json" }, + }); + + const { ok, status } = res; + if (!ok) { + throw new Error(`Failed to create account: ${status}`); + } + + return (await res.json()) as FinalExecutionOutcome; +} diff --git a/typescript/examples/langchain-near-chatbot/chatbot.ts b/typescript/examples/langchain-near-chatbot/chatbot.ts new file mode 100644 index 000000000..0550103e1 --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/chatbot.ts @@ -0,0 +1,352 @@ +import { + AgentKit, + walletActionProvider, + NearKeypairWalletProvider, + nearActionProvider, + NEAR_NETWORK_ID, + NEAR_TESTNET_NETWORK_ID, + NEAR_MAINNET_NETWORK_ID, +} from "@coinbase/agentkit"; +import { getLangChainTools } from "@coinbase/agentkit-langchain"; +import { HumanMessage } from "@langchain/core/messages"; +import { MemorySaver } from "@langchain/langgraph"; +import { createReactAgent } from "@langchain/langgraph/prebuilt"; +import { ChatOpenAI } from "@langchain/openai"; + +import { UnencryptedFileSystemKeyStore } from "@near-js/keystores-node"; +import { KeyPair } from "@near-js/crypto"; + +import { join } from "node:path"; +import * as dotenv from "dotenv"; +import * as readline from "readline"; + +import { + chooseAccount, + createFundedTestnetAccount, + getEndpointsByNetworkId, +} from "./chatbot-utils"; + +dotenv.config(); + +/** + * Validates that required environment variables are set + * + * @throws {Error} - If required environment variables are missing + * @returns {void} + */ +function validateEnvironment(): void { + const missingVars: string[] = []; + + // Check required variables + const requiredVars = ["OPENAI_API_KEY"]; + requiredVars.forEach(varName => { + if (!process.env[varName]) { + missingVars.push(varName); + } + }); + + // Exit if any required variables are missing + if (missingVars.length > 0) { + console.error("Error: Required environment variables are not set"); + missingVars.forEach(varName => { + console.error(`${varName}=your_${varName.toLowerCase()}_here`); + }); + process.exit(1); + } + + // Warn about optional NETWORK_ID + if (!process.env.NETWORK_ID) { + console.warn("Warning: NETWORK_ID both is unset, defaulting to near-testnet"); + } + + const networkId = process.env.NETWORK_ID || NEAR_TESTNET_NETWORK_ID; + if (networkId !== NEAR_TESTNET_NETWORK_ID && networkId !== NEAR_MAINNET_NETWORK_ID) { + console.error( + `Error: NETWORK_ID must be one of ${NEAR_TESTNET_NETWORK_ID} or ${NEAR_MAINNET_NETWORK_ID}`, + ); + process.exit(1); + } + + if (networkId === NEAR_MAINNET_NETWORK_ID) { + // On mainnet, we need to have the NEAR_ACCOUNT_ID and NEAR_PRIVATE_KEY set + if (!process.env.NEAR_PRIVATE_KEY) { + console.error("Error: NEAR_PRIVATE_KEY is required on mainnet"); + process.exit(1); + } + + if (!process.env.NEAR_ACCOUNT_ID) { + console.error("Error: NEAR_ACCOUNT_ID is required on mainnet"); + process.exit(1); + } + } else { + // Check optional dependant variables NEAR_PRIVATE_KEY without NEAR_ACCOUNT_ID + if (process.env.NEAR_PRIVATE_KEY && !process.env.NEAR_ACCOUNT_ID) { + console.error("Error: NEAR_ACCOUNT_ID is required when NEAR_PRIVATE_KEY is set"); + process.exit(1); + } + + // Check optional dependant variables NEAR_ACCOUNT_ID without NEAR_PRIVATE_KEY + if (process.env.NEAR_ACCOUNT_ID && !process.env.NEAR_PRIVATE_KEY) { + console.warn( + `Warning: NEAR_ACCOUNT_ID is set without NEAR_PRIVATE_KEY, the private key will be loaded from the keystore using the provided NEAR_ACCOUNT_ID`, + ); + } + + // Warn about optional NEAR_ACCOUNT_ID & NEAR_PRIVATE_KEY + if (!process.env.NEAR_ACCOUNT_ID && !process.env.NEAR_PRIVATE_KEY) { + console.warn( + "Warning: NEAR_ACCOUNT_ID and NEAR_PRIVATE_KEY are unset, you will be prompted to create an account", + ); + } + } +} + +// Add this right after imports and before any other code +validateEnvironment(); + +/** + * Initialize the agent with CDP Agentkit + * + * @returns Agent executor and config + */ +async function initializeAgent() { + try { + // Initialize LLM + const llm = new ChatOpenAI({ + model: "gpt-4o-mini", + }); + + const network = (process.env.NETWORK_ID || NEAR_TESTNET_NETWORK_ID) as NEAR_NETWORK_ID; + const nearPrivateKey = process.env.NEAR_PRIVATE_KEY as string; + const accountId = process.env.NEAR_ACCOUNT_ID as string; + const rpcProviderUrl = getEndpointsByNetworkId(network); + + let walletProvider; + + if (network === NEAR_MAINNET_NETWORK_ID) { + const keyPair = KeyPair.fromString(nearPrivateKey); + walletProvider = new NearKeypairWalletProvider(keyPair, accountId, rpcProviderUrl, network); + } + + if (network === NEAR_TESTNET_NETWORK_ID) { + if (nearPrivateKey && accountId) { + const keyPair = KeyPair.fromString(nearPrivateKey); + walletProvider = new NearKeypairWalletProvider(keyPair, accountId, rpcProviderUrl, network); + } + + if (accountId && !nearPrivateKey) { + const keystore = new UnencryptedFileSystemKeyStore( + join(process.cwd(), ".near-credentials"), + ); + const keypair = await keystore.getKey(network, accountId); + walletProvider = new NearKeypairWalletProvider(keypair, accountId, rpcProviderUrl, network); + } + + if (!accountId && !nearPrivateKey) { + console.log(`No NEAR account detected. Creating an account...`); + const accountId = await chooseAccount(); + const keyPair = KeyPair.fromRandom("ed25519"); // Generate a new keypair + + const keystore = new UnencryptedFileSystemKeyStore( + join(process.cwd(), ".near-credentials"), + ); + await keystore.setKey("testnet", accountId, keyPair); + + await createFundedTestnetAccount(accountId, keyPair.getPublicKey().toString()); + + console.log(`✅ Account created: ${accountId}`); + + walletProvider = new NearKeypairWalletProvider(keyPair, accountId, rpcProviderUrl, network); + } + } + + if (!walletProvider) { + console.error("Failed to initialize wallet provider"); + process.exit(1); + } + + // Initialize AgentKit + const agentkit = await AgentKit.from({ + walletProvider, + actionProviders: [nearActionProvider(), walletActionProvider()], + }); + + const tools = await getLangChainTools(agentkit); + + // Store buffered conversation history in memory + const memory = new MemorySaver(); + const agentConfig = { configurable: { thread_id: "NEAR AgentKit Chatbot Example!" } }; + + // Create React Agent using the LLM and NEAR AgentKit tools + const agent = createReactAgent({ + llm, + tools, + checkpointSaver: memory, + messageModifier: ` + You are a helpful agent that can interact across chains using NEAR chain signatures and the Coinbase Developer Platform AgentKit. + You are empowered to interact across chains using your tools. Before executing your first action, get the wallet details to see what network + you're on. If there is a 5XX (internal) HTTP error code, ask the user to try again later. + If someone asks you to do something you can't do with your currently available tools, you must say so, and + encourage them to implement it themselves using the CDP SDK + Agentkit, recommend they go to + docs.cdp.coinbase.com for more information. Be concise and helpful with your responses. Refrain from + restating your tools' descriptions unless it is explicitly requested. + `, + }); + + return { agent, config: agentConfig }; + } catch (error) { + console.error("Failed to initialize agent:", error); + throw error; // Re-throw to be handled by caller + } +} + +/** + * Run the agent autonomously with specified intervals + * + * @param agent - The agent executor + * @param config - Agent configuration + * @param interval - Time interval between actions in seconds + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +async function runAutonomousMode(agent: any, config: any, interval = 10) { + console.log("Starting autonomous mode..."); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + const thought = + "Be creative and do something interesting on the blockchain. " + + "Choose an action or set of actions and execute it that highlights your abilities."; + + const stream = await agent.stream({ messages: [new HumanMessage(thought)] }, config); + + for await (const chunk of stream) { + if ("agent" in chunk) { + console.log(chunk.agent.messages[0].content); + } else if ("tools" in chunk) { + console.log(chunk.tools.messages[0].content); + } + console.log("-------------------"); + } + + await new Promise(resolve => setTimeout(resolve, interval * 1000)); + } catch (error) { + if (error instanceof Error) { + console.error("Error:", error.message); + } + process.exit(1); + } + } +} + +/** + * Run the agent interactively based on user input + * + * @param agent - The agent executor + * @param config - Agent configuration + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +async function runChatMode(agent: any, config: any) { + console.log("Starting chat mode... Type 'exit' to end."); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const question = (prompt: string): Promise => + new Promise(resolve => rl.question(prompt, resolve)); + + try { + // eslint-disable-next-line no-constant-condition + while (true) { + const userInput = await question("\nPrompt: "); + + if (userInput.toLowerCase() === "exit") { + break; + } + + const stream = await agent.stream({ messages: [new HumanMessage(userInput)] }, config); + + for await (const chunk of stream) { + if ("agent" in chunk) { + console.log(chunk.agent.messages[0].content); + } else if ("tools" in chunk) { + console.log(chunk.tools.messages[0].content); + } + console.log("-------------------"); + } + } + } catch (error) { + if (error instanceof Error) { + console.error("Error:", error.message); + } + process.exit(1); + } finally { + rl.close(); + } +} + +/** + * Choose whether to run in autonomous or chat mode based on user input + * + * @returns Selected mode + */ +async function chooseMode(): Promise<"chat" | "auto"> { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + const question = (prompt: string): Promise => + new Promise(resolve => rl.question(prompt, resolve)); + + // eslint-disable-next-line no-constant-condition + while (true) { + console.log("\nAvailable modes:"); + console.log("1. chat - Interactive chat mode"); + console.log("2. auto - Autonomous action mode"); + + const choice = (await question("\nChoose a mode (enter number or name): ")) + .toLowerCase() + .trim(); + + if (choice === "1" || choice === "chat") { + rl.close(); + return "chat"; + } else if (choice === "2" || choice === "auto") { + rl.close(); + return "auto"; + } + console.log("Invalid choice. Please try again."); + } +} + +/** + * Start the chatbot agent + */ +async function main() { + try { + const { agent, config } = await initializeAgent(); + const mode = await chooseMode(); + + if (mode === "chat") { + await runChatMode(agent, config); + } else { + await runAutonomousMode(agent, config); + } + } catch (error) { + if (error instanceof Error) { + console.error("Error:", error.message); + } + process.exit(1); + } +} + +if (require.main === module) { + console.log("Starting Agent..."); + main().catch(error => { + console.error("Fatal error:", error); + process.exit(1); + }); +} diff --git a/typescript/examples/langchain-near-chatbot/package.json b/typescript/examples/langchain-near-chatbot/package.json new file mode 100644 index 000000000..e29331fd6 --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/package.json @@ -0,0 +1,32 @@ +{ + "name": "@coinbase/near-langchain-chatbot-example", + "description": "CDP Agentkit Node.js SDK Chatbot Example", + "version": "1.0.0", + "author": "Coinbase Inc.", + "license": "Apache-2.0", + "scripts": { + "start": "NODE_OPTIONS='--no-warnings' ts-node ./chatbot.ts", + "dev": "nodemon ./chatbot.ts", + "lint": "eslint -c .eslintrc.json *.ts", + "lint:fix": "eslint -c .eslintrc.json *.ts --fix", + "format": "prettier --write \"**/*.{ts,js,cjs,json,md}\"", + "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"" + }, + "dependencies": { + "@coinbase/agentkit": "workspace:*", + "@coinbase/agentkit-langchain": "workspace:*", + "@langchain/core": "^0.3.19", + "@langchain/langgraph": "^0.2.21", + "@langchain/openai": "^0.3.14", + "@near-js/client": "^0.0.2", + "@near-js/crypto": "^1.4.1", + "@near-js/keystores-node": "^0.1.1", + "@near-js/types": "^0.3.1", + "dotenv": "^16.4.5", + "zod": "^3.22.4" + }, + "devDependencies": { + "nodemon": "^3.1.0", + "ts-node": "^10.9.2" + } +} diff --git a/typescript/examples/langchain-near-chatbot/tsconfig.json b/typescript/examples/langchain-near-chatbot/tsconfig.json new file mode 100644 index 000000000..a37da3664 --- /dev/null +++ b/typescript/examples/langchain-near-chatbot/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "preserveSymlinks": true, + "outDir": "./dist", + "rootDir": ".", + "module": "Node16" + }, + "include": ["*.ts"] +} diff --git a/typescript/package.json b/typescript/package.json index 33505fa6b..69f8184ef 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -55,4 +55,4 @@ "typedoc": "^0.27.2", "typescript": "^5.4.5" } -} +} \ No newline at end of file diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index c3deaa06e..203e4f0d4 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -77,6 +77,30 @@ importers: '@jup-ag/api': specifier: ^6.0.39 version: 6.0.40 + '@near-js/accounts': + specifier: ^1.3.1 + version: 1.4.1 + '@near-js/crypto': + specifier: ^1.4.2 + version: 1.4.2 + '@near-js/keystores': + specifier: ^0.2.1 + version: 0.2.2 + '@near-js/providers': + specifier: ^1.0.1 + version: 1.0.3 + '@near-js/signers': + specifier: ^0.2.1 + version: 0.2.2 + '@near-js/transactions': + specifier: ^1.3.1 + version: 1.3.3 + '@near-js/types': + specifier: ^0.3.1 + version: 0.3.1 + '@near-js/utils': + specifier: ^1.0.1 + version: 1.1.0 '@privy-io/public-api': specifier: ^2.18.5 version: 2.20.5(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10) @@ -107,12 +131,21 @@ importers: decimal.js: specifier: ^10.5.0 version: 10.5.0 + elliptic: + specifier: ^6.6.1 + version: 6.6.1 ethers: specifier: ^6.13.5 version: 6.13.5(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-sha3: + specifier: ^0.9.3 + version: 0.9.3 md5: specifier: ^2.3.0 version: 2.3.0 + near-api-js: + specifier: ^5.0.1 + version: 5.1.1 opensea-js: specifier: ^7.1.18 version: 7.1.18(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -424,6 +457,49 @@ importers: specifier: ^10.9.2 version: 10.9.2(@types/node@22.13.14)(typescript@5.8.2) + examples/langchain-near-chatbot: + dependencies: + '@coinbase/agentkit': + specifier: workspace:* + version: link:../../agentkit + '@coinbase/agentkit-langchain': + specifier: workspace:* + version: link:../../framework-extensions/langchain + '@langchain/core': + specifier: ^0.3.19 + version: 0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) + '@langchain/langgraph': + specifier: ^0.2.21 + version: 0.2.59(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.1)(zod-to-json-schema@3.24.5(zod@3.24.2)) + '@langchain/openai': + specifier: ^0.3.14 + version: 0.3.17(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@near-js/client': + specifier: ^0.0.2 + version: 0.0.2 + '@near-js/crypto': + specifier: ^1.4.1 + version: 1.4.2 + '@near-js/keystores-node': + specifier: ^0.1.1 + version: 0.1.2 + '@near-js/types': + specifier: ^0.3.1 + version: 0.3.1 + dotenv: + specifier: ^16.4.5 + version: 16.4.7 + zod: + specifier: ^3.22.4 + version: 3.24.2 + devDependencies: + nodemon: + specifier: ^3.1.0 + version: 3.1.9 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.13.14)(typescript@5.8.2) + examples/langchain-privy-chatbot: dependencies: '@coinbase/agentkit': @@ -1544,6 +1620,60 @@ packages: '@napi-rs/wasm-runtime@0.2.7': resolution: {integrity: sha512-5yximcFK5FNompXfJFoWanu5l8v1hNGqNHh9du1xETp9HWk/B/PzvchX55WYOPaIeNglG8++68AAiauBAtbnzw==} + '@near-js/accounts@1.4.1': + resolution: {integrity: sha512-ni3QT9H3NdrbVVKyx56yvz93r89Dvpc/vgVtiIK2OdXjkK6jcj+UKMDRQ6F7rd9qJOInLkHZbVBtcR6j1CXLjw==} + + '@near-js/client@0.0.2': + resolution: {integrity: sha512-L/H6IJGmjk3iaNt+tZQGDzlhF6LQvFjeUNLFxLPg1fOw71EmwssHroKDSo6Xowif9/twPslbNge0KOy1cplpHA==} + + '@near-js/crypto@1.4.1': + resolution: {integrity: sha512-hbricJD0H8nwu63Zw16UZQg3ms2W9NwDBsLt3OEtudTcu9q1MRrVZWc7ATjdmTvhkcgmouEFc6oLBsOxnmSLCA==} + + '@near-js/crypto@1.4.2': + resolution: {integrity: sha512-GRfchsyfWvSAPA1gI9hYhw5FH94Ac1BUo+Cmp5rSJt/V0K3xVzCWgOQxvv4R3kDnWjaXJEuAmpEEnr4Bp3FWrA==} + + '@near-js/keystores-browser@0.2.2': + resolution: {integrity: sha512-Pxqm7WGtUu6zj32vGCy9JcEDpZDSB5CCaLQDTQdF3GQyL0flyRv2I/guLAgU5FLoYxU7dJAX9mslJhPW7P2Bfw==} + + '@near-js/keystores-node@0.1.2': + resolution: {integrity: sha512-MWLvTszZOVziiasqIT/LYNhUyWqOJjDGlsthOsY6dTL4ZcXjjmhmzrbFydIIeQr+CcEl5wukTo68ORI9JrHl6g==} + + '@near-js/keystores@0.2.1': + resolution: {integrity: sha512-KTeqSB+gx5LZNC9VGtHDe+aEiJts6e3nctMnnn/gqIgvW7KJ+BzcmTZZpxCmQLcy+s7hHSpzmyTVRkaCuYjCcQ==} + + '@near-js/keystores@0.2.2': + resolution: {integrity: sha512-DLhi/3a4qJUY+wgphw2Jl4S+L0AKsUYm1mtU0WxKYV5OBwjOXvbGrXNfdkheYkfh3nHwrQgtjvtszX6LrRXLLw==} + + '@near-js/providers@1.0.1': + resolution: {integrity: sha512-a1rU+JjTes/fdpnP/SLRQuWAK80os1DoHw2sszg/ccA9byTdI/CM6eKinrWJrO5i86IARfigOgjCJhrzPscvuQ==} + + '@near-js/providers@1.0.3': + resolution: {integrity: sha512-VJMboL14R/+MGKnlhhE3UPXCGYvMd1PpvF9OqZ9yBbulV7QVSIdTMfY4U1NnDfmUC2S3/rhAEr+3rMrIcNS7Fg==} + + '@near-js/signers@0.2.1': + resolution: {integrity: sha512-l1PnUy4e8NQe5AAHs7mEuWbbUt0rrsZLtcK1UlFaA16MKZmxXdHLMBfUmzyMA4bGzwkyUyGtIebkR+KjBfpEog==} + + '@near-js/signers@0.2.2': + resolution: {integrity: sha512-M6ib+af9zXAPRCjH2RyIS0+RhCmd9gxzCeIkQ+I2A3zjgGiEDkBZbYso9aKj8Zh2lPKKSH7h+u8JGymMOSwgyw==} + + '@near-js/transactions@1.3.1': + resolution: {integrity: sha512-kL9hxUqBr+tILQHFsh5T/bz3UkJrAq5tnyFqh0xf+7qGXZuRIPfuW/HMq4M6wFw0MGi/8ycmDT3yTQFH7PzZqw==} + + '@near-js/transactions@1.3.3': + resolution: {integrity: sha512-1AXD+HuxlxYQmRTLQlkVmH+RAmV3HwkAT8dyZDu+I2fK/Ec9BQHXakOJUnOBws3ihF+akQhamIBS5T0EXX/Ylw==} + + '@near-js/types@0.3.1': + resolution: {integrity: sha512-8qIA7ynAEAuVFNAQc0cqz2xRbfyJH3PaAG5J2MgPPhD18lu/tCGd6pzYg45hjhtiJJRFDRjh/FUWKS+ZiIIxUw==} + + '@near-js/utils@1.0.1': + resolution: {integrity: sha512-MzCAspVJJLrURnSbq059s6cWon2/qbbBVl+Ib1yBOMTs/6EuJ7GRvuSmtmSB7l9Hjjmz8Imn1aB2q3RVYZSbrA==} + + '@near-js/utils@1.1.0': + resolution: {integrity: sha512-5XWRq7xpu8Wud9pRXe2U347KXyi0mXofedUY2DQ9TaqiZUcMIaN9xj7DbCs2v6dws3pJyYrT1KWxeNp5fSaY3w==} + + '@near-js/wallet-account@1.3.3': + resolution: {integrity: sha512-GDzg/Kz0GBYF7tQfyQQQZ3vviwV8yD+8F2lYDzsWJiqIln7R1ov0zaXN4Tii86TeS21KPn2hHAsVu3Y4txa8OQ==} + '@next/env@14.2.15': resolution: {integrity: sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ==} @@ -1626,6 +1756,10 @@ packages: resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} engines: {node: '>= 16'} + '@noble/hashes@1.3.3': + resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} + engines: {node: '>= 16'} + '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} @@ -2690,6 +2824,11 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base-x@2.0.6: + resolution: {integrity: sha512-UAmjxz9KbK+YIi66xej+pZVo/vxUOh49ubEvZW5egCbxhur05pBb+hwuireQwKO4nDpsNm64/jEei17LEpsr5g==} + engines: {node: '>=4.5.0'} + deprecated: use 3.0.0 instead, safe-buffer has been merged and release for compatability + base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} @@ -2750,6 +2889,9 @@ packages: borsh@0.7.0: resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} + borsh@1.0.0: + resolution: {integrity: sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ==} + bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} @@ -2775,6 +2917,9 @@ packages: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} + bs58@4.0.0: + resolution: {integrity: sha512-/jcGuUuSebyxwLLfKrbKnCJttxRf9PM51EnHTwmFKBxl4z1SGkoAhrfd6uZKE0dcjQTfm6XzTP8DPr1tzE4KIw==} + bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} @@ -3178,6 +3323,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -3609,6 +3758,9 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + exponential-backoff@3.1.2: + resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + express-rate-limit@7.5.0: resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} engines: {node: '>= 16'} @@ -3786,6 +3938,12 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + + generate-object-property@1.2.0: + resolution: {integrity: sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3946,6 +4104,10 @@ packages: html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + http-errors@1.7.2: + resolution: {integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==} + engines: {node: '>= 0.6'} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -4016,6 +4178,9 @@ packages: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -4138,6 +4303,12 @@ packages: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} + is-my-ip-valid@1.0.1: + resolution: {integrity: sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg==} + + is-my-json-valid@2.20.6: + resolution: {integrity: sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw==} + is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -4161,6 +4332,9 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -4234,6 +4408,12 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-fetch@3.0.0: + resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} + + isomorphic-unfetch@3.1.0: + resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==} + isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: @@ -4435,6 +4615,9 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-sha3@0.9.3: + resolution: {integrity: sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg==} + js-tiktoken@1.0.19: resolution: {integrity: sha512-XC63YQeEcS47Y53gg950xiZ4IWmkfMe4p2V9OSaBt26q+p47WHn18izuXzSclCI73B7yGqtfRsT6jcZQI0y08g==} @@ -4504,6 +4687,10 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -4627,6 +4814,9 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + lru_map@0.4.1: + resolution: {integrity: sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==} + lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -4882,6 +5072,11 @@ packages: multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + mustache@4.0.0: + resolution: {integrity: sha512-FJgjyX/IVkbXBXYUwH+OYwQKqWpFPLaLVESd70yHjSDunwzV2hZOoTBvPf4KLoxesUzzyfTH6F784Uqd7Wm5yA==} + engines: {npm: '>=1.4.0'} + hasBin: true + mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true @@ -4897,6 +5092,12 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + near-abi@0.2.0: + resolution: {integrity: sha512-kCwSf/3fraPU2zENK18sh+kKG4uKbEUEQdyWQkmW8ZofmLarObIz2+zAYjA1teDZLeMvEQew3UysnPDXgjneaA==} + + near-api-js@5.1.1: + resolution: {integrity: sha512-h23BGSKxNv8ph+zU6snicstsVK1/CTXsQz4LuGGwoRE24Hj424nSe4+/1tzoiC285Ljf60kPAqRCmsfv9etF2g==} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -4932,6 +5133,15 @@ packages: node-fetch-native@1.6.6: resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} + node-fetch@2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -5612,6 +5822,7 @@ packages: rspack-resolver@1.3.0: resolution: {integrity: sha512-az/PLDwa1xijNv4bAFBS8mtqqJC1Y3lVyFag4cuyIUOHq/ft5kSZlHbqYaLZLpsQtPWv4ZGDo5ycySKJzUvU/A==} + deprecated: Please migrate to the brand new `@rspack/resolver` or `unrs-resolver` instead run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -5647,6 +5858,10 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + secp256k1@5.0.0: + resolution: {integrity: sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==} + engines: {node: '>=14.0.0'} + secp256k1@5.0.1: resolution: {integrity: sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==} engines: {node: '>=18.0.0'} @@ -5693,6 +5908,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setprototypeof@1.1.1: + resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -5810,6 +6028,10 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -6042,6 +6264,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toidentifier@1.0.0: + resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==} + engines: {node: '>=0.6'} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -6311,6 +6537,9 @@ packages: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} + unfetch@4.2.0: + resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -8018,6 +8247,160 @@ snapshots: '@tybys/wasm-util': 0.9.0 optional: true + '@near-js/accounts@1.4.1': + dependencies: + '@near-js/crypto': 1.4.2 + '@near-js/providers': 1.0.3 + '@near-js/signers': 0.2.2 + '@near-js/transactions': 1.3.3 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.1.0 + '@noble/hashes': 1.7.1 + borsh: 1.0.0 + depd: 2.0.0 + is-my-json-valid: 2.20.6 + lru_map: 0.4.1 + near-abi: 0.2.0 + transitivePeerDependencies: + - encoding + + '@near-js/client@0.0.2': + dependencies: + '@near-js/crypto': 1.4.1 + '@near-js/keystores': 0.2.1 + '@near-js/providers': 1.0.1 + '@near-js/signers': 0.2.1 + '@near-js/transactions': 1.3.1 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.0.1 + '@noble/hashes': 1.3.3 + isomorphic-fetch: 3.0.0 + transitivePeerDependencies: + - encoding + + '@near-js/crypto@1.4.1': + dependencies: + '@near-js/types': 0.3.1 + '@near-js/utils': 1.0.1 + '@noble/curves': 1.2.0 + borsh: 1.0.0 + randombytes: 2.1.0 + secp256k1: 5.0.0 + + '@near-js/crypto@1.4.2': + dependencies: + '@near-js/types': 0.3.1 + '@near-js/utils': 1.1.0 + '@noble/curves': 1.8.1 + borsh: 1.0.0 + randombytes: 2.1.0 + secp256k1: 5.0.1 + + '@near-js/keystores-browser@0.2.2': + dependencies: + '@near-js/crypto': 1.4.2 + '@near-js/keystores': 0.2.2 + + '@near-js/keystores-node@0.1.2': + dependencies: + '@near-js/crypto': 1.4.2 + '@near-js/keystores': 0.2.2 + + '@near-js/keystores@0.2.1': + dependencies: + '@near-js/crypto': 1.4.1 + '@near-js/types': 0.3.1 + + '@near-js/keystores@0.2.2': + dependencies: + '@near-js/crypto': 1.4.2 + '@near-js/types': 0.3.1 + + '@near-js/providers@1.0.1': + dependencies: + '@near-js/transactions': 1.3.1 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.0.1 + borsh: 1.0.0 + exponential-backoff: 3.1.2 + isomorphic-unfetch: 3.1.0 + optionalDependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + + '@near-js/providers@1.0.3': + dependencies: + '@near-js/transactions': 1.3.3 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.1.0 + borsh: 1.0.0 + exponential-backoff: 3.1.2 + optionalDependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + + '@near-js/signers@0.2.1': + dependencies: + '@near-js/crypto': 1.4.1 + '@near-js/keystores': 0.2.1 + '@noble/hashes': 1.3.3 + + '@near-js/signers@0.2.2': + dependencies: + '@near-js/crypto': 1.4.2 + '@near-js/keystores': 0.2.2 + '@noble/hashes': 1.3.3 + + '@near-js/transactions@1.3.1': + dependencies: + '@near-js/crypto': 1.4.1 + '@near-js/signers': 0.2.1 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.0.1 + '@noble/hashes': 1.3.3 + borsh: 1.0.0 + + '@near-js/transactions@1.3.3': + dependencies: + '@near-js/crypto': 1.4.2 + '@near-js/signers': 0.2.2 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.1.0 + '@noble/hashes': 1.7.1 + borsh: 1.0.0 + + '@near-js/types@0.3.1': {} + + '@near-js/utils@1.0.1': + dependencies: + '@near-js/types': 0.3.1 + bs58: 4.0.0 + depd: 2.0.0 + mustache: 4.0.0 + + '@near-js/utils@1.1.0': + dependencies: + '@near-js/types': 0.3.1 + '@scure/base': 1.2.4 + depd: 2.0.0 + mustache: 4.0.0 + + '@near-js/wallet-account@1.3.3': + dependencies: + '@near-js/accounts': 1.4.1 + '@near-js/crypto': 1.4.2 + '@near-js/keystores': 0.2.2 + '@near-js/providers': 1.0.3 + '@near-js/signers': 0.2.2 + '@near-js/transactions': 1.3.3 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.1.0 + borsh: 1.0.0 + transitivePeerDependencies: + - encoding + '@next/env@14.2.15': {} '@next/eslint-plugin-next@14.2.15': @@ -8071,6 +8454,8 @@ snapshots: '@noble/hashes@1.3.2': {} + '@noble/hashes@1.3.3': {} + '@noble/hashes@1.4.0': {} '@noble/hashes@1.7.0': {} @@ -9579,6 +9964,10 @@ snapshots: balanced-match@1.0.2: {} + base-x@2.0.6: + dependencies: + safe-buffer: 5.2.1 + base-x@3.0.11: dependencies: safe-buffer: 5.2.1 @@ -9652,6 +10041,8 @@ snapshots: bs58: 4.0.1 text-encoding-utf-8: 1.0.2 + borsh@1.0.0: {} + bowser@2.11.0: {} brace-expansion@1.1.11: @@ -9680,6 +10071,10 @@ snapshots: dependencies: fast-json-stable-stringify: 2.1.0 + bs58@4.0.0: + dependencies: + base-x: 2.0.6 + bs58@4.0.1: dependencies: base-x: 3.0.11 @@ -10049,6 +10444,8 @@ snapshots: delayed-stream@1.0.0: {} + depd@1.1.2: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -10332,7 +10729,7 @@ snapshots: '@typescript-eslint/parser': 8.28.0(eslint@8.57.1)(typescript@5.8.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-typescript@3.9.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.4(eslint@8.57.1) @@ -10367,7 +10764,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1): + eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0(supports-color@5.5.0) @@ -10382,14 +10779,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.28.0(eslint@8.57.1)(typescript@5.8.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.9.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -10404,7 +10801,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.28.0(eslint@8.57.1)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.9.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -10671,6 +11068,8 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 + exponential-backoff@3.1.2: {} + express-rate-limit@7.5.0(express@5.0.1): dependencies: express: 5.0.1 @@ -10873,6 +11272,14 @@ snapshots: functions-have-names@1.2.3: {} + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + + generate-object-property@1.2.0: + dependencies: + is-property: 1.0.2 + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -11068,6 +11475,14 @@ snapshots: html-url-attributes@3.0.1: {} + http-errors@1.7.2: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.1 + statuses: 1.5.0 + toidentifier: 1.0.0 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -11150,6 +11565,8 @@ snapshots: once: 1.4.0 wrappy: 1.0.2 + inherits@2.0.3: {} + inherits@2.0.4: {} inline-style-parser@0.2.4: {} @@ -11263,6 +11680,16 @@ snapshots: is-map@2.0.3: {} + is-my-ip-valid@1.0.1: {} + + is-my-json-valid@2.20.6: + dependencies: + generate-function: 2.3.1 + generate-object-property: 1.2.0 + is-my-ip-valid: 1.0.1 + jsonpointer: 5.0.1 + xtend: 4.0.2 + is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -11278,6 +11705,8 @@ snapshots: is-promise@4.0.0: {} + is-property@1.0.2: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -11339,6 +11768,20 @@ snapshots: isexe@2.0.0: {} + isomorphic-fetch@3.0.0: + dependencies: + node-fetch: 2.7.0 + whatwg-fetch: 3.6.20 + transitivePeerDependencies: + - encoding + + isomorphic-unfetch@3.1.0: + dependencies: + node-fetch: 2.7.0 + unfetch: 4.2.0 + transitivePeerDependencies: + - encoding + isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -11850,6 +12293,8 @@ snapshots: joycon@3.1.1: {} + js-sha3@0.9.3: {} + js-tiktoken@1.0.19: dependencies: base64-js: 1.5.1 @@ -11906,6 +12351,8 @@ snapshots: jsonparse@1.3.1: {} + jsonpointer@5.0.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -12041,6 +12488,8 @@ snapshots: dependencies: yallist: 4.0.0 + lru_map@0.4.1: {} + lunr@2.3.9: {} make-dir@4.0.0: @@ -12421,6 +12870,8 @@ snapshots: multiformats@9.9.0: {} + mustache@4.0.0: {} + mustache@4.2.0: {} mz@2.7.0: @@ -12433,6 +12884,32 @@ snapshots: natural-compare@1.4.0: {} + near-abi@0.2.0: + dependencies: + '@types/json-schema': 7.0.15 + + near-api-js@5.1.1: + dependencies: + '@near-js/accounts': 1.4.1 + '@near-js/crypto': 1.4.2 + '@near-js/keystores': 0.2.2 + '@near-js/keystores-browser': 0.2.2 + '@near-js/keystores-node': 0.1.2 + '@near-js/providers': 1.0.3 + '@near-js/signers': 0.2.2 + '@near-js/transactions': 1.3.3 + '@near-js/types': 0.3.1 + '@near-js/utils': 1.1.0 + '@near-js/wallet-account': 1.3.3 + '@noble/curves': 1.8.1 + borsh: 1.0.0 + depd: 2.0.0 + http-errors: 1.7.2 + near-abi: 0.2.0 + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + negotiator@1.0.0: {} next@14.2.15(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -12469,6 +12946,10 @@ snapshots: node-fetch-native@1.6.6: {} + node-fetch@2.6.7: + dependencies: + whatwg-url: 5.0.0 + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -13318,6 +13799,12 @@ snapshots: dependencies: loose-envify: 1.4.0 + secp256k1@5.0.0: + dependencies: + elliptic: 6.6.1 + node-addon-api: 5.1.0 + node-gyp-build: 4.8.4 + secp256k1@5.0.1: dependencies: elliptic: 6.6.1 @@ -13384,6 +13871,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setprototypeof@1.1.1: {} + setprototypeof@1.2.0: {} sha.js@2.4.11: @@ -13512,6 +14001,8 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 + statuses@1.5.0: {} + statuses@2.0.1: {} stdin-discarder@0.1.0: @@ -13796,6 +14287,8 @@ snapshots: dependencies: is-number: 7.0.0 + toidentifier@1.0.0: {} + toidentifier@1.0.1: {} touch@3.1.1: {} @@ -14112,6 +14605,8 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 + unfetch@4.2.0: {} + unified@11.0.5: dependencies: '@types/unist': 3.0.3