forked from radixdlt/scrypto-challenges
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Diogo Sequeira
committed
Oct 20, 2022
1 parent
a5a8eca
commit 8d03d50
Showing
3 changed files
with
258 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[package] | ||
name = "cadi-team" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.1" } | ||
scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.1" } | ||
|
||
[dev-dependencies] | ||
radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.4.1" } | ||
|
||
[profile.release] | ||
opt-level = 's' # Optimize for size. | ||
lto = true # Enable Link Time Optimization. | ||
codegen-units = 1 # Reduce number of codegen units to increase optimizations. | ||
panic = 'abort' # Abort on panic. | ||
strip = "debuginfo" # Strip debug info. | ||
|
||
[lib] | ||
crate-type = ["cdylib", "lib"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
se scrypto::prelude::*; | ||
|
||
blueprint! { | ||
struct CadiTeamEscrow { | ||
vaults: BTreeMap<ResourceSpecifier, Vault>, | ||
obligation_non_fungible_resource: ResourceAddress, | ||
is_escrow_fulfilled: bool | ||
} | ||
|
||
impl CadiTeamEscrow { | ||
pub fn instantiate_escrow( | ||
to_be_paid_by_party_1: ResourceSpecifier, | ||
to_be_paid_by_party_2: ResourceSpecifier, | ||
) -> (ComponentAddress, Bucket) { | ||
assert!( | ||
to_be_paid_by_party_1.validate().is_ok(), | ||
"First resource is invalid" | ||
); | ||
assert!( | ||
to_be_paid_by_party_1.validate().is_ok(), | ||
"Second resource is invalid" | ||
); | ||
assert_ne!( | ||
to_be_paid_by_party_1, to_be_paid_by_party_2, | ||
"they are not equal" | ||
); | ||
|
||
let party_1_obligation: EscrowObligation = EscrowObligation { | ||
amount_to_pay: to_be_paid_by_party_1.clone(), | ||
amount_to_get: to_be_paid_by_party_2.clone() | ||
}; | ||
|
||
let party_2_obligation: EscrowObligation = EscrowObligation { | ||
amount_to_pay: to_be_paid_by_party_2.clone(), | ||
amount_to_get: to_be_paid_by_party_1.clone() | ||
}; | ||
|
||
let escrow_obligations: Bucket = ResourceBuilder::new_non_fungible() | ||
.metadata("name", "Escrow Obligation") | ||
.metadata("symbol", "ESCROW") | ||
.metadata("description", "This resource describes the obligation of the two parties involved in the exchange") | ||
.metadata("team-member-1-github-username", "andrealupini") | ||
.metadata("team-member-2-github-username", "gitpck") | ||
.metadata("team-member-1-github-username", "diogosequeira94") | ||
.metadata("team-member-2-github-username", "ianmac2") | ||
.initial_supply([ | ||
( | ||
NonFungibleId::from_u32(1), | ||
party_1_obligation | ||
), | ||
( | ||
NonFungibleId::from_u32(2), | ||
party_2_obligation | ||
), | ||
]); | ||
|
||
|
||
let mut vaults: BTreeMap<ResourceSpecifier, Vault> = BTreeMap::new(); | ||
vaults.insert( | ||
to_be_paid_by_party_1.clone(), | ||
Vault::new(to_be_paid_by_party_1.resource_address()) | ||
); | ||
vaults.insert( | ||
to_be_paid_by_party_2.clone(), | ||
Vault::new(to_be_paid_by_party_2.resource_address()) | ||
); | ||
|
||
let component_address: ComponentAddress = Self { | ||
vaults, | ||
obligation_non_fungible_resource: escrow_obligations.resource_address(), | ||
is_escrow_fulfilled: false | ||
} | ||
.instantiate() | ||
.globalize(); | ||
|
||
(component_address, escrow_obligations) | ||
|
||
} | ||
|
||
pub fn deposit(&mut self, obligation_badge: Proof, mut funds: Bucket) -> Bucket { | ||
/// First we need to make sure if the person passed the proper obligation badge (we have created) | ||
let obligation_badge: ValidatedProof = obligation_badge | ||
.validate_proof(self.obligation_non_fungible_resource) | ||
.expect("Invalid badge"); | ||
/// After this lets get the data on the badge | ||
let obligation: EscrowObligation = obligation_badge.non_fungible().data(); | ||
let vault: &mut Vault = self.vaults.get_mut(&obligation.amount_to_pay).unwrap(); | ||
|
||
let funds_to_deposit: Bucket = match obligation.amount_to_pay { | ||
ResourceSpecifier::Fungible { amount, .. } => funds.take(amount), | ||
ResourceSpecifier::NonFungible { non_fungible_ids, .. } => funds.take_non_fungibles(&non_fungible_ids), | ||
}; | ||
|
||
vault.put(funds_to_deposit); | ||
funds | ||
} | ||
|
||
pub fn withdraw(&mut self, obligation_badge: Proof) -> Bucket { | ||
assert!( | ||
self.is_escrow_fulfilled(), | ||
"You can not withdraw your funds unless the escrow is not concluded", | ||
); | ||
|
||
let obligation_badge: ValidatedProof = obligation_badge | ||
.validate_proof(self.obligation_non_fungible_resource) | ||
.expect("invalid badge provider"); | ||
|
||
let obligation: EscrowObligation = obligation_badge.non_fungible().data(); | ||
let vault: &mut Vault = self.vaults.get_mut(&obligation.amount_to_get).unwrap(); | ||
vault.take_all() | ||
} | ||
|
||
pub fn is_escrow_fulfilled(&mut self) -> bool { | ||
if self.is_escrow_fulfilled { | ||
self.is_escrow_fulfilled | ||
} else { | ||
self.is_escrow_fulfilled = self.vaults | ||
.iter() | ||
.map(|(resource_specifier, vault)| { | ||
match resource_specifier { | ||
ResourceSpecifier::Fungible { | ||
resource_address, | ||
amount, | ||
} => { | ||
vault.resource_address() == *resource_address | ||
&& vault.amount() >= *amount | ||
} | ||
|
||
ResourceSpecifier::NonFungible { | ||
resource_address, | ||
non_fungible_ids, | ||
} => { | ||
vault.resource_address() == *resource_address | ||
&& vault | ||
.non_fungible_ids() | ||
.iter() | ||
.all(|x| non_fungible_ids.contains(x)) | ||
} | ||
} | ||
}) | ||
.all(|x| x); | ||
self.is_escrow_fulfilled | ||
} | ||
|
||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, NonFungibleData)] | ||
pub struct EscrowObligation { | ||
/// The amount of tokens which this party needs to pay to the other party. | ||
amount_to_pay: ResourceSpecifier, | ||
/// The amount of tokens paid by the other party to this party. | ||
amount_to_get: ResourceSpecifier, | ||
} | ||
|
||
#[derive(Debug, TypeId, Encode, Decode, Describe, Ord, PartialOrd, Eq, PartialEq, Clone)] | ||
pub enum ResourceSpecifier { | ||
/// A variant used to specify the amount of a fungible resource through the [`ResourceAddress`] | ||
/// of the resource the amount of that resource as a [`Decimal`]. | ||
Fungible { | ||
resource_address: ResourceAddress, | ||
amount: Decimal, | ||
}, | ||
/// A variant used to specify non-fungible of that resource based on the [`ResourceAddress`] of | ||
/// the resource and a set of the [`NonFungibleId`]s being specified by the enum. | ||
NonFungible { | ||
resource_address: ResourceAddress, | ||
non_fungible_ids: BTreeSet<NonFungibleId>, | ||
}, | ||
} | ||
|
||
impl ResourceSpecifier { | ||
pub fn validate(&self) -> Result<(), ()> { | ||
match self { | ||
Self::Fungible { amount, .. } => { | ||
if *amount <= Decimal::zero() { | ||
Err(()) | ||
} else { | ||
Ok(()) | ||
} | ||
} | ||
Self::NonFungible { | ||
non_fungible_ids, .. | ||
} => { | ||
if non_fungible_ids.is_empty() { | ||
Err(()) | ||
} else { | ||
Ok(()) | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn resource_address(&self) -> ResourceAddress { | ||
match self { | ||
Self::Fungible { | ||
resource_address, .. | ||
} | ||
| Self::NonFungible { | ||
resource_address, .. | ||
} => *resource_address, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
use radix_engine::ledger::*; | ||
use radix_engine::transaction::*; | ||
use scrypto::prelude::*; | ||
|
||
#[test] | ||
fn test_hello() { | ||
// Set up environment. | ||
let mut ledger = InMemorySubstateStore::with_bootstrap(); | ||
let mut executor = TransactionExecutor::new(&mut ledger, false); | ||
let (pk, sk, account) = executor.new_account(); | ||
let package = executor.publish_package(compile_package!()).unwrap(); | ||
|
||
// Test the `instantiate_hello` function. | ||
let transaction1 = TransactionBuilder::new() | ||
.call_function(package, "Hello", "instantiate_hello", args![]) | ||
.build(executor.get_nonce([pk])) | ||
.sign([&sk]); | ||
let receipt1 = executor.validate_and_execute(&transaction1).unwrap(); | ||
println!("{:?}\n", receipt1); | ||
assert!(receipt1.result.is_ok()); | ||
|
||
// Test the `free_token` method. | ||
let component = receipt1.new_component_addresses[0]; | ||
let transaction2 = TransactionBuilder::new() | ||
.call_method(component, "free_token", args![]) | ||
.call_method_with_all_resources(account, "deposit_batch") | ||
.build(executor.get_nonce([pk])) | ||
.sign([&sk]); | ||
let receipt2 = executor.validate_and_execute(&transaction2).unwrap(); | ||
println!("{:?}\n", receipt2); | ||
assert!(receipt2.result.is_ok()); | ||
} |