diff --git a/packages/evm/src/contracts/erc721/ERC721ContractClient.ts b/packages/evm/src/contracts/erc721/ERC721ContractClient.ts new file mode 100644 index 0000000..e44bb6b --- /dev/null +++ b/packages/evm/src/contracts/erc721/ERC721ContractClient.ts @@ -0,0 +1,90 @@ +import { erc721Abi, type Address, type Hash } from 'viem'; +import { ContractClientBase, type ContractClientBaseOptions } from '../ContractClientBase'; +import type { ERC721TokenMetadata } from '../types'; + +export type ERC721ContractClientOptions = Omit; + +export class ERC721ContractClient extends ContractClientBase { + constructor(options: ERC721ContractClientOptions) { + super({ ...options, abi: erc721Abi }); + } + + async name() { + const result = await this.readContract({ functionName: 'name' }); + return result as string; + } + + async symbol() { + const result = await this.readContract({ functionName: 'symbol' }); + return result as string; + } + + async totalSupply() { + const result = await this.readContract({ functionName: 'totalSupply' }); + return result as bigint; + } + + async balanceOf(owner: Address) { + const result = await this.readContract({ functionName: 'balanceOf', args: [owner] }); + return result as bigint; + } + + async ownerOf(tokenId: bigint) { + const result = await this.readContract({ functionName: 'ownerOf', args: [tokenId] }); + return result as Address; + } + + async approve(to: Address, tokenId: bigint) { + const result = await this.simulateAndWriteContract({ functionName: 'approve', args: [to, tokenId] }); + return result as Hash; + } + + async setApprovalForAll(operator: Address, approved: boolean) { + const result = await this.simulateAndWriteContract({ + functionName: 'setApprovalForAll', + args: [operator, approved], + }); + return result as Hash; + } + + async transferFrom(from: Address, to: Address, tokenId: bigint) { + const result = await this.simulateAndWriteContract({ + functionName: 'transferFrom', + args: [from, to, tokenId], + }); + return result as Hash; + } + + async safeTransferFrom(from: Address, to: Address, tokenId: bigint, data?: `0x${string}`) { + const args = data ? [from, to, tokenId, data] : [from, to, tokenId]; + const result = await this.simulateAndWriteContract({ + functionName: 'safeTransferFrom', + args, + }); + return result as Hash; + } + + async tokenURI(tokenId: bigint) { + const result = await this.readContract({ functionName: 'tokenURI', args: [tokenId] }); + return result as string; + } + + async tokenMetadata() { + const baseParams = { address: this.contractAddress, abi: this.abi }; + const multicallFunctions: { functionName: string; args?: unknown[] }[] = [ + { functionName: 'name' }, + { functionName: 'symbol' }, + { functionName: 'totalSupply' }, + ]; + const [name, symbol, totalSupply] = await this.multicall({ + contracts: multicallFunctions.map((item) => ({ + ...baseParams, + functionName: item.functionName, + args: item.args, + })), + allowFailure: false, + }); + + return { name, symbol, totalSupply } as ERC721TokenMetadata; + } +} diff --git a/packages/evm/src/contracts/types.ts b/packages/evm/src/contracts/types.ts index d3e9b6b..d65acdd 100644 --- a/packages/evm/src/contracts/types.ts +++ b/packages/evm/src/contracts/types.ts @@ -4,3 +4,9 @@ export type ERC20TokenMetadata = { totalSupply: bigint; decimals: number; }; + +export type ERC721TokenMetadata = { + name: string; + symbol: string; + totalSupply: bigint; +}; diff --git a/packages/evm/src/index.ts b/packages/evm/src/index.ts index 746fe78..3501a8b 100644 --- a/packages/evm/src/index.ts +++ b/packages/evm/src/index.ts @@ -3,4 +3,5 @@ export { verifyEIP712Message, verifyMessage } from './utils'; // contracts export { ContractClientBase, type ContractClientBaseOptions } from './contracts/ContractClientBase'; export { ERC20ContractClient, type ERC20ContractClientOptions } from './contracts/erc20/ERC20ContractClient'; -export { type ERC20TokenMetadata } from './contracts/types'; +export { ERC721ContractClient, type ERC721ContractClientOptions } from './contracts/erc721/ERC721ContractClient'; +export { type ERC20TokenMetadata, type ERC721TokenMetadata } from './contracts/types'; diff --git a/packages/evm/tests/contracts.test.ts b/packages/evm/tests/contracts.test.ts index 75f90f6..5f9211e 100644 --- a/packages/evm/tests/contracts.test.ts +++ b/packages/evm/tests/contracts.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'vitest'; -import { sepolia } from 'viem/chains'; -import { ERC20ContractClient } from '../src'; +import { mainnet, sepolia } from 'viem/chains'; +import { ERC20ContractClient, ERC721ContractClient } from '../src'; describe('contracts tests', () => { test('erc20 tests', async () => { @@ -26,4 +26,27 @@ describe('contracts tests', () => { expect(metadata).toStrictEqual({ name: 'USDC', symbol: 'USDC', totalSupply: 411951814911779444n, decimals: 6 }); }); + + test('erc721 tests', async () => { + //https://opensea.io/collection/seeing-signs + const erc721Contract = new ERC721ContractClient({ + chain: mainnet, + contractAddress: '0xbc37ee54f066e79c23389c55925f877f79f3cb84', + }); + + const metadata = await erc721Contract.tokenMetadata(); + + const [name, symbol, totalSupply] = await Promise.all([ + erc721Contract.name(), + erc721Contract.symbol(), + erc721Contract.totalSupply(), + ]); + + expect(metadata.name).toBe(name); + expect(metadata.symbol).toBe(symbol); + expect(metadata.totalSupply).toBe(totalSupply); + + const balance = await erc721Contract.balanceOf('0x303dD5e268855d6A04f3E44f33201180EDee7aFe'); + expect(balance).toBe(0n); + }); }); diff --git a/packages/svm/package.json b/packages/svm/package.json index fae49a5..49040f0 100644 --- a/packages/svm/package.json +++ b/packages/svm/package.json @@ -39,7 +39,8 @@ "@metaplex-foundation/umi-bundle-defaults": "^1.2.0", "@noble/ed25519": "^2.3.0", "@solana/spl-token-metadata": "^0.1.6", - "bs58": "^6.0.0" + "bs58": "^6.0.0", + "@coral-xyz/anchor": "^0.30.1" }, "devDependencies": { "@solana/spl-token": "^0.4.12", diff --git a/packages/svm/src/contracts/ContractClientBase.ts b/packages/svm/src/contracts/ContractClientBase.ts new file mode 100644 index 0000000..1c638dc --- /dev/null +++ b/packages/svm/src/contracts/ContractClientBase.ts @@ -0,0 +1,126 @@ +import { Program, AnchorProvider, setProvider, Wallet } from '@coral-xyz/anchor'; +import type { Idl } from '@coral-xyz/anchor'; +import type { + AllAccountsMap, + AllInstructions, + IdlAccounts, + MakeMethodsNamespace, +} from '@coral-xyz/anchor/dist/cjs/program/namespace/types'; +import { clusterApiUrl, Connection, PublicKey } from '@solana/web3.js'; +import type { Cluster, Commitment, Signer } from '@solana/web3.js'; + +export interface ContractClientBaseOptions { + chainId?: string; + idl?: any; + walletClient: Wallet; + endpoint: string; +} + +export abstract class ContractClientBase { + protected program: Program; + protected connection: Connection; + protected wallet: Wallet; + protected chainId: string; + endpoint: string; + + constructor(contractInfo: ContractClientBaseOptions) { + const { idl, walletClient, chainId, endpoint } = contractInfo; + this.chainId = chainId || 'mainnet-beta'; + + this.connection = this.getConnection(); + this.wallet = walletClient; + this.endpoint = endpoint; + + const provider = new AnchorProvider(this.connection, this.wallet, { commitment: 'confirmed' }); + setProvider(provider); + + this.program = new Program(idl, provider); + } + + get programId() { + return this.program.programId; + } + + protected getConnection() { + const rpcUrl = this.endpoint ? this.endpoint : clusterApiUrl((this.chainId as Cluster) ?? 'mainnet-beta'); + + const conn = new Connection(rpcUrl); + return conn; + } + + protected async findProgramAddress( + seeds: Array, + programId?: PublicKey, + ): Promise<[PublicKey, number]> { + return await PublicKey.findProgramAddressSync(seeds, programId || new PublicKey(this.program.idl.address)); + } + + async fetchAccountData>( + accountName: A, + address: PublicKey | string, + commitment?: Commitment, + ): Promise[A]> { + try { + if (!this.program.account[accountName]) { + throw new Error(`Account ${String(accountName)} not found in program`); + } + + const account = await this.program.account[accountName].fetch(address, commitment); + return account as IdlAccounts[A]; + } catch (error) { + throw error; + } + } + + async invokeView>>( + methodName: M, + args: Parameters>[M]>, + ): Promise { + try { + if (!this.program.methods[methodName]) { + throw new Error(`Method ${methodName} not found in program`); + } + + const result = await this.program.methods[methodName](...args).view(); + + return result; + } catch (error) { + throw error; + } + } + + async invokeInstruction>>( + methodName: M, + accounts: any, + args: any, + options?: { signers?: Signer[] }, + ): Promise { + if (!this.program.methods[methodName]) { + throw new Error(`Method ${methodName} not found in program`); + } + let instruction = this.program.methods[methodName](...args).accounts(accounts); + + if (options?.signers?.length) { + instruction = instruction.signers(options.signers); + } + + const tx = await instruction.rpc(); + + await this.confirmTransaction(tx); + + return tx; + } + + async confirmTransaction(tx: string): Promise { + const latestBlockhash = await this.connection.getLatestBlockhash(); + await this.connection.confirmTransaction( + { + signature: tx, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }, + 'finalized', + ); + return tx; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4958705..98c02ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -105,6 +105,9 @@ importers: '@bizjs/chainkit-utils': specifier: workspace:* version: link:../utils + '@coral-xyz/anchor': + specifier: ^0.30.1 + version: 0.30.1(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10) '@metaplex-foundation/mpl-token-metadata': specifier: ^3.4.0 version: 3.4.0(@metaplex-foundation/umi@1.2.0) @@ -233,6 +236,20 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@coral-xyz/anchor-errors@0.30.1': + resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} + engines: {node: '>=10'} + + '@coral-xyz/anchor@0.30.1': + resolution: {integrity: sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==} + engines: {node: '>=11'} + + '@coral-xyz/borsh@0.30.1': + resolution: {integrity: sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==} + engines: {node: '>=10'} + peerDependencies: + '@solana/web3.js': ^1.68.0 + '@emurgo/cardano-message-signing-nodejs@1.1.0': resolution: {integrity: sha512-PQRc8K8wZshEdmQenNUzVtiI8oJNF/1uAnBhidee5C4o1l2mDLOW+ur46HWHIFKQ6x8mSJTllcjMscHgzju0gQ==} @@ -1173,6 +1190,10 @@ packages: buffer-compare@1.1.1: resolution: {integrity: sha512-O6NvNiHZMd3mlIeMDjP6t/gPG75OqGPeiRZXoMQZJ6iy9GofCls4Ijs5YkPZZwoysizLiedhticmdyx/GyHghA==} + buffer-layout@1.2.2: + resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} + engines: {node: '>=4.5'} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -1213,6 +1234,10 @@ packages: resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} engines: {node: '>= 0.4'} + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + cardinal@2.1.1: resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} hasBin: true @@ -1290,10 +1315,17 @@ packages: create-hash@1.2.0: resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crypto-hash@1.3.0: + resolution: {integrity: sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==} + engines: {node: '>=8'} + dataloader@2.2.3: resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==} @@ -1345,6 +1377,9 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1420,6 +1455,9 @@ packages: ethereum-cryptography@2.2.1: resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -1760,6 +1798,9 @@ packages: loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -1833,6 +1874,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} @@ -2139,6 +2183,9 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2190,6 +2237,9 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + superstruct@0.15.5: + resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} + superstruct@2.0.2: resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} engines: {node: '>=14.0.0'} @@ -2255,6 +2305,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -2717,6 +2770,37 @@ snapshots: human-id: 4.1.1 prettier: 2.8.8 + '@coral-xyz/anchor-errors@0.30.1': {} + + '@coral-xyz/anchor@0.30.1(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor-errors': 0.30.1 + '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10)) + '@noble/hashes': 1.8.0 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10) + bn.js: 5.2.1 + bs58: 4.0.1 + buffer-layout: 1.2.2 + camelcase: 6.3.0 + cross-fetch: 3.2.0 + crypto-hash: 1.3.0 + eventemitter3: 4.0.7 + pako: 2.1.0 + snake-case: 3.0.4 + superstruct: 0.15.5 + toml: 3.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@coral-xyz/borsh@0.30.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.7.3)(utf-8-validate@5.0.10) + bn.js: 5.2.1 + buffer-layout: 1.2.2 + '@emurgo/cardano-message-signing-nodejs@1.1.0': {} '@emurgo/cardano-serialization-lib-nodejs@13.2.1': {} @@ -3737,6 +3821,8 @@ snapshots: buffer-compare@1.1.1: {} + buffer-layout@1.2.2: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -3788,6 +3874,8 @@ snapshots: call-bind-apply-helpers: 1.0.1 get-intrinsic: 1.2.7 + camelcase@6.3.0: {} + cardinal@2.1.1: dependencies: ansicolors: 0.3.2 @@ -3862,12 +3950,20 @@ snapshots: ripemd160: 2.0.2 sha.js: 2.4.11 + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + crypto-hash@1.3.0: {} + dataloader@2.2.3: {} debug@4.4.0: @@ -3906,6 +4002,11 @@ snapshots: dependencies: path-type: 4.0.0 + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.1 @@ -4015,6 +4116,8 @@ snapshots: '@scure/bip32': 1.4.0 '@scure/bip39': 1.3.0 + eventemitter3@4.0.7: {} + eventemitter3@5.0.1: {} expect-type@1.2.1: {} @@ -4378,6 +4481,10 @@ snapshots: loupe@3.1.3: {} + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + lowercase-keys@2.0.0: {} lru-cache@10.4.3: {} @@ -4435,6 +4542,11 @@ snapshots: nanoid@3.3.11: {} + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + node-addon-api@5.1.0: {} node-fetch@2.7.0: @@ -4738,6 +4850,11 @@ snapshots: slash@3.0.0: {} + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + source-map-js@1.2.1: {} source-map@0.8.0-beta.0: @@ -4807,6 +4924,8 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 + superstruct@0.15.5: {} + superstruct@2.0.2: {} symbol.inspect@1.0.1: {} @@ -4858,6 +4977,8 @@ snapshots: dependencies: is-number: 7.0.0 + toml@3.0.0: {} + tough-cookie@4.1.4: dependencies: psl: 1.15.0