Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: CI

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false || github.event.pull_request == null

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
run_install: false

- name: Setup Node.js (act)
if: env.ACT == 'true'
uses: actions/setup-node@v4
with:
node-version: '22'

- name: Setup Node.js
if: env.ACT != 'true'
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'

- name: Install dependencies (act)
if: env.ACT == 'true'
run: pnpm install --no-frozen-lockfile

- name: Install dependencies
if: env.ACT != 'true'
run: pnpm install --frozen-lockfile

- name: Compile contracts
run: pnpm hardhat compile
env:
SKIP_HARDHAT_TASKS: "true"

- name: Build
run: pnpm run build

- name: Test
run: pnpm test
env:
SKIP_HARDHAT_TASKS: "true"
184 changes: 184 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: "Version (e.g., v0.1.0-beta). Auto-increment patch if omitted."
required: false
default: ""
dist_tag:
description: "npm dist-tag (e.g., latest, beta)."
required: false
default: "beta"

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
concurrency:
group: release-${{ github.ref_name }}
cancel-in-progress: false
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Setup Node.js (act)
if: ${{ env.ACT == 'true' }}
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org/'

- name: Setup Node.js
if: ${{ env.ACT != 'true' }}
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: '**/pnpm-lock.yaml'
registry-url: 'https://registry.npmjs.org/'

- name: Install dependencies (act)
if: ${{ env.ACT == 'true' }}
run: pnpm install --no-frozen-lockfile

- name: Install dependencies
if: ${{ env.ACT != 'true' }}
run: pnpm install --frozen-lockfile

- name: Compile contracts
if: ${{ env.ACT != 'true' }}
run: pnpm hardhat compile
env:
SKIP_HARDHAT_TASKS: "true"

- name: Run tests
if: ${{ env.ACT != 'true' }}
run: pnpm test
env:
SKIP_HARDHAT_TASKS: "true"

- name: Compute version
id: version
shell: bash
run: |
set -euo pipefail
INPUT_VERSION="${{ github.event.inputs.version }}"
CHANNEL="${{ github.event.inputs.dist_tag }}"
SHORT_SHA=$(git rev-parse --short HEAD)
if [ -n "$INPUT_VERSION" ]; then
echo "version=$INPUT_VERSION" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$CHANNEL" != "latest" ] && [ -n "$CHANNEL" ]; then
BASE_VERSION=$(node -p "require('./package.json').version")
NEXT="v${BASE_VERSION}-${CHANNEL}.${SHORT_SHA}"
echo "version=$NEXT" >> "$GITHUB_OUTPUT"
exit 0
fi
LATEST=$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' | sort -V | tail -n 1 || true)
if [ -z "$LATEST" ]; then
BASE_VERSION=$(node -p "require('./package.json').version")
NEXT="v${BASE_VERSION}"
else
BASE=${LATEST#v}
NEXT="v$(npx --yes semver -i patch "$BASE")"
fi
echo "version=$NEXT" >> "$GITHUB_OUTPUT"

- name: Update package version
run: pnpm version "$(echo "${{ steps.version.outputs.version }}" | sed 's/^v//')" --no-git-tag-version

- name: Build
run: pnpm run build

- name: Ensure version not already on npm
if: ${{ env.ACT != 'true' }}
shell: bash
run: |
set -euo pipefail
PKG_NAME=$(node -p "require('./package.json').name")
VERSION=$(echo "${{ steps.version.outputs.version }}" | sed 's/^v//')
if npm view "$PKG_NAME@$VERSION" version >/dev/null 2>&1; then
echo "Version $VERSION is already published to npm. Aborting."
exit 1
fi

- name: Publish to npm
if: ${{ env.ACT != 'true' }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
shell: bash
run: |
set -euo pipefail
pnpm publish --tag "${{ github.event.inputs.dist_tag }}" --access public --provenance --no-git-checks

- name: Commit version bump
if: ${{ env.ACT != 'true' }}
env:
VERSION: ${{ steps.version.outputs.version }}
BRANCH: ${{ github.ref_name }}
GIT_AUTHOR_NAME: github-actions[bot]
GIT_AUTHOR_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
shell: bash
run: |
set -euo pipefail
git add package.json pnpm-lock.yaml || true
if git diff --cached --quiet; then
echo "No version changes to commit"
else
git commit -m "chore(release): $VERSION [skip ci]"
git push origin HEAD:refs/heads/$BRANCH
fi

- name: Ensure git tag does not already exist
if: ${{ env.ACT != 'true' }}
shell: bash
run: |
set -euo pipefail
TAG="${{ steps.version.outputs.version }}"
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "Git tag $TAG already exists locally."
exit 1
fi
if git ls-remote --tags origin "refs/tags/$TAG" | grep -q .; then
echo "Git tag $TAG already exists on origin."
exit 1
fi

- name: Create git tag
if: ${{ env.ACT != 'true' }}
shell: bash
run: |
set -euo pipefail
git tag -a "${{ steps.version.outputs.version }}" -m "Release ${{ steps.version.outputs.version }}"
git push origin "${{ steps.version.outputs.version }}"

- name: Create GitHub release
if: ${{ env.ACT != 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
PRERELEASE_FLAG=""
if [[ "${{ github.event.inputs.dist_tag }}" != "latest" && -n "${{ github.event.inputs.dist_tag }}" ]]; then
PRERELEASE_FLAG="--prerelease"
fi
gh release create "${{ steps.version.outputs.version }}" \
--title "${{ steps.version.outputs.version }}" \
--generate-notes $PRERELEASE_FLAG
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ gitignore
node_modules

# we want to keep it flexible for different package managers
pnpm-lock.yaml
yarn.lock
deno.lock
package-lock.json
Expand Down Expand Up @@ -42,4 +41,3 @@ build/
# Debug logs
debug/
logs/

13 changes: 13 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright 2025 Tru Labs Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
52 changes: 49 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,54 @@
# Chainlink Functions - TN
# @trufnetwork/evm-contracts

## Description
Solidity helpers and supporting TypeScript utilities for verifying TrufNetwork attestations. The package is published on npm as [`@trufnetwork/evm-contracts`](https://www.npmjs.com/package/@trufnetwork/evm-contracts).

This directory contains development tools to test and simulate Chainlink Functions, an example consumer, and the Solidity attestation verification library used by TrufNetwork contracts.
## Installation

```bash
pnpm add @trufnetwork/evm-contracts
```

or with npm:

```bash
npm install @trufnetwork/evm-contracts
```

## Solidity Usage

```solidity
import {TrufAttestation} from "@trufnetwork/evm-contracts/contracts/attestation/TrufAttestation.sol";

contract Consumer {
using TrufAttestation for bytes;

function verify(bytes calldata payload, address validator) external view returns (bool) {
TrufAttestation.Attestation memory att = payload.parse();
return att.verify(validator);
}
}
```

For a more complete example (including leader management), see the reference contract linked in the [Attestation Library guide](docs/AttestationLibrary.md).

## TypeScript Helpers

The package also exports helpers for building canonical payloads in tests or off-chain tooling:

```ts
import { buildCanonicalAttestation, buildSignedAttestation } from "@trufnetwork/evm-contracts";

const canonical = buildCanonicalAttestation(fields);
const payload = buildSignedAttestation(fields, signatureBytes);
```

Refer to `docs/AttestationLibrary.md` for details on the field layout and helper APIs.

---

# Chainlink Functions Toolkit

The repository still contains development tooling for Chainlink Functions and legacy oracle flows. The sections below cover the existing setup.

## Requirements

Expand Down
6 changes: 3 additions & 3 deletions docs/AttestationLibrary.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Attestation Library

The `contracts/attestation/TrufAttestation.sol` library lets Solidity contracts verify TrufNetwork attestations on-chain. Phase 1 keeps the scope small: single secp256k1 signer, canonical payload parsing, and caller-managed safeguards.
The `contracts/attestation/TrufAttestation.sol` library lets Solidity contracts verify TrufNetwork attestations on-chain. Install it with `pnpm add @trufnetwork/evm-contracts`. The implementation focuses on single secp256k1 signatures, canonical payload parsing, and caller-managed safeguards.

## When to Use It
- You receive the nine-field payload returned by `get_signed_attestation`.
Expand Down Expand Up @@ -51,7 +51,7 @@ When crafting fixtures or unit tests, reuse the exported builders:
import {
buildCanonicalAttestation,
buildSignedAttestation,
} from "@trufnetwork/evm-contracts/src";
} from "@trufnetwork/evm-contracts";
```
They mirror the canonical encoder maintained in the TrufNetwork node repo (`github.com/trufnetwork/node`, file `extensions/tn_attestation/canonical.go`) and power the Hardhat tests in `test/attestation/TrufAttestation.test.ts`.

Expand All @@ -62,6 +62,6 @@ They mirror the canonical encoder maintained in the TrufNetwork node repo (`gith
- Surface verification failures in logs/metrics instead of swallowing them silently.

## Example Contract
- A minimal, non-production example lives at `contracts/attestation/TrufAttestationConsumer.sol`. It keeps a single owner-set leader and shows how to persist the latest datapoint.
- A minimal, non-production example lives in the repository at `contracts/attestation/TrufAttestationConsumer.sol`. It keeps a single owner-set leader and shows how to persist the latest datapoint.
- The accompanying test suite (`test/attestation/TrufAttestationConsumer.test.ts`) demonstrates end-to-end verification using the golden fixture.
- Replace the leader management with your own governance/allowlist logic before shipping to mainnet.
15 changes: 3 additions & 12 deletions hardhat.config.cts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import "@nomicfoundation/hardhat-chai-matchers";
import "hardhat-switch-network";


import './tasks'
if (process.env.SKIP_HARDHAT_TASKS !== "true") {
require("./tasks");
}

subtask(TASK_COMPILE_SOLIDITY).setAction(async (_, { config }, runSuper) => {
const superRes = await runSuper();
Expand All @@ -20,17 +22,6 @@ subtask(TASK_COMPILE_SOLIDITY).setAction(async (_, { config }, runSuper) => {
join(config.paths.artifacts, "package.json"),
'{ "type": "commonjs" }'
);
} catch (error) {
console.error("Error writing package.json: ", error);
}

return superRes;
});

subtask(TASK_COMPILE_SOLIDITY).setAction(async (_, { config }, runSuper) => {
const superRes = await runSuper();

try {
await writeFile(
join(config.paths.root, "typechain-types", "package.json"),
'{ "type": "commonjs" }'
Expand Down
Loading