Skip to content

0xlocker/CL-20

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CL20 Conviction Locker Contract Example

Conviction Lockers (CL20's) are part of the XBT2027 eco-system for XBT, 2027 and D17. This code is open-source with an MIT License.

This repo contains a minimal Uniswap V2 locker that buys a token, records its own entry price from the pool, and unlocks only when a later on-chain price probe reaches a target multiple.

There is no hosted backend, oracle, menu system, or external API. The contract uses the same pool for the initial baseline check and later unlock checks, so the unlock condition is reproducible from on-chain swaps.

This repository ships with a Sepolia 2027 example based on the token by x.com/XBT2027. The same contract pattern can be adapted to other Uniswap V2 pools by changing the RPC, router, token, quote token, and pair addresses. For now, use testnet only unless you have reviewed the contract and scripts yourself.

This contract is not production ready. Expect changes before 1.0 releases.

What Is Included

  • src/MinimalConvictionLocker.sol: owner-only locker contract source.
  • scripts/deploy-and-lock.mjs: deploys a fresh locker, buys with ETH, records the baseline probe, and creates the first lock.
  • scripts/inspect-lock.mjs: reads the lock state and target values.
  • scripts/verify-withdraw.mjs: verifies the price target and optionally withdraws in the same transaction.
  • .env.example: Sepolia defaults and the main settings to change.
  • package.json / package-lock.json: Hardhat, ethers, and dotenv setup.

The detailed Sepolia run report is a separate artifact:

SEPOLIA-CL-20-FLOW.md

This repo does not include or require the 2027 token contract. 2027 is only the token/pool used for the Sepolia example configuration. The locker can target any compatible Uniswap V2 pair by changing the env addresses.

How The Locker Works

MinimalConvictionLocker deploys a new owner-only vault and can create the first lock in the deployment transaction.

With ETH_IN=0.001, LOCK_BPS=10000, and PROBE_AMOUNT=1:

  1. The constructor deploys a vault owned by your wallet.
  2. It swaps ETH_IN through Uniswap V2 to buy the locked token.
  3. It allocates LOCK_BPS of the bought tokens to the lock. 10000 means 100%.
  4. It sells exactly PROBE_AMOUNT of the bought token as the baseline price probe.
  5. It records the baseline value in WETH/native quote terms, or USDT terms if configured.
  6. It locks the remaining bought tokens.

Later, the owner calls verify or verifyAndWithdraw. If the target has not already been reached, the locker pulls another owner-funded PROBE_AMOUNT, sells it into the same pool, and compares the output to the stored target.

For example:

TARGET_MULTIPLE=5.00

stores targetMultipleX100 = 500, meaning a 5x target. If you prefer percent language:

TARGET_INCREASE_PERCENT=400

also means 5x, because a 400% gain is original capital plus 400%.

Sepolia Example Defaults

Setting Address
Token bought and locked 0x368a8b834464D1B28bdeB4d2437D016de4F5EA67
Price quote pair 0xD7850331ad7090C7c9bD2883e9b42945C13Dfa4f
Quote token Sepolia WETH 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9
Router Sepolia Uniswap V2 router 0xC532a74256D3Db42D0Bf7a0400fEFDbad7694008

Original mainnet reference values from the 2027 example:

Setting Address
Token 0x3483FE3baC9Ca981f53E92f05603E1B32cd1b3cC
Pair 0x7C3ef649FbfDb54c9bB31Dbc1229DC772C000EC8
WETH 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Optional WETH/USDT V3 pool 0x11b815efB8f581194ae79006d24E0d814B7697F6

Mainnet is not the recommended starting point. The scripts default to Sepolia and require an explicit confirmation string before sending any transaction.

Setup

Use Node.js 22.13 or newer.

npm install
npm run compile
cp .env.example .env

Fill in .env with a fresh testnet wallet:

OWNER_PRIVATE_KEY=your_testnet_private_key
OWNER_ADDRESS=0xYourWalletAddress

The owner wallet is the only wallet that can verify, withdraw, or sweep tokens from its locker. The private key never needs to be pasted into a script file; the scripts load it from .env with dotenv.

At minimum, a Sepolia run needs:

OWNER_PRIVATE_KEY=
OWNER_ADDRESS=
RPC_URL=https://ethereum-sepolia-rpc.publicnode.com
CHAIN_ID=11155111
NETWORK_NAME=sepolia
ETH_IN=0.001
TARGET_MULTIPLE=5.00
PROBE_AMOUNT=1
MAX_TOTAL_COST_ETH=0.005

The token, pair, quote token, and router values are already set in .env.example for the Sepolia 2027 example. To point the kit at another pool, change:

LOCKED_TOKEN=
QUOTE_TOKEN=
UNISWAP_V2_PAIR=
UNISWAP_V2_ROUTER=

Recommended wallet hygiene:

  • Use a fresh wallet with no real funds.
  • You can get free Sepolia eth for Testnet from https://cloud.google.com/application/web3/faucet/ethereum/sepolia
  • Do not use a wallet connected to your trading activity.
  • Ideally use a separate macOS or Windows profile from the one you trade from.
  • Treat every mainnet setting as dangerous until you have tested the full flow on testnet.
  • Never commit .env. It is ignored by .gitignore.

Deploy And Lock

Dry run first:

npm run deploy-lock

Send the Sepolia transaction:

EXECUTE=I_UNDERSTAND_SEPOLIA npm run deploy-lock

The script prints LOCKER_ADDRESS=... and the lock ID. Add them to .env:

LOCKER_ADDRESS=0x...
LOCK_ID=0

Inspect

npm run inspect

This prints the locked amount, baseline quote, last quote, target quote, unlock basis, fallback unlock time, and whether the lock is already withdrawable.

Verify And Unlock

Dry run:

npm run verify-withdraw

Execute verification and withdraw in one transaction if the price target is met:

EXECUTE=I_UNDERSTAND_SEPOLIA VERIFY_ACTION=verifyAndWithdraw npm run verify-withdraw

Run verification without withdrawing:

EXECUTE=I_UNDERSTAND_SEPOLIA VERIFY_ACTION=verify npm run verify-withdraw

Withdraw after the fallback time has passed:

EXECUTE=I_UNDERSTAND_SEPOLIA VERIFY_ACTION=withdraw npm run verify-withdraw

Important: price verification sells another PROBE_AMOUNT from the owner wallet. The owner must hold and approve that probe amount. The script can create the approval when executing, but a dry run may stop early if approval is missing because gas cannot be estimated yet.

Adapting To Another Pool

The main env values are:

RPC_URL=
PRIVATE_RPC_URL=
NETWORK_NAME=
CHAIN_ID=
UNISWAP_V2_ROUTER=
LOCKED_TOKEN=
QUOTE_TOKEN=
UNISWAP_V2_PAIR=
WETH_USDT_V3_POOL=

For mainnet, you would change these values to mainnet RPC and mainnet contract addresses, and the confirmation string becomes:

EXECUTE=I_UNDERSTAND_MAINNET npm run deploy-lock

That is supported as an escape hatch, not as a recommendation. Testnet first.

Important Details

  • The initial deploy-lock flow is one transaction: contract creation, ETH buy, baseline probe, and lock creation.
  • Only the owner can verify, withdraw, or sweep unlocked leftovers.
  • The withdrawal destination is always the owner wallet.
  • The contract is not upgradeable.
  • Existing pre-owned ERC-20 tokens cannot be pulled during constructor deployment unless the future contract address was approved in advance or a permit path is added. This kit focuses on the ETH-buy-and-lock path.
  • A 5x trigger does not guarantee a 5x realized exit on the full position. The trigger is a marginal probe price; selling a larger amount can move the AMM price.

Risks

  • This is unaudited experimental code.
  • Deployment and verification cost gas.
  • Public mempool swaps can be sandwiched. Use a protected RPC where available.
  • Shallow liquidity, LP removal, token migrations, or malicious token behavior can make verification fail or make a target easier to hit.
  • The design intentionally uses live pool conditions. It does not protect against market manipulation.
  • A contract bug, compromised key, or wrong env value can create loss.

About

A basic CL-20 Conviction Locker contract for Sepolia Testnet with an example transaction flow

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors