Skip to content

Commit 9301ce0

Browse files
Merge pull request #127 from diogosequeira94/main
cadi team challenge submission
2 parents a638a0a + 6a359a4 commit 9301ce0

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "cadi-team"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.1" }
8+
scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.1" }
9+
10+
[dev-dependencies]
11+
radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.1" }
12+
13+
[profile.release]
14+
opt-level = 's' # Optimize for size.
15+
lto = true # Enable Link Time Optimization.
16+
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
17+
panic = 'abort' # Abort on panic.
18+
strip = "debuginfo" # Strip debug info.
19+
20+
[lib]
21+
crate-type = ["cdylib", "lib"]
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
se scrypto::prelude::*;
2+
3+
blueprint! {
4+
struct CadiTeamEscrow {
5+
vaults: BTreeMap<ResourceSpecifier, Vault>,
6+
obligation_non_fungible_resource: ResourceAddress,
7+
is_escrow_fulfilled: bool
8+
}
9+
10+
impl CadiTeamEscrow {
11+
pub fn instantiate_escrow(
12+
to_be_paid_by_party_1: ResourceSpecifier,
13+
to_be_paid_by_party_2: ResourceSpecifier,
14+
) -> (ComponentAddress, Bucket) {
15+
assert!(
16+
to_be_paid_by_party_1.validate().is_ok(),
17+
"First resource is invalid"
18+
);
19+
assert!(
20+
to_be_paid_by_party_1.validate().is_ok(),
21+
"Second resource is invalid"
22+
);
23+
assert_ne!(
24+
to_be_paid_by_party_1, to_be_paid_by_party_2,
25+
"they are not equal"
26+
);
27+
28+
let party_1_obligation: EscrowObligation = EscrowObligation {
29+
amount_to_pay: to_be_paid_by_party_1.clone(),
30+
amount_to_get: to_be_paid_by_party_2.clone()
31+
};
32+
33+
let party_2_obligation: EscrowObligation = EscrowObligation {
34+
amount_to_pay: to_be_paid_by_party_2.clone(),
35+
amount_to_get: to_be_paid_by_party_1.clone()
36+
};
37+
38+
let escrow_obligations: Bucket = ResourceBuilder::new_non_fungible()
39+
.metadata("name", "Escrow Obligation")
40+
.metadata("symbol", "ESCROW")
41+
.metadata("description", "This resource describes the obligation of the two parties involved in the exchange")
42+
.metadata("team-member-1-github-username", "andrealupini")
43+
.metadata("team-member-2-github-username", "gitpck")
44+
.metadata("team-member-1-github-username", "diogosequeira94")
45+
.metadata("team-member-2-github-username", "ianmac2")
46+
.initial_supply([
47+
(
48+
NonFungibleId::from_u32(1),
49+
party_1_obligation
50+
),
51+
(
52+
NonFungibleId::from_u32(2),
53+
party_2_obligation
54+
),
55+
]);
56+
57+
58+
let mut vaults: BTreeMap<ResourceSpecifier, Vault> = BTreeMap::new();
59+
vaults.insert(
60+
to_be_paid_by_party_1.clone(),
61+
Vault::new(to_be_paid_by_party_1.resource_address())
62+
);
63+
vaults.insert(
64+
to_be_paid_by_party_2.clone(),
65+
Vault::new(to_be_paid_by_party_2.resource_address())
66+
);
67+
68+
let component_address: ComponentAddress = Self {
69+
vaults,
70+
obligation_non_fungible_resource: escrow_obligations.resource_address(),
71+
is_escrow_fulfilled: false
72+
}
73+
.instantiate()
74+
.globalize();
75+
76+
(component_address, escrow_obligations)
77+
78+
}
79+
80+
pub fn deposit(&mut self, obligation_badge: Proof, mut funds: Bucket) -> Bucket {
81+
/// First we need to make sure if the person passed the proper obligation badge (we have created)
82+
let obligation_badge: ValidatedProof = obligation_badge
83+
.validate_proof(self.obligation_non_fungible_resource)
84+
.expect("Invalid badge");
85+
/// After this lets get the data on the badge
86+
let obligation: EscrowObligation = obligation_badge.non_fungible().data();
87+
let vault: &mut Vault = self.vaults.get_mut(&obligation.amount_to_pay).unwrap();
88+
89+
let funds_to_deposit: Bucket = match obligation.amount_to_pay {
90+
ResourceSpecifier::Fungible { amount, .. } => funds.take(amount),
91+
ResourceSpecifier::NonFungible { non_fungible_ids, .. } => funds.take_non_fungibles(&non_fungible_ids),
92+
};
93+
94+
vault.put(funds_to_deposit);
95+
funds
96+
}
97+
98+
pub fn withdraw(&mut self, obligation_badge: Proof) -> Bucket {
99+
assert!(
100+
self.is_escrow_fulfilled(),
101+
"You can not withdraw your funds unless the escrow is not concluded",
102+
);
103+
104+
let obligation_badge: ValidatedProof = obligation_badge
105+
.validate_proof(self.obligation_non_fungible_resource)
106+
.expect("invalid badge provider");
107+
108+
let obligation: EscrowObligation = obligation_badge.non_fungible().data();
109+
let vault: &mut Vault = self.vaults.get_mut(&obligation.amount_to_get).unwrap();
110+
vault.take_all()
111+
}
112+
113+
pub fn is_escrow_fulfilled(&mut self) -> bool {
114+
if self.is_escrow_fulfilled {
115+
self.is_escrow_fulfilled
116+
} else {
117+
self.is_escrow_fulfilled = self.vaults
118+
.iter()
119+
.map(|(resource_specifier, vault)| {
120+
match resource_specifier {
121+
ResourceSpecifier::Fungible {
122+
resource_address,
123+
amount,
124+
} => {
125+
vault.resource_address() == *resource_address
126+
&& vault.amount() >= *amount
127+
}
128+
129+
ResourceSpecifier::NonFungible {
130+
resource_address,
131+
non_fungible_ids,
132+
} => {
133+
vault.resource_address() == *resource_address
134+
&& vault
135+
.non_fungible_ids()
136+
.iter()
137+
.all(|x| non_fungible_ids.contains(x))
138+
}
139+
}
140+
})
141+
.all(|x| x);
142+
self.is_escrow_fulfilled
143+
}
144+
145+
}
146+
}
147+
}
148+
149+
#[derive(Debug, NonFungibleData)]
150+
pub struct EscrowObligation {
151+
/// The amount of tokens which this party needs to pay to the other party.
152+
amount_to_pay: ResourceSpecifier,
153+
/// The amount of tokens paid by the other party to this party.
154+
amount_to_get: ResourceSpecifier,
155+
}
156+
157+
#[derive(Debug, TypeId, Encode, Decode, Describe, Ord, PartialOrd, Eq, PartialEq, Clone)]
158+
pub enum ResourceSpecifier {
159+
/// A variant used to specify the amount of a fungible resource through the [`ResourceAddress`]
160+
/// of the resource the amount of that resource as a [`Decimal`].
161+
Fungible {
162+
resource_address: ResourceAddress,
163+
amount: Decimal,
164+
},
165+
/// A variant used to specify non-fungible of that resource based on the [`ResourceAddress`] of
166+
/// the resource and a set of the [`NonFungibleId`]s being specified by the enum.
167+
NonFungible {
168+
resource_address: ResourceAddress,
169+
non_fungible_ids: BTreeSet<NonFungibleId>,
170+
},
171+
}
172+
173+
impl ResourceSpecifier {
174+
pub fn validate(&self) -> Result<(), ()> {
175+
match self {
176+
Self::Fungible { amount, .. } => {
177+
if *amount <= Decimal::zero() {
178+
Err(())
179+
} else {
180+
Ok(())
181+
}
182+
}
183+
Self::NonFungible {
184+
non_fungible_ids, ..
185+
} => {
186+
if non_fungible_ids.is_empty() {
187+
Err(())
188+
} else {
189+
Ok(())
190+
}
191+
}
192+
}
193+
}
194+
195+
pub fn resource_address(&self) -> ResourceAddress {
196+
match self {
197+
Self::Fungible {
198+
resource_address, ..
199+
}
200+
| Self::NonFungible {
201+
resource_address, ..
202+
} => *resource_address,
203+
}
204+
}
205+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use radix_engine::ledger::*;
2+
use radix_engine::transaction::*;
3+
use scrypto::prelude::*;
4+
5+
#[test]
6+
fn test_hello() {
7+
// Set up environment.
8+
let mut ledger = InMemorySubstateStore::with_bootstrap();
9+
let mut executor = TransactionExecutor::new(&mut ledger, false);
10+
let (pk, sk, account) = executor.new_account();
11+
let package = executor.publish_package(compile_package!()).unwrap();
12+
13+
// Test the `instantiate_hello` function.
14+
let transaction1 = TransactionBuilder::new()
15+
.call_function(package, "Hello", "instantiate_hello", args![])
16+
.build(executor.get_nonce([pk]))
17+
.sign([&sk]);
18+
let receipt1 = executor.validate_and_execute(&transaction1).unwrap();
19+
println!("{:?}\n", receipt1);
20+
assert!(receipt1.result.is_ok());
21+
22+
// Test the `free_token` method.
23+
let component = receipt1.new_component_addresses[0];
24+
let transaction2 = TransactionBuilder::new()
25+
.call_method(component, "free_token", args![])
26+
.call_method_with_all_resources(account, "deposit_batch")
27+
.build(executor.get_nonce([pk]))
28+
.sign([&sk]);
29+
let receipt2 = executor.validate_and_execute(&transaction2).unwrap();
30+
println!("{:?}\n", receipt2);
31+
assert!(receipt2.result.is_ok());
32+
}

0 commit comments

Comments
 (0)