One idea runs through both pages: data is shared across chains, behaviour sits behind traits. Bitcoin and Ethereum only diverge at the builder, signer and broadcaster implementations — never in the types they hand to each other.
The order the traits meet in, read off the right-hand column of both pages. Each stop is one trait.
Data types with no behaviour. Newtypes over String and int, so an address can never be passed where a transaction id belongs.
The notes start with separate BitcoinAddr and EthAddr, then collapse them into one type.
Mainnet = Chain { network_id: 0, name: "btc" }
from is struck out on the page — a UTXO transaction has no single sender.
The middle and right columns of page one, plus the top of page two. The star next to sign() is in the original: it is the one method every builder must have.
First written as TransactionUtxo, then renamed — the indexer normalises both chain shapes into the same Transaction.
Local is the qualifier that matters — it leaves room for a remote or HSM signer behind the same trait.
Generic over Utxo / Wallet. The Option is written in above a crossed-out hash() — there is no id until the tx is signed.
wait(0,1) in the notes — 0 for mempool acceptance, 1 for a first confirmation.
The whole difference between the chains lives here: Bitcoin builds from a list of inputs, Ethereum from a single signer.
Held as an array. Each input is signed separately, which is why the signer belongs to the input rather than the builder.
A first pass listing output, destination and keypair as loose fields is struck out and replaced by the Input struct above.
Transport is injected — the broadcaster takes a JSON-RPC client rather than building one.
One signer for the whole transaction — no input array, and no broadcast() listed on the builder.
keypair is crossed out and replaced by signer — the same correction made on the Bitcoin side.
from() survives only here, after being dropped from the shared Transaction.
The notes written out so they would compile. Return types are filled in where the page left them implicit.
// ---- primitives ---- pub struct Address(String); pub struct TransactionId(String); pub struct NetworkId(u32); pub struct Chain { network_id: NetworkId, name: String } pub struct Transaction { to: Address, amount: Decimal, id: TransactionId, } pub struct Keypair { address: Address, key: String } // ---- interfaces ---- pub trait Addresser { fn address(&self) -> Address; } pub trait Transactioner { fn transaction(&self) -> Transaction; } pub trait Wallet { fn address(&self) -> Address; fn keypair(&self) -> Keypair; } pub trait Signer { fn sign(&self, payload: &[u8]) -> Signature; } pub trait TxBuilder { fn destination(&self) -> Address; fn sign(self) -> SignedTx; // ★ the core method fn transaction_id(&self) -> Option<TransactionId>; // None until signed } pub trait TxBroadcaster { fn broadcast(&self, tx: SignedTx) -> TransactionId; fn wait(&self, id: &TransactionId, confirmations: u8); } // ---- implementations ---- pub struct BtcInput { signer: Box<dyn Signer>, index: u8 } impl BtcTxBuilder { pub fn new(inputs: Vec<BtcInput>, destination: Address) -> Self; } impl EthTxBuilder { pub fn new(signer: Box<dyn Signer>, destination: Address) -> Self; } impl LocalSigner { pub fn new(keypair: Keypair) -> Self; } impl BtcTxBroadcaster { pub fn new(rpc: JsonRpc) -> Self; }
Decisions already made in ink. Kept visible so they don't get re-litigated.
| Dropped | Replaced by | Why it holds up |
|---|---|---|
| — | A UTXO transaction has many senders; from() stays on EthTx only. | |
Transactioner | A shared trait shouldn't be named after one chain's model. | |
signer | Builders never hold a private key, only something that can sign. | |
transaction_id() | Returns a domain type, and an Option — an unsigned tx has no id yet. | |
Address | One type; validation happens in the chain layer. | |
| — | The trait was briefly chain-specific, then made generic. |
Where the notes stop, or where the handwriting doesn't resolve.
core (primitives + traits), chain-btc, chain-eth, indexer.broadcast() appears twice. Once on TxBuilder and once as the whole TxBroadcaster trait, and it is partly struck out on the builder. Cleaner to end the builder at sign().Chain? It's defined but never wired in. Wallet and the builder are both candidates.IndexEvent is only a heading above the BTC/ETH fork; the event type itself is missing.Decimal for amounts. Bitcoin and Ethereum both settle in integer base units (sat, wei); a decimal type usually belongs at the display edge, not in the primitive.broadcast and wait are network calls, so these traits will likely need to be async or return futures.