在Solana中,智能合约称为program。
相关代码见:
- https://github.com/ironaddicteddog/anchor-escrow(Escrow program implemented in Anchor)
- https://github.com/paul-schaaf/solana-escrow
- https://github.com/mvines/solana-bpf-program-template
通过界面创建Solana SPL token有:
- Solana SPL TOKEN CREATOR UI
假设Alice有资产X,Bob有资产Y,二者想进行资产交换。 传统的方案为:引入Alice和Bob都信任的第三方Cathy。Cathy只有等收到 Alice发来的X资产 和 Bob发来的Y资产 之后,才会 将Y资产发给Alice 并 将X资产发给Bob。
可通过智能合约来替代可信第三方Cathy的角色。
Solana中的合约为无状态(Stateless)的,若想存储state,需借助accounts
参数:
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey, //当前执行的合约
accounts: &[AccountInfo], // The accounts referenced by an instruction represent on-chain state and serve as both the inputs and outputs of a program
instruction_data: &[u8], // 传给合约的参数
) -> ProgramResult {
Processor::process(program_id, accounts, instruction_data)
}
Solana合约本身存储在标记为executable
的account中。每个account可hold data and SOL。每个account有一个owner
,仅owner可debit the account and adjust its data。Crediting may be done by anyone。
理论上,合约对其拥有的账户拥有完全的自主权。由合约的创建者来限制这种自主权,由合约的用户来验证程序的创建者是否真的这样做了。 所有需要read或write的accounts都必须传入entrypoint function。因为runtime知道所有要读写的account,因此可支持交易并行执行。
Solana中的合约要点为:
- 每个合约都由其BPF Loader来处理。(合约存储在标记为
executable
的account中,这些executable
account owned by the BPF Loader。)不同的BPF loader可能需要不同的entrypoints。BPF loader本身也是program。【不归BPF Loader所有的合约有:BPF loader本身 以及 System Program。】 - accounts用于存储state。
- accounts归属于programs。
- 只有account owner才可debit an account and adjust its data。
- 所有读写accounts都需要传入entrypoint中。
- 不同于以太坊,Solana中同一用户main account账下不同token具有不同的token account。
Solana普通account属性有:
// 普通account属性
pub struct AccountInfo {
pub key: &'a Pubkey,
pub is_signer: bool,
pub is_writable: bool,
pub lamports: Rc,
pub data: Rc, // 仅用于存储user space information。
pub owner: &'a Pubkey,
pub executable: bool,
pub rent_epoch: Epoch,
}
前序博客有:
- solana token合约
回到Alice和Bob资产交换的场景,要求Alice和Bob必须有token X的account和token Y的account。
如何将Alice的main account与其多个token accounts进行关联呢? Solana token program采用的方案是,Alice对其所有token accounts共用一个private key,为每个token account分配owner。
注意token account的owner 与 普通account owner 的属性不同:
- 普通account owner 为Solana内部属性,通常普通account owner为a program。
- token account owner属性有token program在其user space中声明。
在token account的data中,包含了token account的各种属性:
- token account owner属性
- token account拥有的token数额等等。
// token account属性
pub struct Account {
/// The mint associated with this account
pub mint: Pubkey, // 表示该token account对应哪种token。
/// The owner of this account.
pub owner: Pubkey,
/// The amount of tokens this account holds.
pub amount: u64,
/// If `delegate` is `Some` then `delegated_amount` represents
/// the amount authorized by the delegate
pub delegate: COption,
/// The account's state
pub state: AccountState,
/// If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account
/// is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped
/// SOL accounts do not drop below this threshold.
pub is_native: COption,
/// The amount delegated
pub delegated_amount: u64,
/// Optional authority to close the account.
pub close_authority: COption,
}
一旦token account建立完毕,其private key将不再有任何用处,仅其token owner属性有用。 在本例中,token owner属性分别为Alice和Bob的main account。 当需要进行token transfer时,仅需要使用其main account的私钥对tx进行签名即可。
在3.1,通过将token account的owner设置为main account,实现了token account与main account的关联。 接下来,需要考虑的是Alice如何将token transfer 到 escrow合约?
详细可参看 Solana中的跨合约调用 及 Program Derived Addresses 第3节中的Program Derived Addresses(PDAs)技术。
4. 托管合约示例具体参见:
- https://github.com/paul-schaaf/solana-escrow/scripts【以此为例】
- https://github.com/paul-schaaf/escrow-ui
客户端1:
$ npm run setup-validator ../program/target/deploy/solana_escrow.so
客户端2:
$ npm run all
> escrow-scripts@1.0.0 all
> npm run compile && node build/setup.js && node build/alice.js && node build/bob.js
> escrow-scripts@1.0.0 compile
> tsc -p ./tsconfig.json
Requesting SOL for Alice...
Requesting SOL for Bob...
Requesting SOL for Client...
Creating Mint X...
Creating Alice TokenAccount for X...
Creating Bob TokenAccount for X...
Sending 50X to Alice's X TokenAccount...
Creating Mint Y...
Creating Alice TokenAccount for Y...
Creating Bob TokenAccount for Y...
Sending 50Y to Bob's Y TokenAccount...
✨Setup complete✨
┌─────────┬───────────────────────┬───────────────────────┬─────────────────────┬─────────────────────┐
│ (index) │ Alice Token Account X │ Alice Token Account Y │ Bob Token Account X │ Bob Token Account Y │
├─────────┼───────────────────────┼───────────────────────┼─────────────────────┼─────────────────────┤
│ 0 │ 50 │ 0 │ 0 │ 50 │
└─────────┴───────────────────────┴───────────────────────┴─────────────────────┴─────────────────────┘
Sending Alice's transaction...
✨Escrow successfully initialized. Alice is offering 5X for 3Y✨
┌─────────┬───────────────────────┬───────────────────────┬─────────────────────┬─────────────────────┬───────────────────────────┐
│ (index) │ Alice Token Account X │ Alice Token Account Y │ Bob Token Account X │ Bob Token Account Y │ Temporary Token Account X │
├─────────┼───────────────────────┼───────────────────────┼─────────────────────┼─────────────────────┼───────────────────────────┤
│ 0 │ 45 │ 0 │ 0 │ 50 │ 5 │
└─────────┴───────────────────────┴───────────────────────┴─────────────────────┴─────────────────────┴───────────────────────────┘
Sending Bob's transaction...
✨Trade successfully executed. All temporary accounts closed✨
┌─────────┬───────────────────────┬───────────────────────┬─────────────────────┬─────────────────────┐
│ (index) │ Alice Token Account X │ Alice Token Account Y │ Bob Token Account X │ Bob Token Account Y │
├─────────┼───────────────────────┼───────────────────────┼─────────────────────┼─────────────────────┤
│ 0 │ 45 │ 3 │ 5 │ 47 │
└─────────┴───────────────────────┴───────────────────────┴─────────────────────┴─────────────────────┘
参考资料
[1] Programming on Solana - An Introduction [2] Solana transactions