{"id":15495903,"url":"https://github.com/hairyf/harsta","last_synced_at":"2026-03-10T14:01:48.505Z","repository":{"id":276921444,"uuid":"921609106","full_name":"hairyf/harsta","owner":"hairyf","description":null,"archived":false,"fork":false,"pushed_at":"2025-04-11T14:11:49.000Z","size":3548,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-22T21:02:52.239Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hairyf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-24T09:18:31.000Z","updated_at":"2025-04-11T14:11:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"3cfdfdac-fffc-404e-8211-5f697bc312ab","html_url":"https://github.com/hairyf/harsta","commit_stats":null,"previous_names":["hairyf/harsta"],"tags_count":73,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hairyf%2Fharsta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hairyf%2Fharsta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hairyf%2Fharsta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hairyf%2Fharsta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hairyf","download_url":"https://codeload.github.com/hairyf/harsta/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250324687,"owners_count":21411943,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["builder","contract","exporter","hardhat","hardhat-deploy","viem","wagmi"],"created_at":"2024-10-02T08:20:15.744Z","updated_at":"2026-03-10T14:01:48.460Z","avatar_url":"https://github.com/hairyf.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## harsta\n\nI have been searching for a way to integrate Hardhat with other frameworks (such as nextjs/vue), but unfortunately, I have not found one. Therefore, I plan to implement it myself.\n\n**harsta** is a contract development tool based on Hardhat, designed to streamline the development, testing, and referencing of contracts, addresses, ABIs, and contract instances.\n\n**harsta** simplifies the configuration files for end-to-end development and contract writing. You only need to write one configuration that can be referenced anywhere.\n\n![image](./public/flow.png)\n\n- [Address visualization and third-party ABI](#directory)\n- [Common Harsta Config for Browser and Node.js](#config)\n- [Automatic deployment based on config](#deployments)\n- [Seamless Wagmi support](#wagmi)\n- [Rapid deploy tests](#tests)\n\n## Installation\n\nYou can quickly create a project like Hardhat using the command `pnpm harsta create`, or you can install it separately in any project:\n\n```sh\n$ pnpm harsta create\n\n# OR\n\n$ pnpm install harsta @harsta/client\n\n# OR\n\n$ pnpm install harsta\n```\n\n\u003e Note: `@harsta/client` is optional. If installed, `harsta compile` will automatically be bundled into `@harsta/client`.\n\n## Directory\n\n- `contracts/`, `test/`\n\nThe location of contract source files and tests, similar to Hardhat.\n\n- `config/addresses.ts | config/addresses.json`\n\nThis file contains all contract addresses used in the project. You can customize this file, and it will be automatically updated after contract updates/deployments.\n\n```ts\nexport default {\n  [chainIdNumber]: { [contractName]: '0x...' },\n  // or the contract address currently used in your project\n  0: { USDT: '0x...' }\n}\n```\n\n- `config/fragments/*.json`\n\nAdditional third-party contracts used, which will be compiled into contract instances.\n\n## Quick Start\n\n**harsta** has three key commands:\n\n```sh\n  harsta compile  Compile and output the directory\n  harsta test     Run integration tests\n  harsta deploy   Deploy and save deployments\n  harsta update   Update deployed upgradable contracts\n  harsta node     Starts a JSON-RPC server on top of Local Network\n```\n\nTo compile your contracts in `contracts/`:\n\n```\n$ pnpm harsta compile\n```\n\nIf `@harsta/client` is not installed, **harsta** will default output to `dist`. You can change the output directory with the `--output` option.\n\nAfter compilation, if you install `@harsta/client`, you can directly reference `@harsta/client` as follows:\n\n```ts\nimport {\n  addresses,\n  chains,\n  contracts,\n  defaultChain,\n  interfaces,\n} from '@harsta/client'\n\n// Automatically import contract addresses from the default chain\nconst lock = contracts.Lock.resolve()\nconst time = await lock.unlockTime()\n\n// Use other addresses through attach\nconst lock2 = contracts.Lock.attach(addresses[chains.ethereum.id].Lock)\n\n// Connect to a contract by passing a chain\nconst lock3 = contracts.Lock.resolve(chains.ethereum, '0x...')\n```\n\nSend and write contract:\n\n```ts\nimport { contracts, updateRunner } from '@harsta/client'\n// Update the default runner (signer) to support transactions\nimport { Wallet } from 'ethers'\n\nconst wallet = new Wallet('...')\nupdateRunner(wallet)\n\nconst lock = contracts.Lock.resolve()\nconst transaction = await lock.withdraw()\n```\n\n## Config\n\n**harsta**'s config is similar to Hardhat but more streamlined and simple, with comprehensive chain config.\n\n```ts\nimport { defineConfig } from 'harsta'\nimport 'dotenv/config'\n\nconst config = defineConfig({\n  solidity: '0.8.20',\n  defaultNetwork: 'geneva',\n  namedAccounts: {/* ... */},\n  networks: {\n    ethereum: {\n      name: 'Ethereum',\n      rpc: '...',\n      id: 0,\n      icon: '',\n      currency: {\n        decimals: 18,\n        name: 'ETH',\n        symbol: 'ETH'\n      },\n      explorer: {/* ... */},\n      // Optional, if not written, it will not be accepted by Hardhat\n      deploy: {\n        accounts: [/* account #0 | account #1 */],\n        saveDeployments: true,\n        allowUnlimitedContractSize: true,\n        gas: 'auto',\n        gasPrice: 'auto',\n      },\n      verify: {\n        uri: '...',\n        key: '...'\n      }\n    }\n  },\n  // ...\n})\n```\n\n## Deployments\n\nAfter writing your contracts, you need to define the `deployments` in the configuration file:\n\n```ts\nconst config = defileConfig({\n  // ...\n  deployments: {\n    Contract1: { args: [/* args... */] },\n    Contract2: { kind: 'transparent', args: async env =\u003e [/* args... */] },\n    Contract3: { kind: 'uups', args: () =\u003e [/* ...args */] },\n    Contract4: { target: 'Contract1', args: [/* args... */] },\n    Contract5: { target: 'Contract1', chains: [/* chainIds */] },\n  }\n})\n```\n\nAdditionally, we can tell `harsta` to verify our contract on Etherscan, Sourcify or Blockscout, if the network is supported, by passing `--verify`.\n\nNext, deploy to the desired chain:\n\n```sh\n$ pnpm harsta deploy --network [your network] --verify\n```\n\nIf successful, output the following info:\n\n```sh\nTARGET     \u003e     \u003cname\u003e:\u003ctarget\u003e.sol\nNETWORK    \u003e     \u003cid\u003e \u003cnetwork\u003e\nHash       \u003e     \u003chash\u003e\nFrom       \u003e     \u003caddress\u003e\nArgs       \u003e     \u003carg[]\u003e\n---------------------------------------------------------\nAddress    \u003e     \u003caddress\u003e\n```\n\nIf it is an upgradable contract, then it is:\n\n```sh\nTARGET     \u003e     \u003cname\u003e:\u003ctarget\u003e.sol\nNETWORK    \u003e     \u003cid\u003e \u003cnetwork\u003e\nkIND       \u003e     \u003ctransparent|uups|beacon\u003e\nHash       \u003e     \u003chash\u003e(implement)\nFrom       \u003e     \u003caddress\u003e\n---------------------------------------------------------\nHash       \u003e     \u003chash\u003e(\u003ckind\u003e)\nFrom       \u003e     \u003caddress\u003e\nArgs       \u003e     \u003carg[]\u003e\n---------------------------------------------------------\nImplement  \u003e     \u003caddress\u003e\nProxy      \u003e     \u003caddress\u003e\n```\n\nAfter deployment, the `config/addresses.ts` and `config/deployments` file will be automatically updated.\n\n## Verify\n\nIt is recommended to use the `--verify` flag with `harsta deploy` to automatically verify the contract on explorer after a deployment.\n\nIf you are verifying an already deployed contract, read on.\n\nBefore this, you need to set the `verify` field for the corresponding network:\n\n```ts\nimport { defineConfig } from 'harsta'\n\nconst config = defineConfig({\n  // ...\n  networks: {\n    ethereum: {\n      //  ...\n      verify: { uri: '...', key: '...' }\n    }\n  },\n})\n```\n\nAnd you can verify a contract on Etherscan, Sourcify, oklink or Blockscout with the `harsta verify [target]` command.\n\n## Update\n\nIf your contract files are updated:\n\n- `contracts/Constant1.sol` has been modified\n- `contracts/Constant2.sol` has been modified\n\nrun `deploy --contracts Constant1, Constant2` script will ask and redeploy `Constant1 | Constant2` contracts.\n\nIf your contract is an upgradable contract, create a new file:\n\n- `contracts/Constant1.sol` original contract\n- `contracts/Constant1V1.sol` upgraded contract\n\nrun `update Constant1 --target Constant1V1` will upgrade the contract.\n\n```sh\nTARGET     \u003e     \u003cname\u003e:\u003ctarget\u003e.sol\nNETWORK    \u003e     \u003cid\u003e \u003cnetwork\u003e\nkIND       \u003e     \u003ctransparent|uups|beacon\u003e\nHash       \u003e     \u003chash\u003e(implement)\nFrom       \u003e     \u003caddress\u003e\n---------------------------------------------------------\nHash       \u003e     \u003chash\u003e(upgradeTo)\nFrom       \u003e     \u003caddress\u003e\n---------------------------------------------------------\nImplement  \u003e     \u003coldAddress\u003e\n                 \u003cnewAddress\u003e ←\nProxy      \u003e     \u003caddress\u003e\n```\n\n## Wagmi\n\n**harsta** naturally supports wagmi. By adding a few lines of code, you can use the compiled information and contracts of all chains:\n\nconfig.ts:\n\n```ts\nimport { createConfig, http } from '@wagmi/core'\nimport { createClient } from 'viem'\nimport { chains } from '@harsta/client'\n\nexport const config = createConfig({\n  chains: [chains.ethereum, chains.sepolia],\n  client({ chain }) {\n    return createClient({ chain, transport: http() })\n  },\n})\n```\n\nApp.tsx:\n\n```tsx\nimport { WagmiProvider } from 'wagmi'\nimport { SubscribeWagmiConfig } from '@harsta/client/wagmi'\nimport { config } from './config'\n\nfunction App() {\n  return (\n    \u003cWagmiProvider config={config}\u003e\n      \u003cSubscribeWagmiConfig /\u003e\n      {/* your page content */}\n    \u003c/WagmiProvider\u003e\n  )\n}\n```\n\nAny page:\n\n```tsx\nimport { addresses, contracts } from '@harsta/client'\nimport { useAccount } from 'wagmi'\n\nfunction Page() {\n  const { isConnected } = useAccount()\n  async function withdraw() {\n    const lock = contracts.Lock.resolve('signer')\n    const transaction = await lock.withdraw()\n  }\n  return (\n    \u003c\u003e\n      {isConnected \u0026\u0026 (\n        \u003cbutton onClick={withdraw}\u003e\n          Withdraw\n        \u003c/button\u003e\n      )}\n    \u003c/\u003e\n  )\n}\nexport default Page\n```\n\nBy modifying `.env` to change the default chain and configuration used by the contract:\n\n```\nNEXT_PUBLIC_DEFAULT_CHAIN = 'sepolia'\n# OR\nDEFAULT_CHAIN = 'sepolia'\n```\n\n## Tests\n\nYou can ensure that deployment scripts are executed in tests by calling `await deployments.fixture(['MyContract'])`. This is optimized so that if multiple tests use the same contract, the deployment will be done once, and each test will start in exactly the same state.\n\n```ts\nimport { contracts } from 'harsta/runtime'\nimport { fixture, initial } from 'harsta/tests'\nimport { expect } from 'chai'\n\ndescribe('storage contract', () =\u003e {\n  beforeEach(async () =\u003e {\n    await initial()\n    await fixture(['Storage'])\n  })\n  it('test', async () =\u003e {\n    const storage = contracts.Storage.resolve()\n    // ...\n  })\n})\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhairyf%2Fharsta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhairyf%2Fharsta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhairyf%2Fharsta/lists"}