Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions contracts/launchpad/sources/redeem_random.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
module nft_protocol::redeem_random {
use std::vector;

use sui::transfer;
use sui::object::{Self, UID};
use sui::tx_context::{Self, TxContext};

use originmate::pseudorandom;

/// Attempted to construct a `RedeemCommitment` with a hash length
/// different than 32 bytes
const EInvalidCommitmentLength: u64 = 5;

/// Commitment in `RedeemCommitment` did not match original value committed
///
/// Call `warehouse::random_redeem_nft` with the correct commitment.
const EInvalidCommitment: u64 = 6;

/// Used for the client to commit a pseudo-random
struct RedeemCommitment has key, store {
/// `RedeemCommitment` ID
id: UID,
/// Hashed sender commitment
///
/// Sender will have to provide the pre-hashed value to be able to use
/// this `RedeemCommitment`. This value can be pseudo-random as long
/// as it is not predictable by the validator.
hashed_sender_commitment: vector<u8>,
/// Open commitment made by validator
contract_commitment: vector<u8>,
}

/// Create a new `RedeemCommitment`
///
/// Contract commitment must be unfeasible to predict by the transaction
/// sender. The underlying value of the commitment can be pseudo-random as
/// long as it is not predictable by the validator.
///
/// #### Panics
///
/// Panics if commitment is not 32 bytes.
public fun new_commitment(
hashed_sender_commitment: vector<u8>,
ctx: &mut TxContext,
): RedeemCommitment {
assert!(
vector::length(&hashed_sender_commitment) != 32,
EInvalidCommitmentLength,
);

RedeemCommitment {
id: object::new(ctx),
hashed_sender_commitment,
contract_commitment: pseudorandom::rand_with_ctx(ctx),
}
}

/// Creates a new `RedeemCommitment` and transfers it to the transaction
/// caller.
///
/// Contract commitment must be unfeasible to predict by the transaction
/// caller. The underlying value of the commitment can be pseudo-random as
/// long as it is not predictable by the validator.
///
/// #### Panics
///
/// Panics if commitment is not 32 bytes.
public entry fun init_commitment(
hashed_sender_commitment: vector<u8>,
ctx: &mut TxContext,
) {
let commitment = new_commitment(hashed_sender_commitment, ctx);
transfer::transfer(commitment, tx_context::sender(ctx));
}

/// Consumes `RedeemCommitment`
///
/// #### Panics
///
/// Panics if `user_commitment` does not match the hashed commitment in
/// `RedeemCommitment`.
public fun consume_commitment(
commitment: RedeemCommitment,
user_commitment: vector<u8>,
): (vector<u8>, vector<u8>) {
// Verify user commitment
let RedeemCommitment {
id,
hashed_sender_commitment,
contract_commitment
} = commitment;

object::delete(id);

let user_commitment = std::hash::sha3_256(user_commitment);
assert!(
user_commitment == hashed_sender_commitment,
EInvalidCommitment,
);

(hashed_sender_commitment, contract_commitment)
}

/// Deletes `RedeemCommitment`
public entry fun delete_commitment(commitment: RedeemCommitment) {
let RedeemCommitment {
id,
hashed_sender_commitment: _,
contract_commitment: _,
} = commitment;

object::delete(id);
}
}
3 changes: 0 additions & 3 deletions contracts/launchpad/sources/warehouse.move
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,6 @@ module launchpad::warehouse {
);

// Construct randomized index
let supply = supply(warehouse);
assert!(supply != 0, EEMPTY);

vector::append(&mut user_commitment, contract_commitment);
// Use supply of `Warehouse` as a additional nonce factor
vector::append(&mut user_commitment, sui::bcs::to_bytes(&supply));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module launchpad_v2::pseudorand_redeem {
use sui::dynamic_field as df;

use launchpad_v2::launchpad::LaunchCap;
use launchpad_v2::redeem_strategy;
use launchpad_v2::venue::{Self, Venue, RedeemReceipt, NftCert};

use originmate::pseudorandom;
Expand Down Expand Up @@ -97,7 +98,6 @@ module launchpad_v2::pseudorand_redeem {
let contract_commitment = pseudorandom::rand_no_counter(nonce, ctx);

let inv_index = select(rand_redeem.kiosk_precision, &contract_commitment);
let nft_rel_index = select(rand_redeem.nft_precision, &contract_commitment);

let (inv_id, inv_type) = get_inventory_data(venue, inv_index);

Expand All @@ -106,8 +106,7 @@ module launchpad_v2::pseudorand_redeem {
venue,
inv_type,
inv_id,
rand_redeem.nft_precision,
nft_rel_index,
redeem_strategy::pseudorandom(),
ctx,
)
}
Expand Down
158 changes: 158 additions & 0 deletions contracts/launchpad_v2/sources/config/redeem/strategy.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
module launchpad_v2::redeem_strategy {
use sui::object::{ID, UID};
use sui::dynamic_field as df;

use nft_protocol::redeem_random::RedeemCommitment;

/// Could not register redeem parameters as they already exist
const EConflictingParameters: u64 = 1;

/// Could not extract redeem parameters as they were not registered on the
/// object
const EInvalidParameters: u64 = 2;

struct RedeemStrategy has store, copy, drop {
/// Redeem strategy flag
///
/// Valid values:
///
/// 0 - Sequential
///
/// 1 - Pseudorandom
///
/// 2 - Random
///
/// 3 - ByIndex
///
/// 4 - ByID
///
/// Note that random, index, and ID strategies will registed
flag: u8,
}

public fun new(flag: u8): RedeemStrategy {
RedeemStrategy { flag }
}

/// Sequential redeem strategy
public fun sequential(): RedeemStrategy {
new(0)
}

/// Pseudorandom redeem strategy
public fun pseudorandom(): RedeemStrategy {
new(1)
}

/// Random redeem strategy
public fun random(): RedeemStrategy {
new(2)
}

/// Redeem strategy by NFT index
public fun by_index(): RedeemStrategy {
new(3)
}

/// Redeem strategy by NFT ID
public fun by_id(): RedeemStrategy {
new(4)
}

/// Return raw `RedeemStrategy` flag
public fun flag(strategy: &RedeemStrategy): &u8 {
&strategy.flag
}

/// Return whether strategy is sequential
public fun is_sequential(strategy: &RedeemStrategy): bool {
strategy.flag == 0
}

/// Return whether strategy is pseudorandom
public fun is_pseudorandom(strategy: &RedeemStrategy): bool {
strategy.flag == 1
}

/// Return whether strategy is random
public fun is_random(strategy: &RedeemStrategy): bool {
strategy.flag == 2
}

/// Return whether strategy is by index
public fun is_by_index(strategy: &RedeemStrategy): bool {
strategy.flag == 3
}

/// Return whether strategy is by ID
public fun is_by_id(strategy: &RedeemStrategy): bool {
strategy.flag == 4
}

struct ParametersKey has copy, drop, store {}

struct RandomCommitment has store {
commitment: RedeemCommitment,
user_commitment: vector<u8>,
}

public fun register_parameters_random(
object: &mut UID,
commitment: RedeemCommitment,
user_commitment: vector<u8>,
) {
add_parameters(
object, RandomCommitment { commitment, user_commitment },
)
}

public fun register_parameters_by_index(object: &mut UID, index: u64) {
add_parameters(object, index)
}

public fun register_parameters_by_id(object: &mut UID, id: ID) {
add_parameters(object, id)
}

public fun extract_parameters_random(
object: &mut UID,
): (RedeemCommitment, vector<u8>) {
let commitment: RandomCommitment = remove_parameters(object);
let RandomCommitment { commitment, user_commitment } = commitment;
(commitment, user_commitment)
}

public fun extract_parameters_by_index(object: &mut UID): u64 {
remove_parameters(object)
}

public fun extract_parameters_by_id(object: &mut UID): ID {
remove_parameters(object)
}

// === Helpers ===

public fun add_parameters<T: store>(object: &mut UID, parameters: T) {
assert_no_parameters<T>(object);
df::add(object, ParametersKey {}, parameters)
}

public fun remove_parameters<T: store>(object: &mut UID, ): T {
assert_parameters<T>(object);
df::remove(object, ParametersKey {})
}

public fun has_parameters<T: store>(object: &UID): bool {
df::exists_with_type<ParametersKey, T>(
object, ParametersKey {},
)
}

public fun assert_parameters<T: store>(object: &UID) {
assert!(has_parameters<T>(object), EInvalidParameters);
}

public fun assert_no_parameters<T: store>(object: &UID) {
assert!(!has_parameters<T>(object), EConflictingParameters);
}
}
8 changes: 1 addition & 7 deletions contracts/launchpad_v2/sources/inventory/factory.move
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ module launchpad_v2::factory {

use sui::object::{Self, UID};
use sui::tx_context::TxContext;
use sui::math;
use sui::bcs::{Self, BCS};

use nft_protocol::mint_pass::{Self, MintPass};
Expand Down Expand Up @@ -72,14 +71,9 @@ module launchpad_v2::factory {
venue::assert_cert_buyer(&certificate, ctx);
venue::assert_cert_inventory(&certificate, object::id(factory));

let index = math::divide_and_round_up(
factory.total_deposited * venue::cert_relative_index(&certificate),
venue::cert_index_scale(&certificate)
);

venue::consume_certificate(Witness {}, factory, certificate);

redeem_mint_pass_at_index<T>(factory, index, ctx)
redeem_mint_pass_at_index<T>(factory, 0, ctx)
}

/// Redeems NFT from specific index in `Warehouse`
Expand Down
Loading