A minimal, working template for writing, testing, and deploying Credible Layer assertions. This repo mirrors the public quickstart and keeps the steps as short as possible for first-time users.
assertions/src: Assertion contracts (Solidity)assertions/test: Assertion tests runnable withpcl testsrc: Example protocols with intentional vulnerabilitiesscript: Deployment scripts for the example protocolslib: Submodules forcredible-std,forge-std, and OpenZeppelin
pclinstalled (quick path): https://docs.phylax.systems/credible/credible-install- Foundry (
forge,cast): https://getfoundry.sh/ - Git
- An RPC endpoint and funded wallet for a Credible Layer-enabled network
Quick install (macOS):
brew tap phylaxsystems/pcl
brew install phylaxFoundry install:
curl -L https://foundry.paradigm.xyz | bash
foundryupThis flow is the shortest path to a deployed assertion.
git clone --recurse-submodules https://github.com/phylaxsystems/credible-layer-starter.git
cd credible-layer-starterIf you already cloned:
git submodule update --init --recursivepcl testexport RPC_URL=... # chain RPC URL
forge script script/DeployOwnable.s.sol \
--rpc-url "$RPC_URL" \
--account <account_name> \
--broadcastpcl auth login- Open the login link in your browser.
- Create a project in the dApp and link the Ownable contract address.
- Ownership is verified via the network's admin verifier (typically
owner()or allowlist-based).
pcl store OwnableAssertion
pcl submit -a 'OwnableAssertion' -p <project_name>This stores the assertion in Assertion DA and submits it to the dApp for deployment.
Project names are case-sensitive and must match the dApp exactly.
Note: the assertions in this repo use ph.getAssertionAdopter() so you link the contract in the dApp instead of passing it in a constructor.
- In the project view, deploy the assertion to Staging or Production.
- After the timelock, it becomes staged/enforced and starts protecting transactions.
export OWNABLE_ADDRESS=0x... # deployed Ownable address
# Check current owner
cast call "$OWNABLE_ADDRESS" "owner()" --rpc-url "$RPC_URL"
# Attempt to transfer ownership (should be dropped if assertion is enforced)
cast send "$OWNABLE_ADDRESS" \
"transferOwnership(address)" 0x1234567890123456789012345678901234567890 \
--account <account_name> \
--rpc-url "$RPC_URL" \
--timeout 20
# Owner should be unchanged
cast call "$OWNABLE_ADDRESS" "owner()" --rpc-url "$RPC_URL"If the transaction times out, the assertion likely dropped it. Some clients require a higher gas price to replace dropped txs.
The repo includes two larger examples with dedicated assertions:
- Contract:
src/PhyLock.sol - Assertions:
PhyLockAssertion,OwnershipAssertion - Deploy:
script/DeployPhyLock.s.sol
- Contract:
src/SimpleLending.sol - Assertions:
SimpleLendingAssertion,PriceFeedAssertion - Deploy:
script/DeploySimpleLending.s.sol
The end-to-end flow is the same as the Ownable example:
- Deploy the contract(s)
- Create a project and link contract addresses
pcl store->pcl submit- Deploy in the dApp (staging/production)
- Run the provided
casttransactions in the README below
Use the commands below after you have deployed the contracts and deployed assertions in the dApp.
Deploy these assertions in the dApp before running the transactions:
PhyLockAssertionfor the PhyLock contractOwnershipAssertionfor the same contract
export PHYLOCK_ADDRESS=0x...
export RPC_URL=...
# Deposit 0.7 ETH
cast send "$PHYLOCK_ADDRESS" "deposit()" --value 0.7ether --account <account_name> --rpc-url "$RPC_URL"
# Withdraw 0.2 ETH
cast send "$PHYLOCK_ADDRESS" "withdraw(uint256)" 0.2ether --account <account_name> --rpc-url "$RPC_URL"
# Withdraw 69 ETH (should be dropped)
cast send "$PHYLOCK_ADDRESS" "withdraw(uint256)" 69ether --account <account_name> --rpc-url "$RPC_URL" --timeout 20
# Transfer ownership (should be dropped)
cast send "$PHYLOCK_ADDRESS" \
"transferOwnership(address)" 0x1234567890123456789012345678901234567890 \
--account <account_name> \
--rpc-url "$RPC_URL" \
--timeout 20 \
--gas-price 100000000000Make sure your project includes both the lending contract and the price feed contract, then deploy:
SimpleLendingAssertionforSimpleLendingPriceFeedAssertionfor the price feed
export LENDING_PROTOCOL=0x...
export TOKEN_ADDRESS=0x...
export PRICE_FEED=0x...
export RPC_URL=...
# Mint tokens to the lending protocol
cast send "$TOKEN_ADDRESS" "mint(address,uint256)" "$LENDING_PROTOCOL" 100000ether --account <account_name> --rpc-url "$RPC_URL"
# Deposit 0.5 ETH
cast send "$LENDING_PROTOCOL" "deposit()" --value 0.5ether --account <account_name> --rpc-url "$RPC_URL"
# Borrow 750 tokens
cast send "$LENDING_PROTOCOL" "borrow(uint256)" 750ether --account <account_name> --rpc-url "$RPC_URL"
# Withdraw collateral (should be dropped)
cast send "$LENDING_PROTOCOL" "withdraw(uint256)" 0.25ether --account <account_name> --rpc-url "$RPC_URL" --timeout 20
# Decrease price by 15% (should be dropped)
cast send "$PRICE_FEED" "setPrice(uint256)" 0.75ether --account <account_name> --rpc-url "$RPC_URL" --timeout 20 --gas-price 100000000000- Quickstart: https://docs.phylax.systems/credible/pcl-quickstart
- Deploy with dApp: https://docs.phylax.systems/credible/deploy-assertions-dapp
- Assertions Book: https://docs.phylax.systems/assertions-book/assertions-book-intro
- Examples: https://github.com/phylaxsystems/assertions-examples