forked from benelabs/crucible
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
159 lines (145 loc) · 4.95 KB
/
Copy pathlib.rs
File metadata and controls
159 lines (145 loc) · 4.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#![no_std]
#![allow(deprecated)]
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, token, Address, Env};
/// Current state of the escrow.
#[contracttype]
#[derive(Clone, PartialEq, Debug)]
pub enum EscrowStatus {
Pending,
Approved,
Claimed,
Refunded,
}
/// All escrow data stored in instance storage under a single key.
#[contracttype]
#[derive(Clone)]
pub struct EscrowState {
pub depositor: Address,
pub recipient: Address,
pub arbiter: Address,
pub token: Address,
pub amount: i128,
/// Unix timestamp after which the recipient may claim without arbiter approval.
pub unlock_time: u64,
pub status: EscrowStatus,
}
#[contracttype]
enum DataKey {
State,
}
/// A two-party escrow contract with a time lock and arbiter.
///
/// Workflow:
/// 1. Depositor calls `create`, funding the contract with tokens.
/// 2. Either:
/// - The arbiter calls `approve` to release funds early, then the recipient calls `claim`, OR
/// - Time passes until `unlock_time`, after which the recipient may `claim` directly, OR
/// - The depositor calls `refund` after `unlock_time` if the recipient never claimed.
#[contract]
#[derive(Default)]
pub struct Escrow;
#[contractimpl]
impl Escrow {
/// Initialise the escrow, transferring `amount` tokens from `depositor`
/// into this contract.
pub fn create(
env: Env,
depositor: Address,
recipient: Address,
arbiter: Address,
token: Address,
amount: i128,
unlock_time: u64,
) {
if env.storage().instance().has(&DataKey::State) {
panic!("escrow already exists");
}
if amount <= 0 {
panic!("amount must be positive");
}
depositor.require_auth();
// Pull tokens from depositor into this contract.
token::Client::new(&env, &token).transfer(
&depositor,
env.current_contract_address(),
&amount,
);
env.storage().instance().set(
&DataKey::State,
&EscrowState {
depositor,
recipient,
arbiter,
token,
amount,
unlock_time,
status: EscrowStatus::Pending,
},
);
env.events().publish((symbol_short!("created"),), amount);
}
/// Arbiter approves an early release to the recipient.
pub fn approve(env: Env, caller: Address) {
let mut state: EscrowState = env.storage().instance().get(&DataKey::State).unwrap();
if state.status != EscrowStatus::Pending {
panic!("escrow is not pending");
}
if caller != state.arbiter {
panic!("only the arbiter can approve");
}
caller.require_auth();
state.status = EscrowStatus::Approved;
env.storage().instance().set(&DataKey::State, &state);
env.events().publish((symbol_short!("approved"),), ());
}
/// Recipient claims the escrowed funds.
///
/// Requires either arbiter approval or that `unlock_time` has passed.
pub fn claim(env: Env) {
let mut state: EscrowState = env.storage().instance().get(&DataKey::State).unwrap();
if state.status != EscrowStatus::Pending && state.status != EscrowStatus::Approved {
panic!("escrow already settled");
}
let now = env.ledger().timestamp();
if state.status != EscrowStatus::Approved && now < state.unlock_time {
panic!("time lock has not expired");
}
state.recipient.require_auth();
state.status = EscrowStatus::Claimed;
env.storage().instance().set(&DataKey::State, &state);
token::Client::new(&env, &state.token).transfer(
&env.current_contract_address(),
&state.recipient,
&state.amount,
);
env.events()
.publish((symbol_short!("claimed"),), state.amount);
}
/// Depositor reclaims funds after the time lock expires (if unclaimed).
pub fn refund(env: Env) {
let mut state: EscrowState = env.storage().instance().get(&DataKey::State).unwrap();
if state.status != EscrowStatus::Pending {
panic!("can only refund a pending escrow");
}
let now = env.ledger().timestamp();
if now < state.unlock_time {
panic!("time lock has not expired");
}
state.depositor.require_auth();
state.status = EscrowStatus::Refunded;
env.storage().instance().set(&DataKey::State, &state);
token::Client::new(&env, &state.token).transfer(
&env.current_contract_address(),
&state.depositor,
&state.amount,
);
env.events()
.publish((symbol_short!("refunded"),), state.amount);
}
/// Return the current escrow state.
pub fn get_state(env: Env) -> EscrowState {
env.storage().instance().get(&DataKey::State).unwrap()
}
}
#[cfg(test)]
mod test;