Skip to content

Update Native Verifier use in Loan Flow and Minter #7

Update Native Verifier use in Loan Flow and Minter

Update Native Verifier use in Loan Flow and Minter #7

Workflow file for this run

---
name: examples
"on":
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
permissions: read-all
jobs:
build:
uses: ./.github/workflows/build-creditcoin3.yml
with:
build-options: "--release --features=fast-runtime"
version-string: testing-ci
secrets: inherit
hello-bridge:
runs-on: ubuntu-24.04
needs: build
steps:
- uses: actions/checkout@v6
- name: Download binaries
uses: actions/download-artifact@v7
with:
name: binary-for-testing-ci
path: target/release
- name: Restore executable permissions
working-directory: target/release
run: |
chmod a+x ./attestor*
chmod a+x ./creditcoin3-node
chmod a+x ./proof-gen-api-server
- name: Start creditcoin3-node - Alice
run: |
mkdir -p /var/tmp/hello-bridge-logs/
./target/release/creditcoin3-node \
--chain dev \
--validator --alice --pruning archive \
--node-key d182d503b7dd97e7c055f33438c7717145840fd66b2a055284ee8d768241a463 \
--unsafe-rpc-external --rpc-cors all \
--base-path ./alice-data >/var/tmp/hello-bridge-logs/creditcoin3-node.log 2>&1 &
- name: Start Ethereum simulation with Anvil
run: |
export FOUNDRY_DIR="$HOME/.foundry"
# first install it
curl -L https://foundry.paradigm.xyz | bash
~/.foundry/bin/foundryup
# supported chain keys are hard-coded in attestor/src/cc3.rs and node/src/chain_spec.rs
# Anvil1 == chain_key(2)
~/.foundry/bin/anvil --block-time 6 --chain-id 31337 --port 8141 &
- name: Check that creditcoin3-node and anvil are running
run: |
.github/wait-for-creditcoin.sh 'http://127.0.0.1:9944'
.github/wait-for-ethereum.sh 'http://127.0.0.1:8141'
- name: Start Attestation Zombienet for Alice - Anvil 1
run: |
mkdir -p /var/tmp/hello-bridge-logs/attestors-alice
./target/release/attestor_zombienet \
-n 3 \
--bin=./target/release/attestor \
--eth-url=ws://localhost:8141 \
--cc3-url=ws://localhost:9944 \
--funding-address='//Alice' \
--config=.github/attestor-config.yaml \
--chain-key=2 &
- name: Wait for 3 attestors to be registered
run: |
# note: chain_key is hard-coded in this script so waiting only on Anvil_1
.github/wait-for-attestors.sh 'http://127.0.0.1:9944'
- name: Start proof-gen-api-server DB
run: |
docker run --rm --name proof-gen-postgres \
-p 5433:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=proofs_db \
-d postgres
sleep 60
- name: Start proof-gen-api-server
run: |
POSTGRES_HOST=localhost POSTGRES_PORT=5433 POSTGRES_USER=postgres POSTGRES_PASSWORD=password POSTGRES_DB=proofs_db \
./target/release/proof-gen-api-server \
--cc3-key "//Alice" \
--cc3-rpc-url ws://localhost:9944 \
--eth-rpc-url http://localhost:8141 \
>/var/tmp/hello-bridge-logs/proof-gen-api-server.log &
sleep 30
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- run: npm install -g yarn
- name: Setup
id: setup
run: |
yarn install
SOURCE_CHAIN_RPC_URL="ws://127.0.0.1:8141"
echo "INFO: generate a new wallet"
OUTPUT=$(~/.foundry/bin/cast wallet new)
echo "$OUTPUT"
WALLET_ADDRESS=$(echo "$OUTPUT" | grep Address: | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_address=$WALLET_ADDRESS" >> "$GITHUB_OUTPUT"
WALLET_PK=$(echo "$OUTPUT" | grep "Private key:" | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_pk=$WALLET_PK" >> "$GITHUB_OUTPUT"
echo "INFO: get 1000 ETH on source chain"
# note: using Anvil, Account #0
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 1000000000000000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--rpc-url "$SOURCE_CHAIN_RPC_URL"
CREDITCOIN_RPC_URL="ws://127.0.0.1:9944"
echo "INFO: get 2000 CTC on Creditcoin chain"
# note: this is Alith's PK
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 2000000000000000000000 \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
--rpc-url "$CREDITCOIN_RPC_URL"
echo "INFO: deploy TestERC20.sol to source chain"
# note: using Anvil, Account #0
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$SOURCE_CHAIN_RPC_URL" \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
contracts/sol/TestERC20.sol:TestERC20)
echo "$OUTPUT"
SOURCE_CHAIN_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "source_chain_contract_address=$SOURCE_CHAIN_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: deploy EvmV1Decoder.sol to Creditcoin chain"
# note: this is Alith's PK
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$CREDITCOIN_RPC_URL" \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
contracts/sol/EvmV1Decoder.sol:EvmV1Decoder)
echo "$OUTPUT"
DECODER_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "INFO: deploy SimpleMinterUSC.sol to Creditcoin chain"
# note: this is Alith's PK
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$CREDITCOIN_RPC_URL" \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
--libraries "contracts/sol/EvmV1Decoder.sol:EvmV1Decoder:$DECODER_CONTRACT_ADDRESS" \
contracts/sol/SimpleMinterUSC.sol:SimpleMinterUSC)
echo "$OUTPUT"
SIMPLE_MINTER_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "simple_minter_contract_address=$SIMPLE_MINTER_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: mint tokens on source chain"
~/.foundry/bin/cast send --rpc-url "$SOURCE_CHAIN_RPC_URL" \
"$SOURCE_CHAIN_CONTRACT_ADDRESS" \
"mint(uint256)" 50000000000000000000 \
--private-key "$WALLET_PK"
echo "INFO: burn the tokens (on source chain) we want to bridge"
OUTPUT=$(~/.foundry/bin/cast send --rpc-url "$SOURCE_CHAIN_RPC_URL" \
"$SOURCE_CHAIN_CONTRACT_ADDRESS" \
"burn(uint256)" 50000000000000000000 \
--private-key "$WALLET_PK")
echo "$OUTPUT"
BURN_TX_HASH=$(echo "$OUTPUT" | grep "transactionHash " | tr -s " " | cut -f2 -d" ")
echo "burn_tx_hash=$BURN_TX_HASH" >> "$GITHUB_OUTPUT"
- name: Prepare local .env
run: |
echo "SOURCE_CHAIN_KEY=2" > .env
echo "PROVER_API_URL='http://localhost:3100'" >> .env
echo "SOURCE_CHAIN_CONTRACT_ADDRESS='${{ steps.setup.outputs.source_chain_contract_address }}'" >> .env
echo "CREDITCOIN_RPC_URL='http://127.0.0.1:9944'" >> .env
echo "SOURCE_CHAIN_RPC_URL='http://127.0.0.1:8141'" >> .env
echo "USC_MINTER_CONTRACT_ADDRESS='${{ steps.setup.outputs.simple_minter_contract_address }}'" >> .env
echo "CREDITCOIN_WALLET_PRIVATE_KEY='${{ steps.setup.outputs.creditcoin_wallet_pk }}'" >> .env
echo "===== DEBUG ====="
cat .env
echo "===== END DEBUG ====="
- name: Exercise example
run: |
source .env
echo "INFO: Should not have any bridged tokens initially"
OUTPUT=$(yarn utils:check_balance $USC_MINTER_CONTRACT_ADDRESS "${{ steps.setup.outputs.creditcoin_wallet_address }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 0"
echo "$OUTPUT" | grep "Formatted Balance: 0.0 TEST"
echo "INFO: Submit a mint query to the USC contract"
OUTPUT=$(yarn hello_bridge:submit_query "${{ steps.setup.outputs.burn_tx_hash }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Minting completed"
echo "INFO: Verify bridged tokens"
OUTPUT=$(yarn utils:check_balance $USC_MINTER_CONTRACT_ADDRESS "${{ steps.setup.outputs.creditcoin_wallet_address }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 1000"
echo "$OUTPUT" | grep "Formatted Balance: 0.000000000000001 TEST"
- name: Collect logs
if: always()
run: |
docker logs proof-gen-postgres > /var/tmp/hello-bridge-logs/proof-gen-postgres.log
- name: Upload logs
if: always()
uses: actions/upload-artifact@v6
with:
name: hello-bridge-logs
path: /var/tmp/hello-bridge-logs/
include-hidden-files: true
- name: Kill processes
if: always()
continue-on-error: true
run: |
killall -9 attestor_zombienet
killall -9 attestor
killall -9 creditcoin3-node
custom-contracts-bridging:
runs-on: ubuntu-24.04
needs: build
steps:
- uses: actions/checkout@v6
- name: Download binaries
uses: actions/download-artifact@v7
with:
name: binary-for-testing-ci
path: target/release
- name: Restore executable permissions
working-directory: target/release
run: |
chmod a+x ./attestor*
chmod a+x ./creditcoin3-node
chmod a+x ./proof-gen-api-server
- name: Start creditcoin3-node - Alice
run: |
mkdir -p /var/tmp/custom-contract-bridging-logs/
./target/release/creditcoin3-node \
--chain dev \
--validator --alice --pruning archive \
--node-key d182d503b7dd97e7c055f33438c7717145840fd66b2a055284ee8d768241a463 \
--unsafe-rpc-external --rpc-cors all \
--base-path ./alice-data >/var/tmp/custom-contract-bridging-logs/creditcoin3-node.log 2>&1 &
- name: Start Ethereum simulation with Anvil
run: |
export FOUNDRY_DIR="$HOME/.foundry"
# first install it
curl -L https://foundry.paradigm.xyz | bash
~/.foundry/bin/foundryup
# supported chain keys are hard-coded in attestor/src/cc3.rs and node/src/chain_spec.rs
# Anvil1 == chain_key(2)
~/.foundry/bin/anvil --block-time 6 --chain-id 31337 --port 8141 &
- name: Check that creditcoin3-node and anvil are running
run: |
.github/wait-for-creditcoin.sh 'http://127.0.0.1:9944'
.github/wait-for-ethereum.sh 'http://127.0.0.1:8141'
- name: Start Attestation Zombienet for Alice - Anvil 1
run: |
mkdir -p /var/tmp/custom-contract-bridging-logs/attestors-alice
./target/release/attestor_zombienet \
-n 3 \
--bin=./target/release/attestor \
--eth-url=ws://localhost:8141 \
--cc3-url=ws://localhost:9944 \
--funding-address='//Alice' \
--config=.github/attestor-config.yaml \
--chain-key=2 &
- name: Wait for 3 attestors to be registered
run: |
# note: chain_key is hard-coded in this script so waiting only on Anvil_1
.github/wait-for-attestors.sh 'http://127.0.0.1:9944'
- name: Start proof-gen-api-server DB
run: |
docker run --rm --name proof-gen-postgres \
-p 5433:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=proofs_db \
-d postgres
sleep 60
- name: Start proof-gen-api-server
run: |
POSTGRES_HOST=localhost POSTGRES_PORT=5433 POSTGRES_USER=postgres POSTGRES_PASSWORD=password POSTGRES_DB=proofs_db \
./target/release/proof-gen-api-server \
--cc3-key "//Alice" \
--cc3-rpc-url ws://localhost:9944 \
--eth-rpc-url http://localhost:8141 \
>/var/tmp/custom-contract-bridging-logs/proof-gen-api-server.log &
sleep 30
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- run: npm install -g yarn
- name: Setup
id: setup
run: |
yarn install
SOURCE_CHAIN_RPC_URL="ws://127.0.0.1:8141"
echo "INFO: generate a new wallet"
OUTPUT=$(~/.foundry/bin/cast wallet new)
echo "$OUTPUT"
WALLET_ADDRESS=$(echo "$OUTPUT" | grep Address: | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_address=$WALLET_ADDRESS" >> "$GITHUB_OUTPUT"
WALLET_PK=$(echo "$OUTPUT" | grep "Private key:" | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_pk=$WALLET_PK" >> "$GITHUB_OUTPUT"
echo "INFO: get 1000 ETH on source chain"
# note: using Anvil, Account #0
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 1000000000000000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--rpc-url "$SOURCE_CHAIN_RPC_URL"
CREDITCOIN_RPC_URL="ws://127.0.0.1:9944"
echo "INFO: get 2000 CTC on Creditcoin chain"
# note: this is Alith's PK
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 2000000000000000000000 \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
--rpc-url "$CREDITCOIN_RPC_URL"
echo "INFO: deploy TestERC20.sol to source chain"
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$SOURCE_CHAIN_RPC_URL" \
--private-key "$WALLET_PK" \
contracts/sol/TestERC20.sol:TestERC20)
echo "$OUTPUT"
SOURCE_CHAIN_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "source_chain_contract_address=$SOURCE_CHAIN_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: deploy EvmV1Decoder.sol to Creditcoin chain"
# note: this is Alith's PK
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$CREDITCOIN_RPC_URL" \
--private-key "$WALLET_PK" \
contracts/sol/EvmV1Decoder.sol:EvmV1Decoder)
echo "$OUTPUT"
DECODER_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "INFO: Modify SimpleMinterUSC.sol to mint 2x"
sed -i "s/_mint(msg.sender, MINT_AMOUNT);/_mint(msg.sender, MINT_AMOUNT * 2);/" contracts/sol/SimpleMinterUSC.sol
git diff
echo "INFO: deploy SimpleMinterUSC.sol to Creditcoin chain"
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$CREDITCOIN_RPC_URL" \
--private-key "$WALLET_PK" \
--libraries "contracts/sol/EvmV1Decoder.sol:EvmV1Decoder:$DECODER_CONTRACT_ADDRESS" \
contracts/sol/SimpleMinterUSC.sol:SimpleMinterUSC)
echo "$OUTPUT"
SIMPLE_MINTER_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "simple_minter_contract_address=$SIMPLE_MINTER_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: NOT minting tokens on source chain. Should already have some minted"
echo "INFO: burn the tokens (on source chain) we want to bridge"
OUTPUT=$(~/.foundry/bin/cast send --rpc-url "$SOURCE_CHAIN_RPC_URL" \
"$SOURCE_CHAIN_CONTRACT_ADDRESS" \
"burn(uint256)" 50000000000000000000 \
--private-key "$WALLET_PK")
echo "$OUTPUT"
BURN_TX_HASH=$(echo "$OUTPUT" | grep "transactionHash " | tr -s " " | cut -f2 -d" ")
echo "burn_tx_hash=$BURN_TX_HASH" >> "$GITHUB_OUTPUT"
- name: Prepare local .env
run: |
echo "SOURCE_CHAIN_KEY=2" > .env
echo "PROVER_API_URL='http://localhost:3100'" >> .env
echo "SOURCE_CHAIN_CUSTOM_CONTRACT_ADDRESS='${{ steps.setup.outputs.source_chain_contract_address }}'" >> .env
echo "CREDITCOIN_RPC_URL='http://127.0.0.1:9944'" >> .env
echo "SOURCE_CHAIN_RPC_URL='http://127.0.0.1:8141'" >> .env
echo "USC_CUSTOM_MINTER_CONTRACT_ADDRESS='${{ steps.setup.outputs.simple_minter_contract_address }}'" >> .env
echo "CREDITCOIN_WALLET_PRIVATE_KEY='${{ steps.setup.outputs.creditcoin_wallet_pk }}'" >> .env
echo "===== DEBUG ====="
cat .env
echo "===== END DEBUG ====="
- name: Exercise example
run: |
source .env
echo "INFO: Should not have any bridged tokens initially"
OUTPUT=$(yarn utils:check_balance "$USC_CUSTOM_MINTER_CONTRACT_ADDRESS" "${{ steps.setup.outputs.creditcoin_wallet_address }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 0"
echo "$OUTPUT" | grep "Formatted Balance: 0.0 TEST"
echo "INFO: Submit a mint query to the USC contract"
OUTPUT=$(yarn custom_bridge:submit_query "${{ steps.setup.outputs.burn_tx_hash }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Minting completed"
echo "INFO: Verify bridged tokens"
OUTPUT=$(yarn utils:check_balance "$USC_CUSTOM_MINTER_CONTRACT_ADDRESS" "${{ steps.setup.outputs.creditcoin_wallet_address }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 2000"
echo "$OUTPUT" | grep "Formatted Balance: 0.000000000000002 TEST"
# WARNING: this is 2x compared to hello-bridge example
- name: Collect logs
if: always()
run: |
docker logs proof-gen-postgres > /var/tmp/custom-contract-bridging-logs/proof-gen-postgres.log
- name: Upload logs
if: always()
uses: actions/upload-artifact@v6
with:
name: custom-contract-bridging-logs
path: /var/tmp/custom-contract-bridging-logs/
include-hidden-files: true
- name: Kill processes
if: always()
continue-on-error: true
run: |
killall -9 attestor_zombienet
killall -9 attestor
killall -9 creditcoin3-node
bridge-offchain-worker:
runs-on: ubuntu-24.04
needs: build
steps:
- uses: actions/checkout@v6
- name: Download binaries
uses: actions/download-artifact@v7
with:
name: binary-for-testing-ci
path: target/release
- name: Restore executable permissions
working-directory: target/release
run: |
chmod a+x ./attestor*
chmod a+x ./creditcoin3-node
chmod a+x ./proof-gen-api-server
- name: Start creditcoin3-node - Alice
run: |
mkdir -p /var/tmp/bridge-offchain-worker-logs/
./target/release/creditcoin3-node \
--chain dev \
--validator --alice --pruning archive \
--node-key d182d503b7dd97e7c055f33438c7717145840fd66b2a055284ee8d768241a463 \
--unsafe-rpc-external --rpc-cors all \
--base-path ./alice-data >/var/tmp/bridge-offchain-worker-logs/creditcoin3-node.log 2>&1 &
- name: Start Ethereum simulation with Anvil
run: |
export FOUNDRY_DIR="$HOME/.foundry"
# first install it
curl -L https://foundry.paradigm.xyz | bash
~/.foundry/bin/foundryup
# supported chain keys are hard-coded in attestor/src/cc3.rs and node/src/chain_spec.rs
# Anvil1 == chain_key(2)
~/.foundry/bin/anvil --block-time 6 --chain-id 31337 --port 8141 &
- name: Check that creditcoin3-node and anvil are running
run: |
.github/wait-for-creditcoin.sh 'http://127.0.0.1:9944'
.github/wait-for-ethereum.sh 'http://127.0.0.1:8141'
- name: Start Attestation Zombienet for Alice - Anvil 1
run: |
mkdir -p /var/tmp/bridge-offchain-worker-logs/attestors-alice
./target/release/attestor_zombienet \
-n 3 \
--bin=./target/release/attestor \
--eth-url=ws://localhost:8141 \
--cc3-url=ws://localhost:9944 \
--funding-address='//Alice' \
--config=.github/attestor-config.yaml \
--chain-key=2 &
- name: Wait for 3 attestors to be registered
run: |
# note: chain_key is hard-coded in this script so waiting only on Anvil_1
.github/wait-for-attestors.sh 'http://127.0.0.1:9944'
- name: Start proof-gen-api-server DB
run: |
docker run --rm --name proof-gen-postgres \
-p 5433:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=proofs_db \
-d postgres
sleep 60
- name: Start proof-gen-api-server
run: |
POSTGRES_HOST=localhost POSTGRES_PORT=5433 POSTGRES_USER=postgres POSTGRES_PASSWORD=password POSTGRES_DB=proofs_db \
./target/release/proof-gen-api-server \
--cc3-key "//Alice" \
--cc3-rpc-url ws://localhost:9944 \
--eth-rpc-url http://localhost:8141 \
>/var/tmp/bridge-offchain-worker-logs/proof-gen-api-server.log &
sleep 30
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- run: npm install -g yarn
- name: Setup
id: setup
run: |
yarn install
SOURCE_CHAIN_RPC_URL="ws://127.0.0.1:8141"
echo "INFO: generate a new wallet"
OUTPUT=$(~/.foundry/bin/cast wallet new)
echo "$OUTPUT"
WALLET_ADDRESS=$(echo "$OUTPUT" | grep Address: | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_address=$WALLET_ADDRESS" >> "$GITHUB_OUTPUT"
WALLET_PK=$(echo "$OUTPUT" | grep "Private key:" | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_pk=$WALLET_PK" >> "$GITHUB_OUTPUT"
echo "INFO: get 1000 ETH on source chain"
# note: using Anvil, Account #0
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 1000000000000000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--rpc-url "$SOURCE_CHAIN_RPC_URL"
CREDITCOIN_RPC_URL="ws://127.0.0.1:9944"
echo "INFO: get 2000 CTC on Creditcoin chain"
# note: this is Alith's PK
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 2000000000000000000000 \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
--rpc-url "$CREDITCOIN_RPC_URL"
echo "INFO: deploy TestERC20.sol to source chain"
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$SOURCE_CHAIN_RPC_URL" \
--private-key "$WALLET_PK" \
contracts/sol/TestERC20.sol:TestERC20)
echo "$OUTPUT"
SOURCE_CHAIN_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "source_chain_contract_address=$SOURCE_CHAIN_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: deploy EvmV1Decoder.sol to Creditcoin chain"
# note: this is Alith's PK
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$CREDITCOIN_RPC_URL" \
--private-key "$WALLET_PK" \
contracts/sol/EvmV1Decoder.sol:EvmV1Decoder)
echo "$OUTPUT"
DECODER_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "INFO: Modify SimpleMinterUSC.sol to mint 3x"
sed -i "s/_mint(msg.sender, MINT_AMOUNT);/_mint(msg.sender, MINT_AMOUNT * 3);/" contracts/sol/SimpleMinterUSC.sol
git diff
echo "INFO: deploy SimpleMinterUSC.sol to Creditcoin chain"
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$CREDITCOIN_RPC_URL" \
--private-key "$WALLET_PK" \
--libraries "contracts/sol/EvmV1Decoder.sol:EvmV1Decoder:$DECODER_CONTRACT_ADDRESS" \
contracts/sol/SimpleMinterUSC.sol:SimpleMinterUSC)
echo "$OUTPUT"
SIMPLE_MINTER_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "simple_minter_contract_address=$SIMPLE_MINTER_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: NOT minting tokens on source chain. Should already have some minted"
- name: Prepare local .env
run: |
echo "SOURCE_CHAIN_KEY=2" > .env
echo "PROVER_API_URL='http://localhost:3100'" >> .env
echo "SOURCE_CHAIN_CUSTOM_CONTRACT_ADDRESS='${{ steps.setup.outputs.source_chain_contract_address }}'" >> .env
echo "CREDITCOIN_RPC_URL='http://127.0.0.1:9944'" >> .env
echo "SOURCE_CHAIN_RPC_URL='http://127.0.0.1:8141'" >> .env
echo "USC_CUSTOM_MINTER_CONTRACT_ADDRESS='${{ steps.setup.outputs.simple_minter_contract_address }}'" >> .env
echo "CREDITCOIN_WALLET_PRIVATE_KEY='${{ steps.setup.outputs.creditcoin_wallet_pk }}'" >> .env
echo "===== DEBUG ====="
cat .env
echo "===== END DEBUG ====="
- name: Exercise example
run: |
source .env
echo "INFO: Should not have any bridged tokens initially"
OUTPUT=$(yarn utils:check_balance "$USC_CUSTOM_MINTER_CONTRACT_ADDRESS" "${{ steps.setup.outputs.creditcoin_wallet_address }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 0"
echo "$OUTPUT" | grep "Formatted Balance: 0.0 TEST"
echo "INFO: start offchain worker"
yarn offchain:start_worker > /var/tmp/bridge-offchain-worker-logs/offchain-worker.log 2>&1 &
sleep 10
echo "INFO: burn the tokens (on source chain) we want to bridge"
OUTPUT=$(~/.foundry/bin/cast send --rpc-url "$SOURCE_CHAIN_RPC_URL" \
"$SOURCE_CHAIN_CUSTOM_CONTRACT_ADDRESS" \
"burn(uint256)" 50000000000000000000 \
--private-key "$CREDITCOIN_WALLET_PRIVATE_KEY")
echo "$OUTPUT"
# wait for attestations for the source block where we burned the tokens
sleep 500
grep "Tokens minted!" /var/tmp/bridge-offchain-worker-logs/offchain-worker.log
echo "INFO: Verify bridged tokens"
OUTPUT=$(yarn utils:check_balance "$USC_CUSTOM_MINTER_CONTRACT_ADDRESS" "${{ steps.setup.outputs.creditcoin_wallet_address }}")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 3000"
echo "$OUTPUT" | grep "Formatted Balance: 0.000000000000003 TEST"
# WARNING: this is 3x compared to hello-bridge example
- name: Collect logs
if: always()
run: |
docker logs proof-gen-postgres > /var/tmp/bridge-offchain-worker-logs/proof-gen-postgres.log
- name: Upload logs
if: always()
uses: actions/upload-artifact@v6
with:
name: bridge-offchain-worker-logs
path: /var/tmp/bridge-offchain-worker-logs/
include-hidden-files: true
- name: Kill processes
if: always()
continue-on-error: true
run: |
killall -9 attestor_zombienet
killall -9 attestor
killall -9 creditcoin3-node
killall -9 node
killall -9 tsx
loan-flow:
runs-on: ubuntu-24.04
needs: build
steps:
- uses: actions/checkout@v6
- name: Download binaries
uses: actions/download-artifact@v7
with:
name: binary-for-testing-ci
path: target/release
- name: Restore executable permissions
working-directory: target/release
run: |
chmod a+x ./attestor*
chmod a+x ./creditcoin3-node
chmod a+x ./proof-gen-api-server
- name: Start creditcoin3-node - Alice
run: |
mkdir -p /var/tmp/loan-flow-logs/
./target/release/creditcoin3-node \
--chain dev \
--validator --alice --pruning archive \
--node-key d182d503b7dd97e7c055f33438c7717145840fd66b2a055284ee8d768241a463 \
--unsafe-rpc-external --rpc-cors all \
--base-path ./alice-data >/var/tmp/loan-flow-logs/creditcoin3-node.log 2>&1 &
- name: Start Ethereum simulation with Anvil
run: |
export FOUNDRY_DIR="$HOME/.foundry"
# first install it
curl -L https://foundry.paradigm.xyz | bash
~/.foundry/bin/foundryup
# supported chain keys are hard-coded in attestor/src/cc3.rs and node/src/chain_spec.rs
# Anvil1 == chain_key(2)
~/.foundry/bin/anvil --block-time 6 --chain-id 31337 --port 8141 &
- name: Check that creditcoin3-node and anvil are running
run: |
.github/wait-for-creditcoin.sh 'http://127.0.0.1:9944'
.github/wait-for-ethereum.sh 'http://127.0.0.1:8141'
- name: Start Attestation Zombienet for Alice - Anvil 1
run: |
mkdir -p /var/tmp/loan-flow-logs/attestors-alice
./target/release/attestor_zombienet \
-n 3 \
--bin=./target/release/attestor \
--eth-url=ws://localhost:8141 \
--cc3-url=ws://localhost:9944 \
--funding-address='//Alice' \
--config=.github/attestor-config.yaml \
--chain-key=2 &
- name: Wait for 3 attestors to be registered
run: |
# note: chain_key is hard-coded in this script so waiting only on Anvil_1
.github/wait-for-attestors.sh 'http://127.0.0.1:9944'
- name: Start proof-gen-api-server DB
run: |
docker run --rm --name proof-gen-postgres \
-p 5433:5432 \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=proofs_db \
-d postgres
sleep 60
- name: Start proof-gen-api-server
run: |
POSTGRES_HOST=localhost POSTGRES_PORT=5433 POSTGRES_USER=postgres POSTGRES_PASSWORD=password POSTGRES_DB=proofs_db \
./target/release/proof-gen-api-server \
--cc3-key "//Alice" \
--cc3-rpc-url ws://localhost:9944 \
--eth-rpc-url http://localhost:8141 \
>/var/tmp/loan-flow-logs/proof-gen-api-server.log &
sleep 30
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- run: npm install -g yarn
- name: Setup
id: setup
run: |
yarn install
SOURCE_CHAIN_RPC_URL="ws://127.0.0.1:8141"
echo "INFO: generate a new wallet"
OUTPUT=$(~/.foundry/bin/cast wallet new)
echo "$OUTPUT"
WALLET_ADDRESS=$(echo "$OUTPUT" | grep Address: | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_address=$WALLET_ADDRESS" >> "$GITHUB_OUTPUT"
WALLET_PK=$(echo "$OUTPUT" | grep "Private key:" | cut -f2 -d: | tr -d ' ')
echo "creditcoin_wallet_pk=$WALLET_PK" >> "$GITHUB_OUTPUT"
echo "INFO: get 1000 ETH on source chain"
# note: using Anvil, Account #0
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 1000000000000000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--rpc-url "$SOURCE_CHAIN_RPC_URL"
CREDITCOIN_RPC_URL="ws://127.0.0.1:9944"
echo "INFO: get 2000 CTC on Creditcoin chain"
# note: this is Alith's PK
~/.foundry/bin/cast send "$WALLET_ADDRESS" "0x" \
--value 2000000000000000000000 \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
--rpc-url "$CREDITCOIN_RPC_URL"
echo "INFO: deploy TestERC20.sol to source chain"
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$SOURCE_CHAIN_RPC_URL" \
--private-key "$WALLET_PK" \
contracts/sol/TestERC20.sol:TestERC20)
echo "$OUTPUT"
SOURCE_CHAIN_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "source_chain_contract_address=$SOURCE_CHAIN_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: deploy AuxiliaryLoanContract.sol to source chain"
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$SOURCE_CHAIN_RPC_URL" \
--private-key "$WALLET_PK" \
contracts/sol/AuxiliaryLoanContract.sol:AuxiliaryLoanContract)
echo "$OUTPUT"
AUX_LOAN_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "aux_loan_contract_address=$AUX_LOAN_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: deploy USCLoanManager.sol to Creditcoin"
OUTPUT=$(~/.foundry/bin/forge create \
--broadcast \
--rpc-url "$CREDITCOIN_RPC_URL" \
--private-key "$WALLET_PK" \
contracts/sol/USCLoanManager.sol:USCLoanManager)
echo "$OUTPUT"
USC_LOAN_MANAGER_CONTRACT_ADDRESS=$(echo "$OUTPUT" | grep "Deployed to:" | cut -f2 -d: | tr -d " ")
echo "usc_loan_manager_contract_address=$USC_LOAN_MANAGER_CONTRACT_ADDRESS" >> "$GITHUB_OUTPUT"
echo "INFO: generate a Lender wallet"
OUTPUT=$(~/.foundry/bin/cast wallet new)
echo "$OUTPUT"
LENDER_WALLET_ADDRESS=$(echo "$OUTPUT" | grep Address: | cut -f2 -d: | tr -d ' ')
echo "lender_wallet_address=$LENDER_WALLET_ADDRESS" >> "$GITHUB_OUTPUT"
LENDER_WALLET_PK=$(echo "$OUTPUT" | grep "Private key:" | cut -f2 -d: | tr -d ' ')
echo "lender_wallet_pk=$LENDER_WALLET_PK" >> "$GITHUB_OUTPUT"
echo "INFO: get 100 ETH on source chain"
# note: using Anvil, Account #0
~/.foundry/bin/cast send "$LENDER_WALLET_ADDRESS" "0x" \
--value 100000000000000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--rpc-url "$SOURCE_CHAIN_RPC_URL"
echo "INFO: get 200 CTC on Creditcoin chain"
# note: this is Alith's PK
~/.foundry/bin/cast send "$LENDER_WALLET_ADDRESS" "0x" \
--value 200000000000000000000 \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
--rpc-url "$CREDITCOIN_RPC_URL"
echo "INFO: mint TEST tokens on source chain"
~/.foundry/bin/cast send --rpc-url "$SOURCE_CHAIN_RPC_URL" \
"$SOURCE_CHAIN_CONTRACT_ADDRESS" \
"mint(uint256)" 5000000000000000000 \
--private-key "$LENDER_WALLET_PK"
echo "INFO: generate a Borrower wallet"
OUTPUT=$(~/.foundry/bin/cast wallet new)
echo "$OUTPUT"
BORROWER_WALLET_ADDRESS=$(echo "$OUTPUT" | grep Address: | cut -f2 -d: | tr -d ' ')
echo "borrower_wallet_address=$BORROWER_WALLET_ADDRESS" >> "$GITHUB_OUTPUT"
BORROWER_WALLET_PK=$(echo "$OUTPUT" | grep "Private key:" | cut -f2 -d: | tr -d ' ')
echo "borrower_wallet_pk=$BORROWER_WALLET_PK" >> "$GITHUB_OUTPUT"
echo "INFO: get 100 ETH on source chain"
# note: using Anvil, Account #0
~/.foundry/bin/cast send "$BORROWER_WALLET_ADDRESS" "0x" \
--value 100000000000000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--rpc-url "$SOURCE_CHAIN_RPC_URL"
echo "INFO: get 200 CTC on Creditcoin chain"
# note: this is Alith's PK
~/.foundry/bin/cast send "$BORROWER_WALLET_ADDRESS" "0x" \
--value 200000000000000000000 \
--private-key 0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \
--rpc-url "$CREDITCOIN_RPC_URL"
echo "INFO: mint TEST tokens on source chain"
~/.foundry/bin/cast send --rpc-url "$SOURCE_CHAIN_RPC_URL" \
"$SOURCE_CHAIN_CONTRACT_ADDRESS" \
"mint(uint256)" 3000000000000000000 \
--private-key "$BORROWER_WALLET_PK"
- name: Prepare local .env
run: |
echo "SOURCE_CHAIN_KEY=2" > .env
echo "PROVER_API_URL='http://localhost:3100'" >> .env
echo "CREDITCOIN_RPC_URL='http://127.0.0.1:9944'" >> .env
echo "SOURCE_CHAIN_RPC_URL='http://127.0.0.1:8141'" >> .env
echo "USC_LOAN_MANAGER_CONTRACT_ADDRESS='${{ steps.setup.outputs.usc_loan_manager_contract_address }}'" >> .env
echo "SOURCE_CHAIN_ERC20_CONTRACT_ADDRESS='${{ steps.setup.outputs.source_chain_contract_address }}'" >> .env
echo "SOURCE_CHAIN_LOAN_CONTRACT_ADDRESS='${{ steps.setup.outputs.aux_loan_contract_address }}'" >> .env
echo "CREDITCOIN_WALLET_PRIVATE_KEY='${{ steps.setup.outputs.creditcoin_wallet_pk }}'" >> .env
echo "LENDER_WALLET_PRIVATE_KEY='${{ steps.setup.outputs.lender_wallet_pk }}'" >> .env
echo "BORROWER_WALLET_PRIVATE_KEY='${{ steps.setup.outputs.borrower_wallet_pk }}'" >> .env
echo "===== DEBUG ====="
cat .env
echo "===== END DEBUG ====="
- name: Exercise example
run: |
source .env
echo "INFO: Lender should have 5 TEST initially"
OUTPUT=$(yarn utils:check_balance "$SOURCE_CHAIN_ERC20_CONTRACT_ADDRESS" "${{ steps.setup.outputs.lender_wallet_address }}" "$SOURCE_CHAIN_RPC_URL")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 5000000000"
echo "$OUTPUT" | grep "Formatted Balance: 5.0 TEST"
echo "INFO: Borrower should have 3 TEST initially"
OUTPUT=$(yarn utils:check_balance "$SOURCE_CHAIN_ERC20_CONTRACT_ADDRESS" "${{ steps.setup.outputs.borrower_wallet_address }}" "$SOURCE_CHAIN_RPC_URL")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 3000000000"
echo "$OUTPUT" | grep "Formatted Balance: 3.0 TEST"
echo "INFO: Authorize token"
OUTPUT=$(yarn loan_flow:authorize_token "$SOURCE_CHAIN_ERC20_CONTRACT_ADDRESS")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Token authorized with transaction hash:"
echo "INFO: start offchain worker"
yarn loan_flow:start_worker > /var/tmp/loan-flow-logs/offchain-worker.log 2>&1 &
sleep 10
echo "INFO: Register a loan"
# <loan amount>, <interest rate>, <expiration blocks>
OUTPUT=$(yarn loan_flow:register_loan 1000 500 1000)
echo "$OUTPUT"
echo "$OUTPUT" | grep "Loan registered with transaction hash:"
LOAN_ID=$(echo "$OUTPUT" | grep "Loan successfully registered with ID:" | cut -f2 -d: | tr -d ' ')
# wait for the off-chain worker to see the event above
# in particular for the source block to be attested to
sleep 60
grep "Detected LoanRegistered event for loanId: $LOAN_ID" /var/tmp/loan-flow-logs/offchain-worker.log
grep "Registered loan $LOAN_ID for funding on source chain" /var/tmp/loan-flow-logs/offchain-worker.log
echo "INFO: inspect loan $LOAN_ID"
yarn loan_flow:inspect_loan "$LOAN_ID"
echo "INFO: Lender completely funds the loan"
OUTPUT=$(yarn loan_flow:fund_loan "$LOAN_ID" 1000)
echo "$OUTPUT"
# wait for the off-chain worker to see the event above
# in particular for the source block to be attested to
sleep 60
grep "Detected LoanFunded event for loanId: $LOAN_ID" /var/tmp/loan-flow-logs/offchain-worker.log
# wait for off-chain worker to prove the event above
sleep 120
grep "Loan $LOAN_ID has been marked as funded on Creditcoin" /var/tmp/loan-flow-logs/offchain-worker.log
echo "INFO: inspect loan $LOAN_ID"
yarn loan_flow:inspect_loan "$LOAN_ID"
echo "INFO: Borrower repays loan + interest"
OUTPUT=$(yarn loan_flow:repay_loan "$LOAN_ID" 1050)
echo "$OUTPUT"
sleep 60
grep "Detected LoanRepaid event for loanId: $LOAN_ID" /var/tmp/loan-flow-logs/offchain-worker.log
# wait for off-chain worker to prove the event above
sleep 120
grep "Loan $LOAN_ID has been marked as fully repaid on Creditcoin" /var/tmp/loan-flow-logs/offchain-worker.log
echo "INFO: inspect loan $LOAN_ID"
yarn loan_flow:inspect_loan "$LOAN_ID"
echo "INFO: Lender should have 5 TEST + interest"
OUTPUT=$(yarn utils:check_balance "$SOURCE_CHAIN_ERC20_CONTRACT_ADDRESS" "${{ steps.setup.outputs.lender_wallet_address }}" "$SOURCE_CHAIN_RPC_URL")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 5000000000000000050"
echo "$OUTPUT" | grep "Formatted Balance: 5.00000000000000005 TEST"
echo "INFO: Borrower should have 3 TEST - interest"
OUTPUT=$(yarn utils:check_balance "$SOURCE_CHAIN_ERC20_CONTRACT_ADDRESS" "${{ steps.setup.outputs.borrower_wallet_address }}" "$SOURCE_CHAIN_RPC_URL")
echo "$OUTPUT"
echo "$OUTPUT" | grep "Raw Balance: 2999999999999999950"
echo "$OUTPUT" | grep "Formatted Balance: 2.99999999999999995 TEST"
- name: Collect logs
if: always()
run: |
docker logs proof-gen-postgres > /var/tmp/loan-flow-logs/proof-gen-postgres.log
- name: Upload logs
if: always()
uses: actions/upload-artifact@v6
with:
name: loan-flow-logs
path: /var/tmp/loan-flow-logs/
include-hidden-files: true
- name: Kill processes
if: always()
continue-on-error: true
run: |
killall -9 attestor_zombienet
killall -9 attestor
killall -9 creditcoin3-node
killall -9 node
killall -9 tsx