Skip to main content

lichen_core/
contract_instruction.rs

1// Lichen Smart Contract Instructions
2// Deploy and invoke WASM contracts
3
4use serde::{Deserialize, Serialize};
5
6/// Smart contract instruction types
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
8pub enum ContractInstruction {
9    /// Deploy new contract
10    /// Accounts: [deployer (signer, writable), contract (writable)]
11    /// Data: WASM bytecode
12    Deploy {
13        /// WASM bytecode
14        code: Vec<u8>,
15        /// Initial storage data (optional)
16        init_data: Vec<u8>,
17    },
18
19    /// Call contract function
20    /// Accounts: [caller (signer, writable), contract (writable), ...]
21    /// Data: function name + arguments
22    Call {
23        /// Function name to call
24        function: String,
25        /// Function arguments (serialized)
26        args: Vec<u8>,
27        /// Value to transfer (in spores)
28        value: u64,
29    },
30
31    /// Upgrade contract (only owner can upgrade)
32    /// Accounts: [owner (signer), contract (writable)]
33    /// Data: new WASM bytecode
34    Upgrade {
35        /// New WASM bytecode
36        code: Vec<u8>,
37    },
38
39    /// Close contract and withdraw remaining balance
40    /// Accounts: [owner (signer), contract (writable), destination (writable)]
41    Close,
42
43    /// Set or update the upgrade timelock for a contract.
44    /// Once set, upgrades are staged for N epochs before execution.
45    /// Setting to 0 removes the timelock (instant upgrades again).
46    /// Accounts: [owner (signer), contract (writable)]
47    SetUpgradeTimelock {
48        /// Number of epochs to delay between submission and execution.
49        /// 0 removes the timelock.
50        epochs: u32,
51    },
52
53    /// Execute a previously staged upgrade after the timelock has expired.
54    /// Accounts: [owner (signer), contract (writable)]
55    ExecuteUpgrade,
56
57    /// Veto (cancel) a pending upgrade. Only the governance authority can veto.
58    /// Accounts: [governance_authority (signer), contract (writable)]
59    VetoUpgrade,
60}
61
62impl ContractInstruction {
63    /// Serialize instruction to bytes
64    pub fn serialize(&self) -> Result<Vec<u8>, String> {
65        serde_json::to_vec(self).map_err(|e| e.to_string())
66    }
67
68    /// Deserialize instruction from bytes
69    pub fn deserialize(data: &[u8]) -> Result<Self, String> {
70        serde_json::from_slice(data).map_err(|e| e.to_string())
71    }
72
73    /// Create deploy instruction
74    pub fn deploy(code: Vec<u8>, init_data: Vec<u8>) -> Self {
75        ContractInstruction::Deploy { code, init_data }
76    }
77
78    /// Create call instruction
79    pub fn call(function: String, args: Vec<u8>, value: u64) -> Self {
80        ContractInstruction::Call {
81            function,
82            args,
83            value,
84        }
85    }
86
87    /// Create upgrade instruction
88    pub fn upgrade(code: Vec<u8>) -> Self {
89        ContractInstruction::Upgrade { code }
90    }
91
92    /// Create close instruction
93    pub fn close() -> Self {
94        ContractInstruction::Close
95    }
96
97    /// Create set upgrade timelock instruction
98    pub fn set_upgrade_timelock(epochs: u32) -> Self {
99        ContractInstruction::SetUpgradeTimelock { epochs }
100    }
101
102    /// Create execute upgrade instruction
103    pub fn execute_upgrade() -> Self {
104        ContractInstruction::ExecuteUpgrade
105    }
106
107    /// Create veto upgrade instruction
108    pub fn veto_upgrade() -> Self {
109        ContractInstruction::VetoUpgrade
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_deploy_instruction() {
119        let code = vec![0x00, 0x61, 0x73, 0x6d]; // WASM magic
120        let init_data = vec![1, 2, 3];
121
122        let instr = ContractInstruction::deploy(code.clone(), init_data.clone());
123
124        match instr {
125            ContractInstruction::Deploy {
126                code: c,
127                init_data: d,
128            } => {
129                assert_eq!(c, code);
130                assert_eq!(d, init_data);
131            }
132            _ => panic!("Wrong instruction type"),
133        }
134    }
135
136    #[test]
137    fn test_call_instruction() {
138        let instr = ContractInstruction::call("transfer".to_string(), vec![1, 2, 3, 4], 1000);
139
140        match instr {
141            ContractInstruction::Call { function, .. } => {
142                assert_eq!(function, "transfer");
143            }
144            _ => panic!("Wrong instruction type"),
145        }
146    }
147
148    #[test]
149    fn test_serialization() {
150        let instr = ContractInstruction::call("test".to_string(), vec![1, 2, 3], 0);
151
152        let serialized = instr.serialize().unwrap();
153        let deserialized = ContractInstruction::deserialize(&serialized).unwrap();
154
155        assert_eq!(instr, deserialized);
156    }
157}