Skip to content

Commit a638a0a

Browse files
Merge pull request #126 from J-Son89/main
Lisboa
2 parents 372d8ab + 1ec31fc commit a638a0a

File tree

5 files changed

+279
-0
lines changed

5 files changed

+279
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "escrow"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
8+
scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
9+
10+
[dev-dependencies]
11+
transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
12+
radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
13+
scrypto-unit = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.6.0" }
14+
15+
[profile.release]
16+
opt-level = 's' # Optimize for size.
17+
lto = true # Enable Link Time Optimization.
18+
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
19+
panic = 'abort' # Abort on panic.
20+
strip = "debuginfo" # Strip debug info.
21+
overflow-checks = true # Panic in the case of an overflow.
22+
23+
[lib]
24+
crate-type = ["cdylib", "lib"]
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
use scrypto::prelude::*;
2+
3+
blueprint! {
4+
struct Escrow {
5+
vaults: BTreeMap<ResourceSpecifier, Vault>,
6+
7+
/// verify that the NFT badges presented to the component match this [`ResourceAddress`].
8+
obligation_non_fungible_resource: ResourceAddress,
9+
is_escrow_fulfilled: bool
10+
}
11+
12+
impl Escrow {
13+
14+
pub fn instantiate_escrow(
15+
to_be_paid_by_party_1: ResourceSpecifier,
16+
to_be_paid_by_party_2: ResourceSpecifier,
17+
) -> (ComponentAddress, Bucket) {
18+
assert!(to_be_paid_by_party_1.validate().is_ok(),
19+
"first resource specifier is not valid");
20+
21+
assert!(to_be_paid_by_party_2.validate().is_ok(),
22+
"second resource specifier is not valid");
23+
24+
assert_ne!(to_be_paid_by_party_1,to_be_paid_by_party_2,"resources can not be the same");
25+
26+
let party_1_obligation: EscrowObligation = EscrowObligation {
27+
amount_to_pay: to_be_paid_by_party_1.clone(),
28+
amount_to_get: to_be_paid_by_party_2.clone()
29+
};
30+
31+
let party_2_obligation: EscrowObligation = EscrowObligation {
32+
amount_to_pay: to_be_paid_by_party_2.clone(),
33+
amount_to_get: to_be_paid_by_party_1.clone()
34+
};
35+
36+
let escrow_obligation: Bucket = ResourceBuilder::new_non_fungible()
37+
.metadata("name", "Escrow Obligation")
38+
.metadata("symbol", "ESCROW")
39+
.metadata("description", "obligation of two parties")
40+
.metadata("team-member-1-github-username", "lucas-r-oliveira")
41+
.metadata("team-member-2-github-username", "toastiixd")
42+
.metadata("team-member-3-github-username", "J-Son89")
43+
.metadata("team-member-4-github-username", "aschenkuttel")
44+
.initial_supply([
45+
(
46+
NonFungibleId:: from_u32(1),
47+
party_1_obligation
48+
),
49+
(
50+
NonFungibleId:: from_u32(2),
51+
party_2_obligation
52+
),
53+
]);
54+
55+
let mut vaults: BTreeMap<ResourceSpecifier, Vault> = BTreeMap:: new();
56+
vaults.insert (
57+
to_be_paid_by_party_1.clone(),
58+
Vault::new(to_be_paid_by_party_1.resource_address())
59+
);
60+
vaults.insert(
61+
to_be_paid_by_party_2.clone(),
62+
Vault::new(to_be_paid_by_party_2.resource_address())
63+
);
64+
65+
let component_address: ComponentAddress = Self {
66+
vaults,
67+
obligation_non_fungible_resource: escrow_obligation.resource_address(),
68+
is_escrow_fulfilled: false
69+
}
70+
.instantiate()
71+
.globalize();
72+
73+
(component_address, escrow_obligation)
74+
}
75+
76+
77+
pub fn deposit(&mut self, obligation_badge: Proof, mut funds: Bucket) -> (Bucket, Option<Bucket> ){
78+
let obligation_badge: ValidatedProof = obligation_badge
79+
.validate_proof(self.obligation_non_fungible_resource)
80+
.expect("Invalid badge provided");
81+
82+
let obligation: EscrowObligation = obligation_badge.non_fungible().data();
83+
let vault: &mut Vault = self.vaults.get_mut(&obligation.amount_to_pay).unwrap();
84+
85+
let funds_to_deposit: Bucket = match obligation.amount_to_pay {
86+
ResourceSpecifier::Fungible { amount, .. } => funds.take(amount),
87+
ResourceSpecifier::NonFungible {non_fungible_ids, ..} => funds.take_non_fungibles(&non_fungible_ids)
88+
};
89+
90+
vault.put(funds_to_deposit);
91+
92+
if self.is_escrow_fulfilled() {
93+
let obligation: EscrowObligation = obligation_badge.non_fungible().data();
94+
95+
let vault: &mut Vault = self.vaults.get_mut(&obligation.amount_to_get).unwrap();
96+
97+
return (funds, Some(vault.take_all()));
98+
}
99+
100+
(funds, None)
101+
}
102+
103+
104+
pub fn withdraw(&mut self, obligation_badge: Proof) -> Bucket {
105+
assert!(self.is_escrow_fulfilled(),"escrow not fulfilled");
106+
107+
108+
let obligation_badge: ValidatedProof = obligation_badge
109+
.validate_proof(self.obligation_non_fungible_resource)
110+
.expect("Invalid badge provided");
111+
112+
let obligation: EscrowObligation = obligation_badge.non_fungible().data();
113+
114+
let vault: &mut Vault = self.vaults.get_mut(&obligation.amount_to_get).unwrap();
115+
116+
return vault.take_all();
117+
}
118+
119+
120+
pub fn is_escrow_fulfilled(&mut self) -> bool {
121+
if self.is_escrow_fulfilled {
122+
self.is_escrow_fulfilled
123+
} else {
124+
self.is_escrow_fulfilled = self.vaults
125+
.iter()
126+
.map(|(resource_specifier, vault)| {
127+
match resource_specifier {
128+
// If this is a fungible resource specifier, then check that the resource
129+
// address and the amount both match.
130+
ResourceSpecifier::Fungible {
131+
resource_address,
132+
amount,
133+
} => {
134+
vault.resource_address() == *resource_address
135+
&& vault.amount() >= *amount
136+
}
137+
138+
// If this is a non-fungible resource specifier then check that the resource
139+
// address matches and that the set of non-fungible ids in the specifier is
140+
// a subset of those in the vault.
141+
ResourceSpecifier::NonFungible {
142+
resource_address,
143+
non_fungible_ids,
144+
} => {
145+
vault.resource_address() == *resource_address
146+
&& vault
147+
.non_fungible_ids()
148+
.iter()
149+
.all(|x| non_fungible_ids.contains(x))
150+
}
151+
}
152+
})
153+
.all(|x| x);
154+
self.is_escrow_fulfilled
155+
}
156+
157+
}
158+
}
159+
}
160+
161+
#[derive(Debug, NonFungibleData)]
162+
pub struct EscrowObligation {
163+
/// The amount of tokens which this party needs to pay to the other party.
164+
amount_to_pay: ResourceSpecifier,
165+
/// The amount of tokens paid by the other party to this party.
166+
amount_to_get: ResourceSpecifier,
167+
}
168+
169+
170+
#[derive(Debug, TypeId, Encode, Decode, Describe, Ord, PartialOrd, Eq, PartialEq, Clone)]
171+
pub enum ResourceSpecifier {
172+
/// A variant used to specify the amount of a fungible resource through the [`ResourceAddress`]
173+
/// of the resource the amount of that resource as a [`Decimal`].
174+
Fungible {
175+
resource_address: ResourceAddress,
176+
amount: Decimal,
177+
},
178+
/// A variant used to specify non-fungible of that resource based on the [`ResourceAddress`] of
179+
/// the resource and a set of the [`NonFungibleId`]s being specified by the enum.
180+
NonFungible {
181+
resource_address: ResourceAddress,
182+
non_fungible_ids: BTreeSet<NonFungibleId>,
183+
},
184+
}
185+
186+
impl ResourceSpecifier {
187+
188+
pub fn validate(&self) -> Result<(), ()> {
189+
match self {
190+
Self::Fungible { amount, .. } => {
191+
if *amount <= Decimal::zero() {
192+
Err(())
193+
} else {
194+
Ok(())
195+
}
196+
}
197+
Self::NonFungible {
198+
non_fungible_ids, ..
199+
} => {
200+
if non_fungible_ids.is_empty() {
201+
Err(())
202+
} else {
203+
Ok(())
204+
}
205+
}
206+
}
207+
}
208+
209+
/// Gets the resource address of the specified resource.
210+
///
211+
/// # Returns
212+
///
213+
/// [`ResourceAddress`] - The resource address of the specified resource.
214+
pub fn resource_address(&self) -> ResourceAddress {
215+
match self {
216+
Self::Fungible {
217+
resource_address, ..
218+
}
219+
| Self::NonFungible {
220+
resource_address, ..
221+
} => *resource_address,
222+
}
223+
}
224+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CALL_METHOD
2+
ComponentAddress("${account1}")
3+
"lock_fee"
4+
Decimal("100");
5+
6+
CALL_FUNCTION
7+
PackageAddress("${package}")
8+
"Escrow"
9+
"instantiate_escrow"
10+
Enum("Fungible", ResourceAddress("${token1}"), Decimal("20"))
11+
Enum("Fungible", ResourceAddress("${token2}"), Decimal("300"));
12+
13+
CALL_METHOD
14+
ComponentAddress("${account1}")
15+
"deposit_batch"
16+
Expression("ENTIRE_WORKTOP");
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CALL_METHOD
2+
ComponentAddress("${account1}")
3+
"lock_fee"
4+
Decimal("100");
5+
6+
CALL_METHOD
7+
ComponentAddress("${account1}")
8+
"withdraw_by_ids"
9+
Set<NonFungibleId>(NonFungibleId("0902000000"))
10+
ResourceAddress("${obligation_nft}");
11+
12+
CALL_METHOD
13+
ComponentAddress("${account2}")
14+
"deposit_batch"
15+
Expression("ENTIRE_WORKTOP");

0 commit comments

Comments
 (0)