Arbitrum Academic Bridge @ NTUA — Final Grant Report (Ethereum Greece)

Grant: $26,800
Host institution: National Technical University of Athens, School of ECE (Greece)
Reporting period: March – August 2026
Original proposal on Questbook: Arbitrum Academic Bridge @ NTUA (ECE)

Over five months, Ethereum Greece delivered the Arbitrum Academic Bridge at NTUA ECE: 8 in-person sessions, 8 ecosystem webinars with guest speakers from L2BEAT, Offchain Labs, CardChase, GMX, Kleros, ChainCraft, Uniswap Labs and Lido, weekly office hours, a public Stylus companion repository, a public Dune dashboard tracking on-chain activity of the funded cohort, and 8 YouTube recordings on the ETH Greece channel. The programme closes with a durable community, a full library of reusable materials, and a documented on-chain footprint from the funded participant wallets.

Headline outcomes

  • 8 in-person sessions and 8 ecosystem webinars delivered (target: 8 + 8) :white_check_mark:
  • 8 external guest speakers featured, all from active Arbitrum-ecosystem or Ethereum-infrastructure teams (target: ≥ 5) :white_check_mark:
  • 8 distinct Arbitrum-adjacent app/protocol teams engaged (target: ≥ 3) :white_check_mark:
  • 30 funded participant wallets on Arbitrum One; 29 with ≥ 1 on-chain tx (target: ≥ 40 with 3+ tx partially met, see note below)
  • 96 cohort transactions, 100% successful, across 38 distinct target addresses on Arbitrum
  • Full library of reusable materials: 8 slide decks, 8 webinar recordings, Stylus companion GitHub repo ( GitHub - TzannetosGiannis/arbitrum_smart_contract_101 · GitHub for students at NTUA), a public Dune dashboard

Milestone reports

Each milestone was reported in full as a PDF deliverable. Those PDFs contain all the necessary information to evaluate our work (material, guest speakers, etc.). The links below carry the granular section-by-section detail; this post consolidates the programme-level picture.

Programme-wide KPI scorecard based on proposal

A note on the on-chain KPI. The proposal set a target of 40 wallets with 3+ mainnet transactions. Our funded cohort is 30 wallets (17 during M1–M2, 13 more during Session 7). Of these, 29 executed at least one on-chain transaction and 16 crossed the 3+ transaction threshold within the tracking window. We treat this as partially met: the qualitative behaviour targeted by the KPI which is unassisted on-chain engagement with Arbitrum applications is well-evidenced (96 successful transactions across 38 target addresses), but at a smaller absolute scale than the original 40-wallet ambition. Full per-wallet breakdown is public on the Dune dashboard below.

Deliverables beyond the funded scope

Alongside the committed deliverables, we also delivered a number of adaptations and additional artifacts across the three milestones driven by participant feedback and by our view that this grant should leave durable public goods behind. In chronological order:

  • Guest lecture inside an existing NTUA ECE course (Milestone 1): a dedicated blockchain-fundamentals lecture delivered inside the undergraduate course Software-as-a-Service Technologies (~30 students, 16/3/2026), introducing Bitcoin, Ethereum, and the rationale for Layer-2 systems such as Arbitrum. Following the session, the course professor integrated the material into the official course platform and now publishes programme announcements through the course-management panel extending the programme’s reach to students not otherwise connected to ETH Greece.
  • Extension of in-person sessions from 1 hour to 2 hours (Milestone 1 onward). After participants explicitly asked for more time to engage with the technical material, all in-person sessions from Session 2 onward were extended to 2 hours. This became the standing format for the remainder of the programme, allowing deeper protocol walkthroughs, live on-chain interaction, and unhurried Q&A.
  • Open slide library on Google Drive: all 8 session decks published as an open library, in response to recurring requests from participants who could not commit to attending the 2-hour in-person sessions but wanted to follow the material asynchronously.
  • Webinar recordings on a public YouTube channel: all 8 webinars recorded and published on the ETH Greece YouTube channel, turning each webinar into a durable, asynchronously accessible educational resource. 242+ cumulative views to date; recordings continue to accrue views post-programme.
  • Stylus companion GitHub repository (Milestone 2): A hands-on walkthrough of deploying Stylus smart contracts on a Dockerized local Arbitrum environment, with explicit guidance on using AI coding tools to accelerate Rust-based Arbitrum development. Public and will continue to be maintained beyond the grant.
  • Session 7 Onboarding refresh and Milestone 3 wallet-cohort expansion: the first hour of Session 7 was added to re-deliver the Session 3 Practical Onboarding to 13 newly-joined participants, with wallets funded at ~0.004 ETH each on Arbitrum One. This grew the tracked cohort from 17 to 30 wallets and is what the public Dune dashboard now covers.
  • Public Dune dashboard tracking cohort on-chain activity. A live public dashboard covering all 30 funded wallets, with 96 successful transactions across 38 distinct target addresses recorded to date. Built as a permanent public artifact so any observer can audit the programme’s real on-chain footprint.

What continues after the grant

Ethereum Greece is committed to supporting students at NTUA ECE and across the wider Greek academic community who choose to pursue blockchain-related diploma theses, with technical mentorship and connections into the Arbitrum ecosystem. The webinar series will continue beyond this programme, with additional Arbitrum ecosystem projects invited to present. We will run regular in-person and online community events to sustain the engagement built during this programme, and will actively seek closer collaboration with the Arbitrum ecosystem so that the community, the educational library, and the on-chain footprint documented here continue to compound.

Links

Point of contact: Ioannis Tzannetos
Telegram: itzannetos
LinkedIn: https://www.linkedin.com/in/giannis-tzannetos/

2 Likes

Great to see students getting practical experience with Arbitrum. During the wallet onboarding sessions, were there any common problems students faced, such as getting ETH for gas, connecting to dApps, understanding transaction details, or finding tokens?

Feedback from first-time users would be helpful for improving the mobile wallet experience.

Thanks for sharing this detailed and honest final report, especially the public slide decks, recordings and Dune dashboard that frame this as a reusable educational public good. For similar future initiatives, I’d be keen to see KPIs evolve beyond basic wallet/tx counts towards longer‑term Arbitrum‑native outcomes such as sustained activity, governance participation and concrete contributions from cohort participants.
@itzannetos

We didn’t encounter any issues related to getting ETH for gas, as we funded the students’ wallets in advance.

One challenge we did observe was that many students underestimated the importance of securely storing their recovery phrase. Two weeks after the wallet onboarding session, I asked everyone to share their experiences using their wallets. Interestingly, two students had already lost access to their wallets because they hadn’t stored their recovery phrases properly and had to create new ones. While unfortunate, it became a valuable learning moment for the rest of the group. It also turned into a fun discussion where students shared how they had been using their wallets, which helped increase engagement and let us see who had remained active.

Another observation came when using MetaMask to perform swaps through Metamask Router. Several students found it difficult to distinguish which network a token belonged to (from the mobile app). They intended to stay on Arbitrum, but it wasn’t immediately obvious whether, for example, USDC was on Ethereum or Arbitrum during the swap flow. Though this as they mature in the ecosystem they will know what to expect and how to use their wallets.

Other than that, the students were engineers, so creating and managing a wallet came quite naturally to them. What excited them the most was the realization that they could access a programmable financial system directly from their laptops without needing permission or going through a KYC process.

Thanks! I completely agree that these would be much stronger indicators of success.

I’ve been thinking about how we could design KPIs around longer-term outcomes, but it’s not always straightforward to define metrics that are both meaningful and practical to measure over time. I’d love to hear any suggestions or examples of approaches that have worked well in other educational initiatives. It would definitely help us design stronger proposals and evaluation frameworks for future cohorts.

1 Like

Thanks, this is very helpful. The recovery phrase issue and the confusion around which network a token belongs to are both important points.

Losing access after only two weeks shows how easily new users can underestimate wallet backups. Clearer network labels during swaps could also help prevent mistakes.

I’ll share these points with our team as product feedback. Thanks for sharing the students’ actual experience.

Thanks for the openness. A practical approach could be to set a small number of follow up KPIs at 30, 90 and 180 days, rather than relying only on activity during the course.

For example, track the share of participants who remain active on Arbitrum, complete a meaningful onchain action, join governance discussions or voting, contribute to an ecosystem project, or refer new builders and users.

It may also help to combine onchain data with short participant surveys, since contributions such as research, community work and project ideas are not always visible onchain. This would create a more balanced picture of long term impact while keeping reporting practical for the team.

I took a quick look at the public Stylus crowdfunding example. Three implementation points may be worth checking:

  1. initialize() is publicly callable and assigns ownership to the first caller. If deployment and initialization are separate transactions, an uninitialized deployment can potentially be claimed by another address.
  2. contribute() remains available after claim() as long as the deadline has not passed. Since claim() can only execute once, ETH contributed after the initial claim may become permanently inaccessible.
  3. is_active() only checks initialization and the deadline, so it can continue reporting the campaign as active even after funds have already been claimed. This also allows the UI/state model to disagree with the actual lifecycle of the campaign.

Just flagging these from a quick review of the public example — you may want to include the post-claim state explicitly in the campaign state machine.

Thanks, these are great suggestions. I’ll circle back in a few months and check the participants’ activity to see how engagement has evolved over time. If there’s enough meaningful data, I can also create an additional dashboard to track some of these longer-term KPIs and share the results.

1 Like

Thanks for taking the time to review the implementation and flag these issues.

Would you be open to addressing them and submitting a pull request to the repo? It would be great to turn this feedback into a concrete contribution and have an additional contributor to the project.

The review can be turned into a concrete technical contribution.

I would not implement the three points as three independent guards. They are three manifestations of one lifecycle-model defect in crowdfunding/src/lib.rs: campaign state is currently distributed across initialized, claimed, deadline, and total_raised >= goal. Because no single state is authoritative, the contract can accept combinations that the UI, accounting, and withdrawal paths interpret differently.

Rather than submit a pull request under my identity, I am providing the complete correction specification and acceptance criteria below so the project team can integrate it directly. No attribution is required. If provenance is recorded in the report or changelog, “the educational example was extended following an independent public review” is sufficient.

1. Replace independent flags with one authoritative lifecycle

Use an explicit campaign state machine:

UNINITIALIZED
    -> FUNDING
        -> SUCCESSFUL
            -> CLAIMED
        -> FAILED
            -> REFUNDED

The required transition rules are:

  • UNINITIALIZED -> FUNDING: only during atomic deployment/initialization.
  • FUNDING -> SUCCESSFUL: in the same transaction that makes the accepted contribution total reach or exceed the goal.
  • FUNDING -> FAILED: when block.timestamp >= deadline and the goal has not been reached.
  • SUCCESSFUL -> CLAIMED: once, by the owner.
  • FAILED -> REFUNDED: after all refundable contributor balances have been withdrawn.
  • CLAIMED and REFUNDED are terminal states and must never accept new contributions.

Time passing does not execute an on-chain transition by itself, so the implementation should have one internal status-resolution rule. Every mutating method must use the same rule before applying its guard, and the public status() view must expose the same effective state. Individual functions must not reconstruct campaign state independently from separate booleans.

2. Remove the first-caller ownership window

The present initialize() assigns ownership to whichever address calls it first. Because deployment and initialization are separate transactions in the demo, the deployed contract exists in a claimable state between those transactions.

The preferred correction is deployment-time construction:

  • bind the owner, goal, and deadline in the constructor;
  • deploy and initialize atomically;
  • reject a zero owner, zero goal, and zero duration;
  • compute the deadline with checked arithmetic;
  • remove the externally callable first-caller initialization path.

If a separate initializer is retained for teaching purposes, deployment must occur through an atomic deploy-and-initialize path that binds the intended owner. A publicly claimable uninitialized contract should not exist at any observable address.

demo_crowdfunding/deploy.sh should therefore create a fully initialized campaign. simulate.sh should no longer perform ownership acquisition as a later step.

3. Make contribution acceptance a state transition, not only a timestamp check

contribute() should accept ETH only when the effective state is exactly FUNDING.

The boundary should be defined once and used everywhere:

block.timestamp < deadline   => contributions are open
block.timestamp >= deadline  => contributions are closed

For every accepted contribution:

  1. require a non-zero amount;
  2. update the contributor’s refundable principal;
  3. update campaign accounting;
  4. if the goal is reached or exceeded, transition to SUCCESSFUL in that same transaction;
  5. emit the contribution event and, when applicable, a state-transition or goal-reached event.

Once the state is SUCCESSFUL, CLAIMED, FAILED, or REFUNDED, contribute() must revert. This removes the current path in which ETH can enter after claim() but can neither be claimed again nor refunded.

4. Separate lifetime activity from currently escrowed funds

The current total_raised variable is used as both a historical total and a current balance-like value, but the refund path decreases it while the successful claim path leaves it unchanged. That gives the same field different meanings in different terminal paths.

Use separate accounting concepts:

  • total_contributed: cumulative gross contributions; never decreases;
  • escrowed_amount: contributor funds currently held by the campaign;
  • contributions[address]: the contributor’s remaining refundable principal.

This makes the report, events, UI, and contract logic agree on what each number means.

The central accounting invariant is:

Every wei accepted through contribute() is always represented by exactly one valid exit path: claimable by the owner after success, or refundable to its contributor after failure. No terminal state accepts new value.

5. Restrict claim() to the successful state

claim() should require:

  • effective state is SUCCESSFUL;
  • caller is the recorded owner;
  • escrowed_amount > 0.

Apply checks-effects-interactions:

  1. capture the claimable amount;
  2. set escrowed_amount to zero;
  3. transition to CLAIMED;
  4. perform the ETH transfer;
  5. emit Claimed.

A failed ETH transfer should use a dedicated error such as TransferFailed, not GoalNotReached. Reusing an unrelated error makes debugging, teaching, and downstream interpretation incorrect.

The claim path must remain single-use, and is_active() must already be false before and after the claim.

6. Restrict refund() to the failed state

refund() should require:

  • effective state is FAILED;
  • the caller has a non-zero refundable principal.

Before transferring ETH:

  1. set the caller’s refundable principal to zero;
  2. decrement escrowed_amount;
  3. transfer the exact principal;
  4. emit Refunded;
  5. if escrowed_amount == 0, transition to REFUNDED.

A contributor must not be able to refund twice. Refunds must be unavailable in SUCCESSFUL and CLAIMED.

As with claim(), transfer failure should have a dedicated transfer error.

7. Derive all public views from the same lifecycle

The current is_active() checks only initialization and time, so it can report true after funds have been claimed.

The corrected views should follow the authoritative state:

  • status() returns the effective campaign state;
  • is_active() returns status == FUNDING;
  • goal_reached() returns true for SUCCESSFUL and CLAIMED;
  • claimed() can be removed or derived as status == CLAIMED;
  • total_contributed() and escrowed_amount() expose distinct accounting values.

The UI and demo scripts should consume status() rather than rebuilding lifecycle meaning from several separate calls.

8. Add negative-path and transition tests

The existing shell simulation covers only the successful happy path, so it cannot detect any of the three reported failures. The corrected example should include automated tests for at least the following cases:

  1. Deployment leaves no externally claimable initialization window.
  2. The intended owner is bound atomically and cannot be replaced.
  3. Zero goal, zero duration, and deadline overflow are rejected.
  4. A contribution at block.timestamp >= deadline is rejected.
  5. The contribution that reaches the goal transitions FUNDING -> SUCCESSFUL.
  6. is_active() becomes false when the campaign becomes successful.
  7. The owner can claim exactly once.
  8. A contribution after CLAIMED reverts and cannot change the contract balance or accounting.
  9. A failed campaign permits each contributor to refund exactly once.
  10. Refunds reduce escrowed_amount but do not rewrite total_contributed.
  11. A successful or claimed campaign never permits refunds.
  12. After every transition, every accepted contribution still has exactly one valid exit path.
  13. Terminal states reject every value-accepting operation.

demo_crowdfunding/simulate.sh should also contain two explicit scenarios rather than only one:

  • a successful campaign that reaches the goal and is claimed;
  • a failed campaign that passes the deadline and completes all refunds.

It should additionally attempt a post-claim contribution and verify that the call reverts and the contract remains in the same terminal state.

Scope of the correction

The files affected are not limited to one guard in contribute():

  • crowdfunding/src/lib.rs: lifecycle, initialization, accounting, errors, views, and transitions;
  • demo_crowdfunding/deploy.sh: atomic creation;
  • demo_crowdfunding/simulate.sh: positive and negative lifecycle coverage;
  • the public guide/README: the corrected state model and its invariants;
  • automated tests: transition and terminal-state acceptance criteria.

The three original observations therefore should not be closed as three unrelated patches. The correct contribution is the explicit lifecycle invariant above. Once that invariant is authoritative, the initialization race, post-claim locked funds, and false is_active() result are all removed by the same coherent model.

Please use or modify this specification directly. I do not need to be listed as a contributor; preserving the corrected reasoning in the educational material is the useful outcome.