Skip to content

feat(external-delegate-token-master): add pinocchio example - #715

Open
MarkFeder wants to merge 2 commits into
solana-foundation:mainfrom
MarkFeder:tokens-external-delegate-token-master-pinocchio
Open

feat(external-delegate-token-master): add pinocchio example#715
MarkFeder wants to merge 2 commits into
solana-foundation:mainfrom
MarkFeder:tokens-external-delegate-token-master-pinocchio

Conversation

@MarkFeder

Copy link
Copy Markdown
Contributor

Adds a Pinocchio implementation of external-delegate-token-master, alongside the existing Anchor one.

What it does

Lets an Ethereum key authorise Solana token transfers. Tokens sit in an account owned by a PDA of this program, and a secp256k1 signature over an on-chain-rebuilt digest is what moves them — no Solana signature from the token holder required.

Four instructions: Initialize, SetEthereumAddress, TransferTokens (Ethereum-signed), and AuthorityTransfer (the Solana escape hatch, so losing the Ethereum key does not strand the balance).

The crypto, and why it is hand-rolled

solana-keccak-hasher and solana-secp256k1-recover both link std, which collides with nostd_panic_handler! in a #![no_std] pinocchio program — error[E0152]: found duplicate lang item panic_impl. So crypto.rs declares the two syscalls directly with solana_define_syscall::define_syscall!, which is smaller and supplies a host-side stub so cargo clippy still compiles for the host target.

The subtle part, called out in a comment because it is the classic way to get this wrong: sol_secp256k1_recover returns the bare 64-byte X || Y, whereas most Ethereum tooling (@noble/curves included) returns the 65-byte 0x04-prefixed form. Keccak-hashing the wrong one yields a different address. The Rust side does not slice; the TypeScript side does.

Replay resistance

The digest commits to everything that decides where funds go — domain separator, program id, user account, both token accounts, amount — plus a nonce that is consumed before the transfer executes. So a signature cannot be replayed, redirected, or re-presented for a different amount. It is signed raw, with no EIP-191 prefix, so a wallet's default personal_sign output deliberately will not verify.

There are tests for each of those: replay, amount swap, and wrong signing key.

One addition over the Anchor version

A fresh account's Ethereum address is all zeroes. The Anchor version verifies against it as-is, so a signature that recovers to the zero address would be accepted on an account whose owner never opted in. This port rejects transfers until an address has been set (EthereumAddressUnset), with a test.

Dependencies

Adds solana-define-syscall to the workspace, and @noble/curves + @noble/hashes as dev dependencies for the test (the same libraries the Anchor test uses); pnpm-lock.yaml is regenerated and --frozen-lockfile verified.

Tests

10 LiteSVM tests covering both transfer paths and five rejection cases, exercising real secp256k1 recovery through the syscall. Verified locally: tsc --noEmit, pnpm test, prettier --check, cargo fmt --check, cargo clippy -D warnings.

@MarkFeder
MarkFeder requested a review from dev-jodee as a code owner August 31, 2026 13:37
@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a Pinocchio implementation of the external-delegate token-transfer example, including raw Keccak/secp256k1 syscall bindings, program state and instruction handlers, workspace integration, and LiteSVM coverage.

  • Implements initialization, Ethereum-address configuration, Ethereum-authorized transfers with nonce-based replay protection, and authority recovery transfers.
  • Uses a user-account-derived PDA to authorize SPL Token transfers.
  • Adds TypeScript tests for successful transfers and authorization, replay, amount-substitution, wrong-key, and unset-address rejection paths.
  • Adds the program to the Rust workspace and documents the new example.

Confidence Score: 5/5

The PR appears safe to merge with no concrete changed-code defect identified.

The new implementation consistently validates the stored Solana authority, derives and signs with the user-scoped PDA, binds Ethereum signatures to the complete transfer context and nonce, and relies on the SPL Token program’s account and mint invariants at the CPI boundary.

Important Files Changed

Filename Overview
tokens/external-delegate-token-master/pinocchio/program/src/crypto.rs Declares no-std-compatible Keccak and secp256k1 recovery syscalls and derives Ethereum addresses from the recovered bare public key.
tokens/external-delegate-token-master/pinocchio/program/src/instructions/transfer_tokens.rs Validates both authorization proofs, binds the signature to all transfer-critical fields and a nonce, and performs the PDA-authorized token transfer.
tokens/external-delegate-token-master/pinocchio/program/src/instructions/mod.rs Centralizes authority validation, PDA derivation, signer seeds, and the SPL Token transfer CPI.
tokens/external-delegate-token-master/pinocchio/program/src/instructions/initialize.rs Creates a rent-exempt 60-byte program-owned user account and initializes its authority and zeroed state.
tokens/external-delegate-token-master/pinocchio/program/src/state.rs Defines and bounds-checks the compact authority, Ethereum-address, and nonce account layout.
tokens/external-delegate-token-master/pinocchio/program/src/processor.rs Dispatches the four compact instruction formats and rejects unsupported discriminators.
tokens/external-delegate-token-master/pinocchio/tests/test.ts Exercises initialization, token custody, both transfer paths, nonce replay protection, signature binding, authority enforcement, and unset-address rejection.

Reviews (1): Last reviewed commit: "external-delegate-token-master: drop an ..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant