{"id":51040356,"url":"https://github.com/nikicat/cr2dep","last_synced_at":"2026-06-22T10:30:59.609Z","repository":{"id":359210325,"uuid":"1245036225","full_name":"nikicat/cr2dep","owner":"nikicat","description":"PoC: TRON CREATE2 calldata-bound proxy deployer","archived":false,"fork":false,"pushed_at":"2026-05-20T21:20:45.000Z","size":60,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T02:58:19.411Z","etag":null,"topics":["create2","deployer","smart-contracts","tron"],"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/nikicat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-20T21:05:27.000Z","updated_at":"2026-05-20T21:18:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nikicat/cr2dep","commit_stats":null,"previous_names":["nikicat/cr2dep"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/nikicat/cr2dep","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikicat%2Fcr2dep","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikicat%2Fcr2dep/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikicat%2Fcr2dep/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikicat%2Fcr2dep/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nikicat","download_url":"https://codeload.github.com/nikicat/cr2dep/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikicat%2Fcr2dep/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34645680,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["create2","deployer","smart-contracts","tron"],"created_at":"2026-06-22T10:30:58.813Z","updated_at":"2026-06-22T10:30:59.599Z","avatar_url":"https://github.com/nikicat.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cr2dep\n\nPoC for one-shot, deterministic on-chain side-effects on TRON: every (target, calldata) pair gets its own CREATE2 address, and the contract at that address can only ever forward to *that* pair. Useful for use-cases like committing in advance to a future transfer/swap/contract interaction whose execution is then permissionless (anyone can pull the trigger, but only the bound action will fire).\n\n## How it works\n\n```\nProxyFactory.deploy(impl, salt, commitment)\n        │\n        ▼  CREATE2 with init code = [10-byte constructor] ‖ [45-byte EIP-1167 stub→impl] ‖ [32-byte commitment]\n        │\n        ▼\n  proxy at address  A = keccak256(0x41 ‖ factory ‖ salt ‖ keccak256(initCode))[12:]\n  runtime           = 45-byte EIP-1167 stub (commitment lives only in init code → binds A but isn't deployed)\n\n  anyone calls  →   A.execute(salt, target, data)\n                          │\n                          ▼  delegatecall\n                    BoundCaller.execute:\n                      commitment = keccak256(abi.encode(target, data))\n                      recompute A' = keccak256(0x41 ‖ FACTORY ‖ salt ‖ keccak256(initCode(SELF, commitment)))[12:]\n                      require A' == address(this)\n                      target.call{value: msg.value}(data)\n```\n\nTwo contracts, deployed once per chain:\n\n| Contract       | Role                                                                                                                                       |\n| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |\n| `ProxyFactory` | Singleton. `deploy(impl, salt, commitment)` CREATE2s a 45-byte EIP-1167 proxy whose init code embeds `commitment`.                         |\n| `BoundCaller`  | Impl behind every proxy. Re-derives the proxy's own CREATE2 address from `(FACTORY, salt, initCode(SELF, keccak256(abi.encode(target, data))))` and refuses unless it matches `address(this)`. |\n\nThe address binding — not access control — is what makes each proxy single-purpose. `execute` is fully public; only the originally-bound (target, data) will pass the check.\n\nBoth contracts take a `PREFIX` immutable in their constructors. TRON uses `0x41` (the CREATE2 prefix replacement for EVM's `0xff`); the CLI sets this automatically. The EDR tests pass `0xff` so the same contracts can be tested end-to-end on a stock EVM.\n\nPairs are stored in a small SQLite DB; addresses are recomputed on demand and never persisted.\n\n## Prerequisites\n\n- Node ≥ 20\n- `pnpm` (the repo declares `packageManager: pnpm@9.15.0` — Corepack will fetch it)\n- A funded TRON account with TRX for fees. Either:\n  - **Browser wallet** (default): [TronLink](https://www.tronlink.org) installed in a browser. Nile testnet faucet: \u003chttps://nileex.io/join/getJoinPage\u003e.\n  - **Local key** (opt-in, CI/unattended): a TRON private key in `PRIVATE_KEY`.\n\n## Setup\n\n```bash\npnpm install\npnpm compile\ncp .env.example .env  # set TRON_RPC; leave PRIVATE_KEY blank to use TronLink\npnpm cli init         # creates cr2dep.db\n```\n\n### Signing\n\nThe CLI uses [`browser-tron-signer`](https://www.npmjs.com/package/browser-tron-signer) by default — every signing command spins up a small local HTTP server, opens a TronLink approval page in your browser, and waits for you to approve. No key ever touches the server.\n\nIf `PRIVATE_KEY` is set in the environment, it's used directly instead (handy for CI). When unset, browser signing is used.\n\nEnv vars:\n\n| Var             | Default                      | Notes                                  |\n| --------------- | ---------------------------- | -------------------------------------- |\n| `PRIVATE_KEY`   | unset                        | Opt-in fallback. If set, signs locally with this key instead of TronLink. Hex (with or without `0x`). |\n| `TRON_RPC`      | `https://nile.trongrid.io`   | Full-node HTTP endpoint                |\n| `TRON_API_KEY`  | unset                        | Trongrid API key (raises rate limits)  |\n| `TRON_NETWORK`  | inferred from `TRON_RPC`     | Browser-signer network: `mainnet` / `shasta` / `nile` |\n| `TRON_MCP_PORT` | `3848`                       | Local HTTP port for the browser approval UI |\n| `BROWSER`       | OS default                   | Force a specific browser for the approval page (e.g. `firefox`, `brave-browser`). Useful when TronLink isn't installed in your default browser. |\n\n## Deploy the on-chain singletons\n\nYou only do this once per network. Addresses are persisted to `.cr2dep.json`.\n\n```bash\npnpm cli deploy-factory    # ProxyFactory (no constructor args beyond PREFIX=0x41)\npnpm cli deploy-impl       # BoundCaller(factoryAddress, PREFIX=0x41)\n```\n\nIf you've already deployed these (e.g. shared between team members), skip the deploys and just point the CLI at them:\n\n```bash\npnpm cli set-factory  T...\npnpm cli set-impl     T...\n```\n\n## Day-to-day usage\n\n```bash\n# Store a binding. Prints the predicted CREATE2 address right away.\npnpm cli add --target T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb \\\n             --data 0xa9059cbb...                       \\\n             --salt 0x00...                  # optional, defaults to bytes32(0)\n\npnpm cli list              # all stored pairs with predicted addresses\npnpm cli address 1         # just the address for pair #1\n\npnpm cli deploy  1         # CREATE2-deploys the proxy bound to pair #1\npnpm cli execute 1         # call execute(salt, target, data) on the proxy — anyone can do this\n```\n\n`add` only writes to SQLite — no network call, no funds needed. `deploy-factory`, `deploy-impl`, `deploy`, and `execute` are the only commands that broadcast transactions; each will pop a TronLink approval page in your browser unless `PRIVATE_KEY` is set.\n\nThe same proxy address can be re-`execute`d as many times as you like; the address binding is what's enforced, not one-shot semantics. (If you want one-shot, gate it with a storage slot in `BoundCaller`.)\n\n### Sample bindings (TRON mainnet)\n\nThe proxy only ever does its single bound action, so for sanity-checking the pipeline you want something cheap and harmless. Two options against USDT (`TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t`):\n\n```bash\n# Read-only: balanceOf(0x0...0) — view call, return value discarded, proxy holds nothing.\npnpm cli add \\\n  --target TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t \\\n  --data 0x70a082310000000000000000000000000000000000000000000000000000000000000000\n\n# Tiny write: approve(TPL66VK2gCXNCD7EJg9pgJRfqcRazjhUZY, 0) — observable on Tronscan,\n# revokes any allowance from the proxy to that spender (zero before, zero after — no-op revoke).\npnpm cli add \\\n  --target TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t \\\n  --data 0x095ea7b3000000000000000000000000922953dcf6886cf3afe9faf80f93922e57b1b0fd0000000000000000000000000000000000000000000000000000000000000000\n```\n\nMainnet deploys burn real TRX (rough order: 200–500 TRX per singleton, ~30 per proxy). To verify the wiring without spending mainnet TRX, point `TRON_RPC` at Nile (`https://nile.trongrid.io`) and use the [Nile faucet](https://nileex.io/join/getJoinPage).\n\n## Tests\n\nEDR (Hardhat 3's in-process EVM) is used as a stand-in for the TVM. It uses Ethereum's `0xff` CREATE2 prefix instead of TRON's `0x41`, so the tests deploy both contracts with `PREFIX=0xff` and exercise the full deploy + execute path end-to-end:\n\n```bash\npnpm test         # hardhat test nodejs\npnpm typecheck    # tsc --noEmit\n```\n\nCovers init-code shape, off-chain ↔ on-chain address parity, deploy at predicted address, calldata + value forwarding, binding-mismatch reverts (wrong data / target / salt), and bubbled target reverts.\n\n## TRON-specific notes\n\n- **CREATE2 prefix:** TRON's `CREATE2` opcode uses `0x41` where Ethereum uses `0xff`. The contracts take this as a deploy-time immutable; the CLI's `src/create2.ts` defaults to `0x41`. If you deploy with the wrong prefix the system silently stops working — every `execute` will fail `BindingMismatch`.\n- **PUSH0:** disabled (`evmVersion: \"paris\"`) so the compiled bytecode runs on any TVM hardfork still in the wild.\n- **Address format:** the CLI accepts and prints `T…` base58 addresses (canonical TRON form) and `0x…` 20-byte hex interchangeably. The 20-byte form is what gets stored in SQLite and hashed.\n\n## Layout\n\n```\ncontracts/\n  ProxyFactory.sol         singleton; CREATE2-deploys the proxies\n  BoundCaller.sol          impl behind every proxy; verifies address↔(target,data) binding\n  test/TestTarget.sol      tiny counter target used by tests only\nsrc/\n  cli.ts                   commander CLI\n  signer.ts                browser-tron-signer + PRIVATE_KEY fallback, as an AsyncDisposable\n  create2.ts               commitmentOf / buildProxyInitCode / predictProxyAddress\n  artifacts.ts             loads Hardhat 3 artifacts\n  tron.ts                  T-base58 ↔ 0x EVM hex (via TronWeb static utils)\n  db.ts                    better-sqlite3 — pairs(id, target, calldata, salt, deployed, tx_hash)\n  config.ts                .cr2dep.json: { factoryAddress, implementationAddress }\ntest/cr2dep.test.ts        EDR end-to-end tests (14 cases)\nhardhat.config.ts          solc 0.8.28, evmVersion paris, hardhat-toolbox-viem plugin\n```\n\n## Status\n\nPoC. Not audited. Don't ship to mainnet without a review — in particular, anything that relies on the binding-check assembly or the CREATE2 prefix is the kind of thing that breaks silently if something upstream changes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnikicat%2Fcr2dep","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnikicat%2Fcr2dep","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnikicat%2Fcr2dep/lists"}