Solana India Fellowship - Week[3]
This post is a part of a series of posts documenting my experience as a fellow in the Solana India Fellowship. You can find other posts in this series here.
Week[3]
Introducing Aureus
Last week, I described my idea of building out a BNPL financing protocol on top of Solana pay. Well, the project now has a website and twitter. It is still pretty bare-bones and I am still figuring out the finer details, I will iterate on it till I find something which works well.
Payment channel
This week’s exercise focused on building a bi-directional payment channel on-chain.
The classic way of doing this on ETH requires signature verification on-chain. However, the Solana compute budget is much more limited and you cannot even verify a single ECDSA signature on-chain before running out of compute units.
// Mind the budget
ComputeBudget {
max_units: 200_000,
log_64_units: 100,
create_program_address_units: 1500,
invoke_units: 1000,
max_invoke_depth: 4,
sha256_base_cost: 85,
sha256_byte_cost: 1,
max_call_depth: 64,
stack_frame_size: 4_096,
log_pubkey_units: 100,
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
sysvar_base_cost: 100,
secp256k1_recover_cost: 25_000,
syscall_base_cost: 100,
heap_size: None,
heap_cost: 8,
}
Solana does signature verification on the transaction with accounts marked as isSigner
before the on-chain program
even runs. Thus, we can simply store the current balances on-chain, make Alice and Bob both sign when they want to
update these balances. Then, any single signer can close the channel when they want.
This kind of defeats the purpose of a payment channel, which is to avoid fees, but due of weird Solana quirks, there is no other obvious way of doing this. (Also a bi-directional payment channel doesn’t make much sense on Solana anyways, you can do 10,000 transactions on-chain and your total transaction fee will be around $2)
Some nonce shenanigans
Solana transactions do not have a nonce
field like ethereum, so to prevent replay attacks, each transaction has a
recent_blockchash
field which needs to be populated with a blockhash which is less than ~2min old. If the blockhash is
older than 2 minutes, the transaction will be rejected. This is a fine replacement for a nonce field, but leads to annoying
problems if you want to co-ordinate multiple offline signers who are geographically apart.
The solution to this is to use a durable nonce
,
which is an onchain program which replicates the Ethereum nonce semantics. A new on-chain account is initialized with
the pubkey of the user and a nonce value with the
InitializeNonceAccount
call on SystemProgram. This the only time I have used the recent_blockhashes
sysvar.
Then, you can create a transaction with the AdvanceNonceAccount
instruction before your actual instruction and you can
attach the nonce to this whole transaction.