ido better error reporting
This commit is contained in:
parent
f8ddac6ec3
commit
a944db4702
1 changed files with 424 additions and 235 deletions
|
|
@ -17,6 +17,7 @@ use std::sync::LazyLock;
|
||||||
use std::collections::{HashMap};
|
use std::collections::{HashMap};
|
||||||
use sqlx::{Row, SqlitePool};
|
use sqlx::{Row, SqlitePool};
|
||||||
use crate::db::blob::{ToBlob, blob_to_display_hex};
|
use crate::db::blob::{ToBlob, blob_to_display_hex};
|
||||||
|
use anyhow::Error;
|
||||||
|
|
||||||
const BCMR_SIGNATURE: &[u8] = &[
|
const BCMR_SIGNATURE: &[u8] = &[
|
||||||
0x6a, 0x04, 0x42, 0x43, 0x4d, 0x52, 0x20,
|
0x6a, 0x04, 0x42, 0x43, 0x4d, 0x52, 0x20,
|
||||||
|
|
@ -50,6 +51,7 @@ static MIN_PLP_SHARE: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(20_000_00
|
||||||
static MAX_PLP_SHARE: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(80_000_000i64)); // 80%
|
static MAX_PLP_SHARE: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(80_000_000i64)); // 80%
|
||||||
static MAX_PLP_ANNUAL_DISCOUNT: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(10_000_000i64)); // 10%
|
static MAX_PLP_ANNUAL_DISCOUNT: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(10_000_000i64)); // 10%
|
||||||
static MIN_PLP_ANNUAL_DISCOUNT: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(0i64)); // 0%
|
static MIN_PLP_ANNUAL_DISCOUNT: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(0i64)); // 0%
|
||||||
|
static MIN_PLP_AFTER_DISCOUNT: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(5_000_000i64)); // 5%
|
||||||
|
|
||||||
static ENTRY_EXECUTION_FEE: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(10000i64)); // 10k sats
|
static ENTRY_EXECUTION_FEE: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(10000i64)); // 10k sats
|
||||||
static IDO_CREATE_EXECUTION_FEE: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(30000i64)); // 30k sats
|
static IDO_CREATE_EXECUTION_FEE: LazyLock<BigInt> = LazyLock::new(|| BigInt::from(30000i64)); // 30k sats
|
||||||
|
|
@ -280,14 +282,17 @@ fn build_storage_script(governingOutpointIndex: u32) -> Script {
|
||||||
Script::from(bytes)
|
Script::from(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_storage_script_with_data(governingOutpointIndex: u32, data: &[u8]) -> Script {
|
fn build_storage_script_with_data_and_size(governingOutpointIndex: u32, data: &[u8]) -> Script {
|
||||||
|
let mut data_and_size = Vec::new();
|
||||||
|
data_and_size.extend_from_slice(data);
|
||||||
|
data_and_size.extend_from_slice(&encode_padded_vm_number(&BigInt::from(data.len()), 2).unwrap());
|
||||||
let mut bytes = Builder::new()
|
let mut bytes = Builder::new()
|
||||||
.push_slice(&bigint_to_vm_number(&BigInt::from(governingOutpointIndex)))
|
.push_slice(&bigint_to_vm_number(&BigInt::from(governingOutpointIndex)))
|
||||||
.into_script()
|
.into_script()
|
||||||
.to_bytes();
|
.to_bytes();
|
||||||
bytes.extend_from_slice(&STORAGE_CONTRACT);
|
bytes.extend_from_slice(&STORAGE_CONTRACT);
|
||||||
let suffix = Builder::new()
|
let suffix = Builder::new()
|
||||||
.push_slice(data)
|
.push_slice(&data_and_size)
|
||||||
.push_opcode(opcodes::all::OP_DROP)
|
.push_opcode(opcodes::all::OP_DROP)
|
||||||
.into_script()
|
.into_script()
|
||||||
.to_bytes();
|
.to_bytes();
|
||||||
|
|
@ -865,7 +870,7 @@ fn is_preinit_broadcast(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ipfs_bcmr_with_placeholder(
|
fn deserialize_ipfs_bcmr_with_placeholder(
|
||||||
bcmr_data: &[u8],
|
bcmr_data: &[u8],
|
||||||
) -> Result<Option<IpfsBcmrWithPlaceholder>> {
|
) -> Result<Option<IpfsBcmrWithPlaceholder>> {
|
||||||
if bcmr_data.len() == 0 ||
|
if bcmr_data.len() == 0 ||
|
||||||
|
|
@ -910,6 +915,35 @@ fn parse_ipfs_bcmr_with_placeholder(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize_ipfs_bcmr_with_placeholder(
|
||||||
|
input: &Option<IpfsBcmrWithPlaceholder>,
|
||||||
|
) -> Vec<u8> {
|
||||||
|
match input {
|
||||||
|
Some(value) => {
|
||||||
|
let metadata_bytecode = Builder::new()
|
||||||
|
.push_slice(&value.metadata.registryReplaceCalls)
|
||||||
|
.push_slice(&value.metadata.bcmrUrisReplaceCalls)
|
||||||
|
.into_script()
|
||||||
|
.to_bytes();
|
||||||
|
let mut bcmr_bytecode = Vec::new();
|
||||||
|
bcmr_bytecode.extend_from_slice(&BCMR_SIGNATURE);
|
||||||
|
bcmr_bytecode.extend_from_slice(&value.contentHash);
|
||||||
|
bcmr_bytecode.extend_from_slice(&value.urisBytecode);
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
bytes.extend_from_slice(&metadata_bytecode);
|
||||||
|
bytes.extend_from_slice(&bcmr_bytecode);
|
||||||
|
bytes
|
||||||
|
},
|
||||||
|
None => Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_ipfs_bcmr_with_placeholder_from_p2s_storage(script: &Script) -> Result<Option<IpfsBcmrWithPlaceholder>> {
|
||||||
|
deserialize_ipfs_bcmr_with_placeholder(
|
||||||
|
&extract_data_from_storage_script_with_data_and_size(script)?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
struct IdoContext {
|
struct IdoContext {
|
||||||
preinit_txid: Vec<u8>,
|
preinit_txid: Vec<u8>,
|
||||||
init_txid: Option<Vec<u8>>,
|
init_txid: Option<Vec<u8>>,
|
||||||
|
|
@ -923,22 +957,20 @@ struct IdoContext {
|
||||||
offering_token_id: Option<Vec<u8>>,
|
offering_token_id: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_ido_context_from_preinit(network: Option<Network>, tx: &Transaction) -> Result<IdoContext> {
|
struct IdoPreinitParseParamsResult {
|
||||||
let platform_fee_nfth = match network {
|
preinit_parameters: IdoPreInitParameters,
|
||||||
Some(Network::Chipnet) => CHIPNET_PLATFORM_FEE_NFTH.clone(),
|
offered_token_is_in_supply: bool,
|
||||||
Some(Network::Bitcoin) => MAINNET_PLATFORM_FEE_NFTH.clone(),
|
is_valid_ido: bool,
|
||||||
_ => MAINNET_PLATFORM_FEE_NFTH.clone(),
|
}
|
||||||
};
|
|
||||||
let offered_token_is_in_supply;
|
fn parse_ido_preinit_tx_params(tx: &Transaction, platform_fee_nfth: &[u8], errors: &mut Vec<Error>, invalid_ido_reasons: &mut Vec<Error>) -> Option<IdoPreinitParseParamsResult> {
|
||||||
let mut is_valid_ido = true;
|
|
||||||
// verify the ido is created correctly
|
|
||||||
let preinit_parameters: IdoPreInitParameters;
|
|
||||||
// preinit announcement
|
// preinit announcement
|
||||||
let annOut = tx.output.get(0); // preinit params
|
let annOut = tx.output.get(0); // preinit params
|
||||||
if annOut.is_some() {
|
if annOut.is_some() {
|
||||||
let instructions: Vec<_> = annOut.unwrap().script_pubkey.instructions().collect();
|
let instructions: Vec<_> = annOut.unwrap().script_pubkey.instructions().collect();
|
||||||
if instructions.len() != 3 {
|
if instructions.len() != 3 {
|
||||||
return Err(anyhow::anyhow!("should have 3 opcodes"));
|
errors.push(anyhow::anyhow!("should have 3 opcodes"));
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
// authguard locking bytecode
|
// authguard locking bytecode
|
||||||
let authguardLockingBytecode;
|
let authguardLockingBytecode;
|
||||||
|
|
@ -947,43 +979,51 @@ async fn create_ido_context_from_preinit(network: Option<Network>, tx: &Transact
|
||||||
authguardLockingBytecode = data.to_vec();
|
authguardLockingBytecode = data.to_vec();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow::anyhow!("invalid announcement (3)"));
|
errors.push(anyhow::anyhow!("invalid announcement (3)"));
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// params main
|
// params main
|
||||||
match &instructions[1] {
|
match &instructions[1] {
|
||||||
Ok(Instruction::PushBytes(data)) => {
|
Ok(Instruction::PushBytes(data)) => {
|
||||||
if data.len() < 185 {
|
if data.len() < 185 {
|
||||||
return Err(anyhow::anyhow!("Incorrect announcement.mainData size"));
|
errors.push(anyhow::anyhow!("Incorrect announcement.mainData size"));
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
let mut is_valid_ido = true;
|
||||||
// preinit_parameters.offeredTokenTotalSupply < BigInt::from(0)
|
// preinit_parameters.offeredTokenTotalSupply < BigInt::from(0)
|
||||||
offered_token_is_in_supply = vm_number_to_bigint(&data[168..176]) < BigInt::from(0);
|
let offered_token_is_in_supply = vm_number_to_bigint(&data[168..176]) < BigInt::from(0);
|
||||||
|
|
||||||
let offeringBcmrStorageOut = tx.output.get(8);
|
let offeringBcmrStorageOut = tx.output.get(8);
|
||||||
if offeringBcmrStorageOut.is_none() {
|
if offeringBcmrStorageOut.is_none() {
|
||||||
return Err(anyhow::anyhow!("offering bcmr storage not defined!"));
|
errors.push(anyhow::anyhow!("offering bcmr storage not defined!"));
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
let offeringBcmr = parse_ipfs_bcmr_with_placeholder(
|
|
||||||
&extract_data_from_storage_script_with_data_and_size(
|
let offeringBcmrResult = deserialize_ipfs_bcmr_with_placeholder_from_p2s_storage(&offeringBcmrStorageOut.unwrap().script_pubkey);
|
||||||
&offeringBcmrStorageOut.unwrap().script_pubkey
|
if offeringBcmrResult.is_err() {
|
||||||
)?
|
errors.push(anyhow::anyhow!("offering bcmr deserialize failed, {}", offeringBcmrResult.err().unwrap()));
|
||||||
)?;
|
return None;
|
||||||
|
}
|
||||||
|
let offeringBcmr = offeringBcmrResult.unwrap();
|
||||||
|
|
||||||
let mut oTokenBcmr: Option<IpfsBcmrWithPlaceholder> = None;
|
let mut oTokenBcmr: Option<IpfsBcmrWithPlaceholder> = None;
|
||||||
if offered_token_is_in_supply {
|
if offered_token_is_in_supply {
|
||||||
let oTokenBcmrStorageOut = tx.output.get(9);
|
let oTokenBcmrStorageOut = tx.output.get(9);
|
||||||
if oTokenBcmrStorageOut.is_none() {
|
if oTokenBcmrStorageOut.is_none() {
|
||||||
return Err(anyhow::anyhow!("oToken bcmr storage not defined!"));
|
errors.push(anyhow::anyhow!("oToken bcmr storage not defined!"));
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let oTokenBcmrResult = deserialize_ipfs_bcmr_with_placeholder_from_p2s_storage(&oTokenBcmrStorageOut.unwrap().script_pubkey);
|
||||||
|
if oTokenBcmrResult.is_err() {
|
||||||
|
errors.push(anyhow::anyhow!("oToken bcmr deserialize failed, {}", oTokenBcmrResult.err().unwrap()));
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
oTokenBcmr = oTokenBcmrResult.unwrap();
|
||||||
}
|
}
|
||||||
oTokenBcmr = parse_ipfs_bcmr_with_placeholder(
|
|
||||||
&extract_data_from_storage_script_with_data_and_size(
|
|
||||||
&oTokenBcmrStorageOut.unwrap().script_pubkey
|
|
||||||
)?
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preinit_parameters = IdoPreInitParameters {
|
let preinit_parameters = IdoPreInitParameters {
|
||||||
preInitBcmr: IdoPreInitBcmrParameters {
|
preInitBcmr: IdoPreInitBcmrParameters {
|
||||||
offering: offeringBcmr,
|
offering: offeringBcmr,
|
||||||
oToken: oTokenBcmr,
|
oToken: oTokenBcmr,
|
||||||
|
|
@ -1019,230 +1059,362 @@ async fn create_ido_context_from_preinit(network: Option<Network>, tx: &Transact
|
||||||
|
|
||||||
// enforce limitation (xToken == NATIVE_BCH)
|
// enforce limitation (xToken == NATIVE_BCH)
|
||||||
if preinit_parameters.offering.offer.xTokenCategory.is_some() {
|
if preinit_parameters.offering.offer.xTokenCategory.is_some() {
|
||||||
return Err(anyhow::anyhow!("non-null xTokenCategory is not supported"));
|
errors.push(anyhow::anyhow!("non-null xTokenCategory is not supported"));
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
if !offered_token_is_in_supply {
|
if !offered_token_is_in_supply {
|
||||||
let permanentLiquidityOTokenReserve: BigInt = &preinit_parameters.offeredTokenAmount * &preinit_parameters.permanentLiquidityShareNumerator / &PERMANENT_LIQUIDITY_SHARE_DENOMINATOR.clone();
|
let permanentLiquidityOTokenReserve: BigInt = &preinit_parameters.offeredTokenAmount * &preinit_parameters.permanentLiquidityShareNumerator / &PERMANENT_LIQUIDITY_SHARE_DENOMINATOR.clone();
|
||||||
if preinit_parameters.offeredTokenTotalSupply <= &preinit_parameters.offeredTokenAmount + &permanentLiquidityOTokenReserve {
|
if preinit_parameters.offeredTokenTotalSupply <= &preinit_parameters.offeredTokenAmount + &permanentLiquidityOTokenReserve {
|
||||||
info!("ido parse, not a valid ido, preinit_parameters.offeredTokenTotalSupply <= preinit_parameters.offeredTokenAmount + permanentLiquidityOTokenReserve");
|
invalid_ido_reasons.push(anyhow::anyhow!("ido parse, not a valid ido, preinit_parameters.offeredTokenTotalSupply <= preinit_parameters.offeredTokenAmount + permanentLiquidityOTokenReserve"));
|
||||||
is_valid_ido = false;
|
is_valid_ido = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if preinit_parameters.permanentLiquidityShareNumerator < *MIN_PLP_SHARE ||
|
if preinit_parameters.permanentLiquidityShareNumerator < *MIN_PLP_SHARE {
|
||||||
preinit_parameters.permanentLiquidityShareNumerator > *MAX_PLP_SHARE ||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.permanentLiquidityShareNumerator < *MIN_PLP_SHARE"));
|
||||||
preinit_parameters.offering.offer.discountAnnualRateNumerator < *MIN_PLP_ANNUAL_DISCOUNT ||
|
is_valid_ido = false;
|
||||||
preinit_parameters.offering.offer.discountAnnualRateNumerator > *MAX_PLP_ANNUAL_DISCOUNT ||
|
}
|
||||||
preinit_parameters.offering.offer.maxDiscountRateNumerator !=
|
|
||||||
preinit_parameters.permanentLiquidityShareNumerator ||
|
if preinit_parameters.permanentLiquidityShareNumerator > *MAX_PLP_SHARE {
|
||||||
preinit_parameters.offeredTokenAmount <= BigInt::from(0) ||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.permanentLiquidityShareNumerator > *MAX_PLP_SHARE"));
|
||||||
preinit_parameters.offering.delphiCategory != DELPHI_TOKEN_ID ||
|
is_valid_ido = false;
|
||||||
preinit_parameters.offering.platformFeeNFTH != platform_fee_nfth ||
|
}
|
||||||
preinit_parameters.offering.platformFeeNumerator < *PLATFORM_FEE ||
|
if preinit_parameters.offering.offer.discountAnnualRateNumerator < *MIN_PLP_ANNUAL_DISCOUNT {
|
||||||
preinit_parameters.offering.executionFee < *ENTRY_EXECUTION_FEE {
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.offer.discountAnnualRateNumerator > *MAX_PLP_ANNUAL_DISCOUNT"));
|
||||||
is_valid_ido = false;
|
is_valid_ido = false;
|
||||||
}
|
}
|
||||||
|
if preinit_parameters.offering.offer.discountAnnualRateNumerator > *MAX_PLP_ANNUAL_DISCOUNT {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.offer.discountAnnualRateNumerator > *MAX_PLP_ANNUAL_DISCOUNT"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if preinit_parameters.offering.offer.maxDiscountRateNumerator < BigInt::from(0) {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.offer.maxDiscountRateNumerator < BigInt::from(0)"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
if &preinit_parameters.offering.offer.maxDiscountRateNumerator + &MIN_PLP_AFTER_DISCOUNT.clone() >
|
||||||
|
preinit_parameters.permanentLiquidityShareNumerator {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.offer.maxDiscountRateNumerator + MIN_PLP_AFTER_DISCOUNT > preinit_parameters.permanentLiquidityShareNumerator"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
if preinit_parameters.offeredTokenAmount <= BigInt::from(0) {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offeredTokenAmount <= BigInt::from(0)"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
if preinit_parameters.offering.delphiCategory != DELPHI_TOKEN_ID {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.delphiCategory != DELPHI_TOKEN_ID"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
if &preinit_parameters.offering.platformFeeNFTH != platform_fee_nfth {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.platformFeeNFTH != platform_fee_nfth"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
if preinit_parameters.offering.platformFeeNumerator < *PLATFORM_FEE {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.platformFeeNumerator < *PLATFORM_FEE"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
if preinit_parameters.offering.executionFee < *ENTRY_EXECUTION_FEE {
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("preinit_parameters.offering.executionFee < *ENTRY_EXECUTION_FEE"));
|
||||||
|
is_valid_ido = false;
|
||||||
|
}
|
||||||
|
return Some(IdoPreinitParseParamsResult {
|
||||||
|
preinit_parameters,
|
||||||
|
offered_token_is_in_supply,
|
||||||
|
is_valid_ido,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow::anyhow!("invalid announcement (2)"));
|
errors.push(anyhow::anyhow!("invalid announcement (2)"));
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!("announcement output is missing"));
|
errors.push(anyhow::anyhow!("announcement output is missing"));
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IdoPreinitParseResult {
|
||||||
|
parameters: Option<IdoPreInitParameters>,
|
||||||
|
state: Option<IdoPreInitState>,
|
||||||
|
is_valid_ido: bool,
|
||||||
|
offered_token_is_in_supply: bool,
|
||||||
|
offered_token_id: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ido_preinit_tx(network: Option<Network>, tx: &Transaction, errors: &mut Vec<Error>, invalid_ido_reasons: &mut Vec<Error>) -> IdoPreinitParseResult {
|
||||||
|
let platform_fee_nfth = match network {
|
||||||
|
Some(Network::Chipnet) => CHIPNET_PLATFORM_FEE_NFTH.clone(),
|
||||||
|
Some(Network::Bitcoin) => MAINNET_PLATFORM_FEE_NFTH.clone(),
|
||||||
|
_ => MAINNET_PLATFORM_FEE_NFTH.clone(),
|
||||||
|
};
|
||||||
|
let offered_token_is_in_supply: bool;
|
||||||
|
let mut is_valid_ido: bool;
|
||||||
|
let nullable_preinit_parameters: Option<IdoPreInitParameters>;
|
||||||
|
// preinit announcement
|
||||||
|
match parse_ido_preinit_tx_params(tx, &platform_fee_nfth, errors, invalid_ido_reasons) {
|
||||||
|
Some(output) => {
|
||||||
|
offered_token_is_in_supply = output.offered_token_is_in_supply;
|
||||||
|
is_valid_ido = output.is_valid_ido;
|
||||||
|
nullable_preinit_parameters = Some(output.preinit_parameters);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
offered_token_is_in_supply = false;
|
||||||
|
is_valid_ido = false;
|
||||||
|
nullable_preinit_parameters = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let preInitOut = tx.output.get(1);
|
let mut authguardNftOut = None;
|
||||||
if preInitOut.is_none() {
|
|
||||||
return Err(anyhow::anyhow!("preinit output is not defined!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// find authguardCategory
|
// find authguardCategory
|
||||||
let mut authguardNftOut = None;
|
if let Some(params) = nullable_preinit_parameters {
|
||||||
if preinit_parameters.authguardNftOutputIndex >= BigInt::from(0) {
|
match params.authguardNftOutputIndex.to_usize() {
|
||||||
authguardNftOut = tx.output.get(preinit_parameters.authguardNftOutputIndex.to_usize().ok_or_else(|| anyhow::anyhow!("authguardNftOutputIndex out of range"))?);
|
Some(index) => {
|
||||||
}
|
authguardNftOut = tx.output.get(index);
|
||||||
|
if authguardNftOut.is_none() {
|
||||||
let preInit_lock_parameters = IdoPreInitLockParameters {
|
errors.push(anyhow::anyhow!("authguardNftOutputIndex out of range"));
|
||||||
preInitBcmr: preinit_parameters.preInitBcmr.clone(),
|
}
|
||||||
authguardLockingBytecode: preinit_parameters.authguardLockingBytecode.clone(),
|
},
|
||||||
permanentLiquidityShareNumerator: preinit_parameters.permanentLiquidityShareNumerator.clone(),
|
_ => errors.push(anyhow::anyhow!("authguardNftOutputIndex out of range")),
|
||||||
offeredTokenAmount: preinit_parameters.offeredTokenAmount.clone(),
|
|
||||||
offeredTokenTotalSupply: preinit_parameters.offeredTokenTotalSupply.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let preinit_state = IdoPreInitState {
|
|
||||||
authguardCategory: if authguardNftOut.is_some() && authguardNftOut.unwrap().token.is_some() { authguardNftOut.unwrap().token.as_ref().unwrap().id.to_blob() } else { vec![0; 32] },
|
|
||||||
idoCategory: vec![0; 32],
|
|
||||||
nextTxSetterFlag: BigInt::from(0),
|
|
||||||
oTokenGenerated: if offered_token_is_in_supply { BigInt::from(1) } else { BigInt::from(0) },
|
|
||||||
};
|
|
||||||
if build_p2sh32_script(
|
|
||||||
&build_preinit_bytecode(&preInit_lock_parameters, &preinit_state)
|
|
||||||
) != preInitOut.unwrap().script_pubkey {
|
|
||||||
return Err(anyhow::anyhow!("preInit output bytecode does not match!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let offeringBytecodeStorageOut = tx.output.get(2);
|
|
||||||
if offeringBytecodeStorageOut.is_none() {
|
|
||||||
return Err(anyhow::anyhow!("offering bytecode storage not defined!"));
|
|
||||||
}
|
|
||||||
if build_p2sh32_script(
|
|
||||||
&build_storage_script_with_data(
|
|
||||||
1,
|
|
||||||
&build_offering_bytecode(
|
|
||||||
&preinit_parameters.offering.offer.maxDiscountRateNumerator,
|
|
||||||
&preinit_parameters.offering.offer.discountAnnualRateNumerator,
|
|
||||||
&preinit_parameters.offering.offer.priceNumerator,
|
|
||||||
&preinit_parameters.offering.offer.minOffer,
|
|
||||||
preinit_parameters.offering.offer.xTokenCategory.as_deref().unwrap_or(&[]),
|
|
||||||
)
|
|
||||||
).to_bytes()
|
|
||||||
) != offeringBytecodeStorageOut.unwrap().script_pubkey {
|
|
||||||
return Err(anyhow::anyhow!("offering bytecode storage does not match!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let launcherBytecodeStorageOut = tx.output.get(3);
|
|
||||||
if launcherBytecodeStorageOut.is_none() {
|
|
||||||
return Err(anyhow::anyhow!("launcher bytecode storage not defined!"));
|
|
||||||
}
|
|
||||||
if build_p2sh32_script(
|
|
||||||
&build_storage_script_with_data(
|
|
||||||
1,
|
|
||||||
&build_launcher_bytecode(
|
|
||||||
&vec![0u8; 32], // collectorNFTH
|
|
||||||
&preinit_parameters.offering.platformFeeNFTH,
|
|
||||||
&preinit_parameters.offering.platformFeeNumerator,
|
|
||||||
&preinit_parameters.offering.delphiCategory,
|
|
||||||
&preinit_parameters.offering.launchConditions.expiresAt,
|
|
||||||
&preinit_parameters.offering.launchConditions.deployThreshold,
|
|
||||||
&preinit_parameters.offering.launchConditions.immediateDeployThreshold,
|
|
||||||
)
|
|
||||||
).to_bytes()
|
|
||||||
) != launcherBytecodeStorageOut.unwrap().script_pubkey {
|
|
||||||
return Err(anyhow::anyhow!("launcher bytecode storage does not match!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let distDeployBytecodeStorageOut = tx.output.get(4);
|
|
||||||
if distDeployBytecodeStorageOut.is_none() {
|
|
||||||
return Err(anyhow::anyhow!("dist deploy bytecode storage not defined!"));
|
|
||||||
}
|
|
||||||
if build_p2sh32_script(
|
|
||||||
&build_storage_script_with_data(
|
|
||||||
1,
|
|
||||||
&DISTRIBUTOR_DEPLOY_CONTRACT,
|
|
||||||
).to_bytes()
|
|
||||||
) != distDeployBytecodeStorageOut.unwrap().script_pubkey {
|
|
||||||
return Err(anyhow::anyhow!("dist deploy bytecode storage does not match!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let distRefundBytecodeStorageOut = tx.output.get(5);
|
|
||||||
if distRefundBytecodeStorageOut.is_none() {
|
|
||||||
return Err(anyhow::anyhow!("dist refund bytecode storage not defined!"));
|
|
||||||
}
|
|
||||||
if build_p2sh32_script(
|
|
||||||
&build_storage_script_with_data(
|
|
||||||
1,
|
|
||||||
&DISTRIBUTOR_REFUND_CONTRACT,
|
|
||||||
).to_bytes()
|
|
||||||
) != distRefundBytecodeStorageOut.unwrap().script_pubkey {
|
|
||||||
return Err(anyhow::anyhow!("dist refund bytecode storage does not match!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let offeringInitiatorBytecodeStorageOut = tx.output.get(6);
|
|
||||||
if offeringInitiatorBytecodeStorageOut.is_none() {
|
|
||||||
return Err(anyhow::anyhow!("offering initiator bytecode storage not defined!"));
|
|
||||||
}
|
|
||||||
if build_p2sh32_script(
|
|
||||||
&build_storage_script_with_data(
|
|
||||||
1,
|
|
||||||
&build_partial_offering_initiator_bytecode(
|
|
||||||
&BigInt::from(7i64), // extensionOutpointIndex
|
|
||||||
&BigInt::from(6i64), // tokenStorageOutpointIndex
|
|
||||||
&BigInt::from(5i64), // offeringBcmrStorageOutpointIndex
|
|
||||||
&BigInt::from(4i64), // distRefundOutpointIndex
|
|
||||||
&BigInt::from(3i64), // distDeployOutpointIndex
|
|
||||||
&BigInt::from(2i64), // launcherBytecodeOutpointIndex
|
|
||||||
&BigInt::from(1i64), // offeringBytecodeOutpointIndex
|
|
||||||
&preinit_parameters.offering.executionFee,
|
|
||||||
),
|
|
||||||
).to_bytes()
|
|
||||||
) != offeringInitiatorBytecodeStorageOut.unwrap().script_pubkey {
|
|
||||||
return Err(anyhow::anyhow!("offering initiator bytecode storage does not match!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let idoInitiatorBytecodeStorageOut = tx.output.get(7);
|
|
||||||
if idoInitiatorBytecodeStorageOut.is_none() {
|
|
||||||
return Err(anyhow::anyhow!("ido initiator bytecode storage not defined!"));
|
|
||||||
}
|
|
||||||
if build_p2sh32_script(
|
|
||||||
&build_storage_script_with_data(
|
|
||||||
1,
|
|
||||||
&build_partial_ido_initiator_bytecode(
|
|
||||||
&build_partial_postlaunch_bytecode(
|
|
||||||
&preinit_parameters.permanentLiquidityShareNumerator,
|
|
||||||
&preinit_parameters.offering.offer.priceNumerator,
|
|
||||||
),
|
|
||||||
&BigInt::from(7i64), // permanentPoolOTokenReserveOutpointIndex
|
|
||||||
&BigInt::from(0i64), // offeringInitiatorOutpointIndex
|
|
||||||
),
|
|
||||||
).to_bytes()
|
|
||||||
) != idoInitiatorBytecodeStorageOut.unwrap().script_pubkey {
|
|
||||||
return Err(anyhow::anyhow!("ido initiator bytecode storage does not match!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip validating offering & oToken bcmr
|
|
||||||
|
|
||||||
let mut offered_token_id: Option<Vec<u8>> = None;
|
|
||||||
|
|
||||||
if offered_token_is_in_supply {
|
|
||||||
// verify offered token storage
|
|
||||||
let offeredTokenStorageOut = tx.output.get(9);
|
|
||||||
if offeredTokenStorageOut.is_none() {
|
|
||||||
// valid = false;
|
|
||||||
return Err(anyhow::anyhow!("offered token supply not found!"));
|
|
||||||
}
|
}
|
||||||
let permanentLiquidityOTokenReserve: BigInt = &preinit_parameters.offeredTokenAmount * &preinit_parameters.permanentLiquidityShareNumerator / &PERMANENT_LIQUIDITY_SHARE_DENOMINATOR.clone();
|
|
||||||
let requiredTokens = &preinit_parameters.offeredTokenAmount + permanentLiquidityOTokenReserve;
|
let state = IdoPreInitState {
|
||||||
if offeredTokenStorageOut.unwrap().token.is_none() {
|
authguardCategory: if authguardNftOut.is_some() && authguardNftOut.unwrap().token.is_some() { authguardNftOut.unwrap().token.as_ref().unwrap().id.to_blob() } else { vec![0; 32] },
|
||||||
return Err(anyhow::anyhow!("offered token storage does not contain a token!"));
|
idoCategory: vec![0; 32],
|
||||||
}
|
nextTxSetterFlag: BigInt::from(0),
|
||||||
if build_p2sh32_script(&build_storage_script(1u32).to_bytes()) !=
|
oTokenGenerated: if offered_token_is_in_supply { BigInt::from(1) } else { BigInt::from(0) },
|
||||||
offeredTokenStorageOut.unwrap().script_pubkey {
|
};
|
||||||
return Err(anyhow::anyhow!("offered token locking bytecode does not match!"));
|
let preInitOut = tx.output.get(1);
|
||||||
|
if preInitOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("preinit output is not defined!"));
|
||||||
|
} else {
|
||||||
|
let preInit_lock_parameters = IdoPreInitLockParameters {
|
||||||
|
preInitBcmr: params.preInitBcmr.clone(),
|
||||||
|
authguardLockingBytecode: params.authguardLockingBytecode.clone(),
|
||||||
|
permanentLiquidityShareNumerator: params.permanentLiquidityShareNumerator.clone(),
|
||||||
|
offeredTokenAmount: params.offeredTokenAmount.clone(),
|
||||||
|
offeredTokenTotalSupply: params.offeredTokenTotalSupply.clone(),
|
||||||
|
};
|
||||||
|
let bytecode = build_preinit_bytecode(&preInit_lock_parameters, &state);
|
||||||
|
if build_p2sh32_script(&bytecode) != preInitOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("preInit output bytecode does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
}
|
}
|
||||||
let token_amount = BigInt::from(offeredTokenStorageOut.unwrap().token.as_ref().unwrap().amount);
|
|
||||||
if token_amount < requiredTokens {
|
|
||||||
return Err(anyhow::anyhow!("invalid offered token amount!"));
|
|
||||||
}
|
}
|
||||||
offered_token_id = Some(offeredTokenStorageOut.unwrap().token.as_ref().unwrap().id.to_blob());
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect preinit fee
|
|
||||||
let mut preinit_paid_fee: u64 = 0;
|
let offeringBytecodeStorageOut = tx.output.get(2);
|
||||||
for output in &tx.output {
|
if offeringBytecodeStorageOut.is_none() {
|
||||||
if build_p2sh32_script(
|
errors.push(anyhow::anyhow!("offering bytecode storage not defined!"));
|
||||||
&build_p2nfth_script(&platform_fee_nfth).to_bytes()
|
} else {
|
||||||
) == output.script_pubkey {
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
preinit_paid_fee += output.value;
|
1,
|
||||||
|
&build_offering_bytecode(
|
||||||
|
¶ms.offering.offer.maxDiscountRateNumerator,
|
||||||
|
¶ms.offering.offer.discountAnnualRateNumerator,
|
||||||
|
¶ms.offering.offer.priceNumerator,
|
||||||
|
¶ms.offering.offer.minOffer,
|
||||||
|
params.offering.offer.xTokenCategory.as_deref().unwrap_or(&[]),
|
||||||
|
)
|
||||||
|
).to_bytes();
|
||||||
|
if build_p2sh32_script(&bytecode) != offeringBytecodeStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("offering bytecode storage does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let launcherBytecodeStorageOut = tx.output.get(3);
|
||||||
|
if launcherBytecodeStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("launcher bytecode storage not defined!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
|
1,
|
||||||
|
&build_launcher_bytecode(
|
||||||
|
&vec![0u8; 32], // collectorNFTH
|
||||||
|
¶ms.offering.platformFeeNFTH,
|
||||||
|
¶ms.offering.platformFeeNumerator,
|
||||||
|
¶ms.offering.delphiCategory,
|
||||||
|
¶ms.offering.launchConditions.expiresAt,
|
||||||
|
¶ms.offering.launchConditions.deployThreshold,
|
||||||
|
¶ms.offering.launchConditions.immediateDeployThreshold,
|
||||||
|
)
|
||||||
|
).to_bytes();
|
||||||
|
if build_p2sh32_script(&bytecode) != launcherBytecodeStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("launcher bytecode storage does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let distDeployBytecodeStorageOut = tx.output.get(4);
|
||||||
|
if distDeployBytecodeStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("dist deploy bytecode storage not defined!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
|
1,
|
||||||
|
&DISTRIBUTOR_DEPLOY_CONTRACT,
|
||||||
|
).to_bytes();
|
||||||
|
if build_p2sh32_script(&bytecode) != distDeployBytecodeStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("dist deploy bytecode storage does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let distRefundBytecodeStorageOut = tx.output.get(5);
|
||||||
|
if distRefundBytecodeStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("dist refund bytecode storage not defined!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
|
1,
|
||||||
|
&DISTRIBUTOR_REFUND_CONTRACT,
|
||||||
|
).to_bytes();
|
||||||
|
if build_p2sh32_script(&bytecode) != distRefundBytecodeStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("dist refund bytecode storage does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let offeringInitiatorBytecodeStorageOut = tx.output.get(6);
|
||||||
|
if offeringInitiatorBytecodeStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("offering initiator bytecode storage not defined!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
|
1,
|
||||||
|
&build_partial_offering_initiator_bytecode(
|
||||||
|
&BigInt::from(7i64), // extensionOutpointIndex
|
||||||
|
&BigInt::from(6i64), // tokenStorageOutpointIndex
|
||||||
|
&BigInt::from(5i64), // offeringBcmrStorageOutpointIndex
|
||||||
|
&BigInt::from(4i64), // distRefundOutpointIndex
|
||||||
|
&BigInt::from(3i64), // distDeployOutpointIndex
|
||||||
|
&BigInt::from(2i64), // launcherBytecodeOutpointIndex
|
||||||
|
&BigInt::from(1i64), // offeringBytecodeOutpointIndex
|
||||||
|
¶ms.offering.executionFee,
|
||||||
|
),
|
||||||
|
).to_bytes();
|
||||||
|
if build_p2sh32_script(&bytecode) != offeringInitiatorBytecodeStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("offering initiator bytecode storage does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let idoInitiatorBytecodeStorageOut = tx.output.get(7);
|
||||||
|
if idoInitiatorBytecodeStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("ido initiator bytecode storage not defined!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
|
1,
|
||||||
|
&build_partial_ido_initiator_bytecode(
|
||||||
|
&build_partial_postlaunch_bytecode(
|
||||||
|
¶ms.permanentLiquidityShareNumerator,
|
||||||
|
¶ms.offering.offer.priceNumerator,
|
||||||
|
),
|
||||||
|
&BigInt::from(7i64), // permanentPoolOTokenReserveOutpointIndex
|
||||||
|
&BigInt::from(0i64), // offeringInitiatorOutpointIndex
|
||||||
|
),
|
||||||
|
).to_bytes();
|
||||||
|
if build_p2sh32_script(&bytecode) != idoInitiatorBytecodeStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("ido initiator bytecode storage does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let offeringBcmrStorageOut = tx.output.get(8);
|
||||||
|
if offeringBcmrStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("offered token bcmr storage not found!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
|
1u32,
|
||||||
|
&serialize_ipfs_bcmr_with_placeholder(¶ms.preInitBcmr.offering),
|
||||||
|
);
|
||||||
|
if bytecode != offeringBcmrStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("offering bcmr storage locking bytecode does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut offered_token_id: Option<Vec<u8>> = None;
|
||||||
|
|
||||||
|
if offered_token_is_in_supply {
|
||||||
|
// verify offered token storage
|
||||||
|
let offeredTokenStorageOut = tx.output.get(9);
|
||||||
|
if offeredTokenStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("offered token supply not found!"));
|
||||||
|
} else {
|
||||||
|
let permanentLiquidityOTokenReserve: BigInt = ¶ms.offeredTokenAmount * ¶ms.permanentLiquidityShareNumerator / &PERMANENT_LIQUIDITY_SHARE_DENOMINATOR.clone();
|
||||||
|
let requiredTokens = ¶ms.offeredTokenAmount + permanentLiquidityOTokenReserve;
|
||||||
|
if offeredTokenStorageOut.unwrap().token.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("offered token storage does not contain a token!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script(1u32).to_bytes();
|
||||||
|
if build_p2sh32_script(&bytecode) != offeredTokenStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("offered token locking bytecode does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
let token_amount = BigInt::from(offeredTokenStorageOut.unwrap().token.as_ref().unwrap().amount);
|
||||||
|
if token_amount < requiredTokens {
|
||||||
|
errors.push(anyhow::anyhow!("invalid offered token amount!"));
|
||||||
|
}
|
||||||
|
offered_token_id = Some(offeredTokenStorageOut.unwrap().token.as_ref().unwrap().id.to_blob());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let oTokenBcmrStorageOut = tx.output.get(9);
|
||||||
|
if oTokenBcmrStorageOut.is_none() {
|
||||||
|
errors.push(anyhow::anyhow!("offered token bcmr storage not found!"));
|
||||||
|
} else {
|
||||||
|
let bytecode = build_storage_script_with_data_and_size(
|
||||||
|
1u32,
|
||||||
|
&serialize_ipfs_bcmr_with_placeholder(¶ms.preInitBcmr.oToken),
|
||||||
|
);
|
||||||
|
if bytecode != oTokenBcmrStorageOut.unwrap().script_pubkey {
|
||||||
|
errors.push(anyhow::anyhow!("offered token bcmr storage locking bytecode does not match, expected bytecode: {}", hex::encode(bytecode)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect preinit fee
|
||||||
|
let mut preinit_paid_fee: u64 = 0;
|
||||||
|
for output in &tx.output {
|
||||||
|
if build_p2sh32_script(
|
||||||
|
&build_p2nfth_script(&platform_fee_nfth).to_bytes()
|
||||||
|
) == output.script_pubkey {
|
||||||
|
preinit_paid_fee += output.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if BigInt::from(preinit_paid_fee) < *IDO_CREATE_EXECUTION_FEE {
|
||||||
|
is_valid_ido = false;
|
||||||
|
invalid_ido_reasons.push(anyhow::anyhow!("ido parse, not enough preinit fee paid!"));
|
||||||
|
}
|
||||||
|
IdoPreinitParseResult {
|
||||||
|
parameters: Some(params),
|
||||||
|
state: Some(state),
|
||||||
|
is_valid_ido,
|
||||||
|
offered_token_is_in_supply,
|
||||||
|
offered_token_id,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IdoPreinitParseResult {
|
||||||
|
parameters: None,
|
||||||
|
state: None,
|
||||||
|
is_valid_ido,
|
||||||
|
offered_token_is_in_supply: false,
|
||||||
|
offered_token_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if BigInt::from(preinit_paid_fee) < *IDO_CREATE_EXECUTION_FEE {
|
}
|
||||||
is_valid_ido = false;
|
|
||||||
info!("ido parse, not enough preinit fee paid!");
|
fn create_ido_context_from_preinit(network: Option<Network>, tx: &Transaction, errors: &mut Vec<Error>, invalid_ido_reasons: &mut Vec<Error>) -> Result<IdoContext> {
|
||||||
|
let result = parse_ido_preinit_tx(network, tx, errors, invalid_ido_reasons);
|
||||||
|
if errors.len() > 0 {
|
||||||
|
Err(anyhow::anyhow!("Failed to parse the preinit tx!"))
|
||||||
|
} else {
|
||||||
|
let params = result.parameters.expect("parameters should be defined!");
|
||||||
|
let state = result.state.expect("state should be defined!");
|
||||||
|
Ok(IdoContext {
|
||||||
|
preinit_txid: tx.txid().to_blob(),
|
||||||
|
init_txid: None,
|
||||||
|
launch_txid: None,
|
||||||
|
status: "PREINIT".to_string(),
|
||||||
|
parameters: IdoParameters::PreInit(params),
|
||||||
|
state: IdoState::PreInit(state),
|
||||||
|
is_valid_ido: result.is_valid_ido,
|
||||||
|
offered_token_id: result.offered_token_id,
|
||||||
|
offering_token_id: None,
|
||||||
|
is_token_created_at_preinit: !result.offered_token_is_in_supply,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Ok(IdoContext {
|
|
||||||
preinit_txid: tx.txid().to_blob(),
|
|
||||||
init_txid: None,
|
|
||||||
launch_txid: None,
|
|
||||||
status: "PREINIT".to_string(),
|
|
||||||
parameters: IdoParameters::PreInit(preinit_parameters),
|
|
||||||
state: IdoState::PreInit(preinit_state),
|
|
||||||
is_valid_ido: is_valid_ido,
|
|
||||||
offered_token_id: offered_token_id,
|
|
||||||
offering_token_id: None,
|
|
||||||
is_token_created_at_preinit: !offered_token_is_in_supply,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_create_ido(
|
async fn on_create_ido(
|
||||||
|
|
@ -1251,7 +1423,22 @@ async fn on_create_ido(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
block_height: i64,
|
block_height: i64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match create_ido_context_from_preinit(network, tx).await {
|
let mut errors: Vec<Error> = Vec::new();
|
||||||
|
let mut invalid_ido_reasons: Vec<Error> = Vec::new();
|
||||||
|
let result = create_ido_context_from_preinit(network, tx, &mut errors, &mut invalid_ido_reasons);
|
||||||
|
if errors.len() > 0 {
|
||||||
|
info!("Parse ido preinit failed, txid: {}\nError(s):", hex::encode(tx.txid().to_blob()));
|
||||||
|
for error in errors {
|
||||||
|
info!(" - {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if invalid_ido_reasons.len() > 0 {
|
||||||
|
info!("Invalid ido found, preinit_txid: {}\nReason(s):", hex::encode(tx.txid().to_blob()));
|
||||||
|
for reason in invalid_ido_reasons {
|
||||||
|
info!(" - {}", reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match result {
|
||||||
Ok(context) => {
|
Ok(context) => {
|
||||||
// create the ido
|
// create the ido
|
||||||
let mut dbtx = pool.begin().await?;
|
let mut dbtx = pool.begin().await?;
|
||||||
|
|
@ -1445,8 +1632,8 @@ fn ido_add_tx(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow::anyhow!("expecting PreInitState!"));
|
return Err(anyhow::anyhow!("expecting PreInitState!"));
|
||||||
}
|
}
|
||||||
match parse_preinit_state_from_tx(tx) {
|
match parse_preinit_state_from_tx(tx) {
|
||||||
Ok(new_state) => {
|
Ok(new_state) => {
|
||||||
let ready = is_preinit_state_ready_to_init(&new_state);
|
let ready = is_preinit_state_ready_to_init(&new_state);
|
||||||
|
|
@ -1875,7 +2062,9 @@ async fn on_add_ido_tx(
|
||||||
}
|
}
|
||||||
// start from PREINIT
|
// start from PREINIT
|
||||||
let preinit_tx = items_tx_map.get(&ido.preinit_txid).ok_or_else(|| anyhow::anyhow!("ido's entrypoint does not exist in the txchain"))?;
|
let preinit_tx = items_tx_map.get(&ido.preinit_txid).ok_or_else(|| anyhow::anyhow!("ido's entrypoint does not exist in the txchain"))?;
|
||||||
let mut context = create_ido_context_from_preinit(network, preinit_tx).await?;
|
let mut _errors: Vec<Error> = Vec::new();
|
||||||
|
let mut _invalid_ido_reasons: Vec<Error> = Vec::new();
|
||||||
|
let mut context = create_ido_context_from_preinit(network, preinit_tx, &mut _errors, &mut _invalid_ido_reasons)?;
|
||||||
let mut updates: Vec<IdoUpdate> = Vec::new();
|
let mut updates: Vec<IdoUpdate> = Vec::new();
|
||||||
for i in 1..new_chain.len() {
|
for i in 1..new_chain.len() {
|
||||||
let item = new_chain.get(i).ok_or_else(|| anyhow::anyhow!("txchain item not found at index"))?;
|
let item = new_chain.get(i).ok_or_else(|| anyhow::anyhow!("txchain item not found at index"))?;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue