ido: resync postlaunch contract & enforce IdoParams NFT version

- update IDO_POSTLAUNCH_CONTRACT to the recompiled 1627-byte script
  (libriften cd58dd1): postlaunch.cash reordered the permanent-pool
  sibling config, moving the all-zero nftOwner to the end. The indexer
  byte-compares this body against on-chain output#7, so the stale
  1669-byte constant would have marked every new IDO invalid.
- set CHIPNET_IDO_PARAMS_NFT_CATEGORY (was an all-zero placeholder).
- validate the ORB IdoParams NFT commitment version byte: only index
  when it equals IDO_PARAMS_VERSION (0x00), else push an
  "incompatible with the indexer" reason and mark the IDO invalid.
  The other commitment fields are read at v0 offsets, so the parameter
  comparisons are skipped on a version mismatch to avoid spurious errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hossein Zoda 2026-07-13 01:51:56 +00:00
parent c3f3c269e2
commit ca7f829591

View file

@ -81,8 +81,10 @@ static MIN_PLP_AFTER_DISCOUNT: LazyLock<Integer> = LazyLock::new(|| Integer::fro
// commitment carries the economic parameters (platform fee, execution fees,
// offering-duration window, delphi category, platform-fee nfth) that the
// announced IDO parameters are validated against.
// TODO:: set the ido params nft category for chipnet
const CHIPNET_IDO_PARAMS_NFT_CATEGORY: &[u8; 32] = &[0u8; 32];
const CHIPNET_IDO_PARAMS_NFT_CATEGORY: &[u8; 32] = &[
0xc9, 0x16, 0x7d, 0x12, 0x39, 0x46, 0x27, 0xba, 0x28, 0x30, 0x70, 0x5f, 0xb2, 0xb0, 0xf0, 0x0b,
0x49, 0xeb, 0x28, 0x46, 0x9e, 0x65, 0xda, 0x53, 0x69, 0x12, 0xd0, 0x50, 0x75, 0x89, 0x08, 0x00,
];
// TODO:: set the ido params nft category for mainnet
const MAINNET_IDO_PARAMS_NFT_CATEGORY: &[u8; 32] = &[0u8; 32];
@ -122,7 +124,7 @@ static IDO_INITIATOR_CONTRACT: LazyLock<Vec<u8>> = LazyLock::new(|| {
hex::decode("c0ce827701209dc0cf827700a0695579827701209dc0c85779c8885679c99dc0c85779c8885679c99d5479529376ca827778ca7853947f77527f75817bca7b53945279947f777c7f7501157f7701207f75c0cfc0ce7eaa88547ac852d1827701209d52d300a06902aa20c101147f755479827751807e54797e5379827751807e537a7e01207e7b7e01207e52d17e01207e547a7e587e52d358807e537a7eaa7e01877e57cd8857ccc0c6a26957d1c0ce8857d2c0cf8857d3c0d09d525752807e7c7e7658cd8858cc02e803a26958d1008859cd8859cc78c6a26959d178ce8859d278cf8859d37cd09c").unwrap()
});
static IDO_POSTLAUNCH_CONTRACT: LazyLock<Vec<u8>> = LazyLock::new(|| {
hex::decode("5e79009c63c0009dc0c9009e69c0cdc0c788c0ccc0c6a269c0d1c0ce88c0d3c0d09dc0d2c0cf88520052807e5e7a7ec0c851c88851c9589d7651cd8851cc51c6a26951d151ce8851d351d09d7652cd8852d10088c0c852c88852c9599d53cd8853cc52c6a26953d152ce8853d352d09d54d10088c4559d6d6d6d6d6d6d6d51675e79519c63c0009dc0c9009dc0cdc0c788c0d1c0ce88c0d3c0d09dc0d2c0cf88c0c853c88853c9539d53cd53c78853cc53c6a26953d153ce8853d353d09d54ce5c7a8854cf517f755f84558854cd54c78854cc54c6a26954d154ce8854d354d09d54d254cf8854cf5f7f77587f758154cf577f77587f758151d07b9f52d07b9f9b690120c0cfc0ce7eaa7e5c7a7e000000556576c75579876376ce5d798763527978d093537a757c6b7c6c6776ce60798763537978d093547a757c6b7c6b7c6c6c6776ce008868686ec6937b757c6776ce008868768b7776c3a266c0ccc0c6537a93a269c0ccc0c6567a8193a269c0c851c88851c9519d51cd51c78851cc51c6a26951d0537a937600a06351d15e798851d378a26951d200886751d1008868c0c852c88852c9529d52cd52c78852cc52c6a26952d0537a937600a06352d15b798852d378a26952d200886752d10088686d6d6d6d6d6d6d6d51675e7a529d5e7a92c0009dc0c9009d5a79827701209d55ce5d7a8855cf517f755f845588c0c851c88851c9519d55cf5f7f77587f75817600a06351d078a26951ce5d79886851cf0088c0c852c88852c9529d55cf577f77587f75817600a06352d078a26952ce5b79886852cf0088c0c853c88853c9539d53ce5e7988000055cf517f756084010087635d7981547994765d79950400e1f505967653d0a06353d07768765d79950500e87648179655cf01177f77587f758194760500e8764817955e79967800a07800a09a6378567a757c6b7c6b7c6b7c6b7c6c6c6c6c765379a16376557a757c6b7c6b7c6b7c6c6c6c675279557a757c6b7c6b7c6b7c6c6c6c68686d6d68c0c651c69352c69353c69352d053799451d053d093537994012001127a7e0113797e5c7900a26952795d7a950400e1f50596537a7894567900a0567900a09a6300d10111798800d357799d00d2008851d10113798851d356799d51d2008859796302e007b27554cd016a8854d1008800cd53798851cd5379886754ce01207f755c798854cf827701279d54cf517f75518854ce54d18854cf517f7701207f7554cf01217f77527f754ceec0768b78c878c88876c95279c98b9d76c776013f7f770e75c08c76c8c0c888c0c97cc98b9c8876517f77587f75817801297f77527f527f527f587f587f75547a81547a81547a81547a81547a815879ce5979d1885879c65979cc9d5779ce5879d1885779c65879cc9d5879d05979d3949003a08601768c5279587a957893527996537a577a957b937b9655795279937b527993557aa2695779cd5879c7885679cd517f75013e885679cd597f77567a597f77885579cd517f77587f75785880885679d37c947651a2695579d351a269567ad0557a945479935579d0547993957c7b94537a93537ad3537a9395a12052797e4cbd78cf7bce7eaac0768b78c878c88876c95279c98b9d76c776013f7f770e75c08c76c8c0c888c0c97cc98b9c8876517f77587f75817600a2695479567987635379d35479d05279949d5279d35379d09d5379ce5479d1885379c65479cc9d5279ce5379d1885279c65379cc9d5379cd5479c78878517f750058807e5279597f777e5379cd7888756778597f7701207f75557978887800a0635479cd012058797e0778cf7bce7eaa877e885479d15579ce885479d352799d6875686d6d6d517eaa00cd07ca537f7776aa2052797e0f8802ed007f7b63756777680089008a7e88280000000000000000000000000000000000000000000000000000000000000000000000000000000052797e0200007e60797e10000000000000000000000000000000007e013e787e0e75c08c76c8c0c888c0c97cc98b9c7e51cd78886d6d75686700d1008800cd016a8851d1008851cd016a8854d1008854cd016a8868537900a063527952cd8852d35479a26952d10113798852d200886752cd016a8852d10088687600a063527953cd8853d3789d53d10111798853d200886753cd016a8853d100886801205e7a7e01137a7e55cd8855cc557aa2697800a06355d35279a26955d15f798855d200886755d1008868566576c49f766b6376d100876476d101207f757655ce01207f7587916976c0ce01207f758791697568768b77686c91666d6d6d6d6d6d6d6d6d75516868").unwrap()
hex::decode("5e79009c63c0009dc0c9009e69c0cdc0c788c0ccc0c6a269c0d1c0ce88c0d3c0d09dc0d2c0cf88520052807e5e7a7ec0c851c88851c9589d7651cd8851cc51c6a26951d151ce8851d351d09d7652cd8852d10088c0c852c88852c9599d53cd8853cc52c6a26953d152ce8853d352d09d54d10088c4559d6d6d6d6d6d6d6d51675e79519c63c0009dc0c9009dc0cdc0c788c0d1c0ce88c0d3c0d09dc0d2c0cf88c0c853c88853c9539d53cd53c78853cc53c6a26953d153ce8853d353d09d54ce5c7a8854cf517f755f84558854cd54c78854cc54c6a26954d154ce8854d354d09d54d254cf8854cf5f7f77587f758154cf577f77587f758151d07b9f52d07b9f9b690120c0cfc0ce7eaa7e5c7a7e000000556576c75579876376ce5d798763527978d093537a757c6b7c6c6776ce60798763537978d093547a757c6b7c6b7c6c6c6776ce008868686ec6937b757c6776ce008868768b7776c3a266c0ccc0c6537a93a269c0ccc0c6567a8193a269c0c851c88851c9519d51cd51c78851cc51c6a26951d0537a937600a06351d15e798851d378a26951d200886751d1008868c0c852c88852c9529d52cd52c78852cc52c6a26952d0537a937600a06352d15b798852d378a26952d200886752d10088686d6d6d6d6d6d6d6d51675e7a529d5e7a92c0009dc0c9009d5a79827701209d55ce5d7a8855cf517f755f845588c0c851c88851c9519d55cf5f7f77587f75817600a06351d078a26951ce5d79886851cf0088c0c852c88852c9529d55cf577f77587f75817600a06352d078a26952ce5b79886852cf0088c0c853c88853c9539d53ce5e7988000055cf517f756084010087635d7981547994765d79950400e1f505967653d0a06353d07768765d79950500e87648179655cf01177f77587f758194760500e8764817955e79967800a07800a09a6378567a757c6b7c6b7c6b7c6b7c6c6c6c6c765379a16376557a757c6b7c6b7c6b7c6c6c6c675279557a757c6b7c6b7c6b7c6c6c6c68686d6d68c0c651c69352c69353c69352d053799451d053d093537994012001127a7e0113797e5c7900a26952795d7a950400e1f50596537a7894567900a0567900a09a6300d10111798800d357799d00d2008851d10113798851d356799d51d2008859796302e007b27554cd016a8854d1008800cd53798851cd5379886754ce01207f755c798854cf827701279d54cf517f7501008854ce54d18854cf517f7701207f7554cf01217f77527f754cc5c0768b78c878c88876c95279c98b9d76c7517f77587f527f527f527f587f587f75557a81557a81557a81557a81557a81557a815779ce5879d1885779c65879cc9d5679ce5779d1885679c65779cc9d5779d05879d3949003a08601765a955279587a9578938c7c967b567a955279938c7b9655795279937b527993557aa2695679cd5779c788013e7858807e5679c7597f777e5679cd885679d37c947651a2695579d351a269567ad0557a945479935579d0547993957c7b94537a93537ad3537a9395a12052797e4cb778cf7bce7eaac0768b78c878c88876c95279c98b9d76c776517f77587f75817600a2695479567987635379d35479d05279949d5279d35379d09d5379ce5479d1885379c65479cc9d5279ce5379d1885279c65379cc9d5379cd5479c7885279cd597f77527f75768100a269013e0058807e787e53795b7f777e5479cd78886d6778011f7f7701207f75557978887800a0635479cd012058797e0778cf7bce7eaa877e885479d15579ce885479d352799d6875686d6d6d517eaa00cd07ca537f7776aa2052797e0f8802c4007f7b63756777680089008a7e8808000000000000000052797e0200007e60797e0800000000000000007e0800000000000000007e2000000000000000000000000000000000000000000000000000000000000000007e013e787e0e75c08c76c8c0c888c0c97cc98b9c7e51cd78886d6d75686700d1008800cd016a8851d1008851cd016a8854d1008854cd016a8868537900a063527952cd8852d35479a26952d10113798852d200886752cd016a8852d10088687600a063527953cd8853d3789d53d10111798853d200886753cd016a8853d100886801205e7a7e01137a7e55cd8855cc557aa2697800a06355d35279a26955d15f798855d200886755d1008868566576c49f766b6376d100876476d101207f757655ce01207f7587916976c0ce01207f758791697568768b77686c91666d6d6d6d6d6d6d6d6d75516868").unwrap()
});
static IDO_PREINIT_CONTRACT: LazyLock<Vec<u8>> = LazyLock::new(|| {
hex::decode("c0519dc0c800c88800c9529dc0c852c88852c9539dc0c853c88853c9549dc0c854c88854c9559dc0c855c88855c9569dc0c856c88856c9579d5c79009e63c0c858c88858c9599d67c0c858c88858c9599d68c0c857c88857c9589d597981009c5d79009c9b5b7981009c9b63c0d1008852cd00c78852d1008853cd52c78853d1008854cd53c78854d1008855cd54c78855d1008856cd55c78856d1008857cd56c78857d100885c79009e6359cd58c78859cc58c6a26959d158ce8859d358d09d59d258cf8867597981009e5c79009e9a6459cd58c78859d10088686858cd57c78858d100880302010054797ec101587f775b7981009c6301005f79009e63015177685e79009c6300cd53798800d10088760251207e5e797e01207e5d797e52797e7b757c67c0c859c88859c9009d00cd016a8800d100885acd5c79885ad1c0c8885ad3009d5ad20100885bd10088c45c9d760200207e5e797e01207ec0c87e52797e7b757c6875675e79009c635d79009c6300cd52798800d10088030051205d797e01207e5c797e787e7767c0c859c88859c9009d53795579950400e1f50596547978935479789455795279a06902aa20012060797e5d797e5c797eaa7e01877e5c798277009c6302a91401200111797e5c797ea97e01877e776800cd788800d1c0c88800d352799d00d2008859cd56798859d1c0c88859d353799d59d2008858c7827758c77853947f77527f758158c7527953945279947f77787f7576827700a06376517f75768176014b9f788b547982779f9a635279788b7f77537a757c6b7c6c5279517f757b757c6878016a8764016a537a757c6b7c6c686d67016a77685acd78885ad100885bd10088c45c9d035100200114797e01207e0113797e58797e587a757c6b7c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6c6d6d6d7568675d79009c6300cd52798800d10088035151205d797e01207e5c797e787e7767c0c859c88859c9009d00cd016a8800d100885acd5279885ad1c0c8885ad3009d5ad201ff885bd10088c45c9d03510020c0c87e01207e5c797e787e7768686802aa20c101147f7552797eaa7e01877ec0cd886d67c0c859c88859c95a9d55ca827755ca7853947f77527f758155ca527953945279947f77787f75012302aa2001205f797e5d797e5b797eaa7e01877e7e5b798277009c63011702a914012060797e5b797ea97e01877e7e776800cd02aa20c101147f7553797e54797eaa7e01877e8800d1008800ca827700ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686800ca537953945379945279947f7702aa20030200005d797e52797eaa7e01877e51cd8851d1008852ca827752ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686852ca537953945379945279947f7702aa20030200000111797e707c0114937f757e01207e01ff0119797eaa7e707c0135937f777eaa7e01877e52cd8852d1008853ca827753ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686853ca537953945379945279947f7702aa20030200000115797e52797eaa7e01877e53cd8853d1008854ca827754ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686854ca537953945379945279947f7702aa20030200000119797e52797eaa7e01877e54cd8854d1008857c7827757c77853947f77527f75815178529302ff00a063755367785293014ba0637552686857c7537953945379945279947f7755cd03020000011d797e52797e8855d1008803020000011c797e56cd8856cc58c6a26956d158ce8856d3011a799d56d2008856ca827756ca7853947f77527f758156ca527953945279947f77787f7557cd02aa20c101147f7501207e01000128797eaa7e53797eaa7e01877e8857cc59c6a26957d159ce8857d359d09d57d259cf88525752807e011f797e58cd8858d158ce8858d358d0011e79949d58d200886d6d6d6d6d6d6d6d6d6d6d6d6d75686d6d6d6d6d6d7551").unwrap()
@ -141,7 +143,7 @@ static REBUILD_IPFS_PLACEHOLDER_BCMR_FOR_GENESIS_WITH_AUTHGUARD_CONTRACT: LazyLo
/// The 121-byte commitment of the ORB IdoParams NFT (libriften
/// packages/cauldron/src/orb/v0). Byte layout:
/// [0] version
/// [0] version (must equal IDO_PARAMS_VERSION)
/// [1..33] orbPoolParamsCategory
/// [33..65] delphiCategory
/// [65..97] platformFeeNfth
@ -155,7 +157,13 @@ static REBUILD_IPFS_PLACEHOLDER_BCMR_FOR_GENESIS_WITH_AUTHGUARD_CONTRACT: LazyLo
/// uses, so equality checks against announced parameters are direct compares.
const IDO_PARAMS_COMMITMENT_SIZE: usize = 121;
/// The only ORB IdoParams NFT commitment version this indexer understands
/// (libriften `ORB_IDO_PARAMS_VERSION`). Any other value means the params NFT
/// was minted for a newer/incompatible layout and the IDO is not indexed.
const IDO_PARAMS_VERSION: u8 = 0x00;
pub struct IdoParamsNftCommitment {
pub version: u8,
pub orbPoolParamsCategory: Vec<u8>,
pub delphiCategory: Vec<u8>,
pub platformFeeNfth: Vec<u8>,
@ -175,6 +183,7 @@ fn parse_ido_params_nft_commitment(commitment: &[u8]) -> Result<IdoParamsNftComm
));
}
Ok(IdoParamsNftCommitment {
version: commitment[0],
orbPoolParamsCategory: commitment[1..33].to_vec(),
delphiCategory: commitment[33..65].to_vec(),
platformFeeNfth: commitment[65..97].to_vec(),
@ -1644,19 +1653,32 @@ fn parse_ido_preinit_tx_params(
}
}
if let Some(ref ido_params) = ido_params_commitment {
// The remaining fields are read at v0 commitment offsets, so a
// different version could mean a different layout: reject on the
// version alone and skip the parameter comparisons.
if ido_params.version != IDO_PARAMS_VERSION {
invalid_ido_reasons.push(anyhow::anyhow!(
"ido params nft version {} is incompatible with the indexer (expected {})",
ido_params.version,
IDO_PARAMS_VERSION
));
is_valid_ido = false;
} else {
if preinit_parameters.offering.delphiCategory != ido_params.delphiCategory {
invalid_ido_reasons.push(anyhow::anyhow!(
"offering.delphiCategory != ido_params.delphiCategory"
));
is_valid_ido = false;
}
if preinit_parameters.offering.platformFeeNFTH != ido_params.platformFeeNfth {
if preinit_parameters.offering.platformFeeNFTH != ido_params.platformFeeNfth
{
invalid_ido_reasons.push(anyhow::anyhow!(
"offering.platformFeeNFTH != ido_params.platformFeeNfth"
));
is_valid_ido = false;
}
if preinit_parameters.offering.platformFeeNumerator != ido_params.platformFee {
if preinit_parameters.offering.platformFeeNumerator != ido_params.platformFee
{
invalid_ido_reasons.push(anyhow::anyhow!(
"offering.platformFeeNumerator != ido_params.platformFee"
));
@ -1668,7 +1690,8 @@ fn parse_ido_preinit_tx_params(
));
is_valid_ido = false;
}
if preinit_parameters.orbPoolParamsCategory != ido_params.orbPoolParamsCategory
if preinit_parameters.orbPoolParamsCategory
!= ido_params.orbPoolParamsCategory
{
invalid_ido_reasons.push(anyhow::anyhow!(
"orbPoolParamsCategory baked in the postlaunch bytecode != ido_params.orbPoolParamsCategory"
@ -1676,6 +1699,7 @@ fn parse_ido_preinit_tx_params(
is_valid_ido = false;
}
}
}
// The first output must be the Delphi NFT. Its category must match the
// announcement's delphiCategory (itself pinned to the IdoParams NFT
@ -1704,7 +1728,10 @@ fn parse_ido_preinit_tx_params(
let delphi_timestamp =
u64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], 0, 0]);
created_at = delphi_timestamp as i64;
if let Some(ref ido_params) = ido_params_commitment {
if let Some(ido_params) = ido_params_commitment
.as_ref()
.filter(|p| p.version == IDO_PARAMS_VERSION)
{
let offering_duration =
&preinit_parameters.offering.launchConditions.expiresAt
- Integer::from(delphi_timestamp);
@ -2058,7 +2085,10 @@ fn parse_ido_preinit_tx(
// collect the preinit (create) execution fee: sats paid to bare-p2s
// p2nfth(platformFeeNFTH) outputs, which must cover the
// createExecutionFee pinned by the IdoParams NFT commitment.
if let Some(ref ido_params) = ido_params_commitment {
if let Some(ido_params) = ido_params_commitment
.as_ref()
.filter(|p| p.version == IDO_PARAMS_VERSION)
{
let mut preinit_paid_fee: u64 = 0;
let fee_script = build_p2nfth_script(&params.offering.platformFeeNFTH);
for output in &tx.output {
@ -3408,7 +3438,7 @@ mod tests {
#[test]
fn ido_params_nft_commitment_roundtrip() {
let mut commitment = vec![0u8; IDO_PARAMS_COMMITMENT_SIZE];
commitment[0] = 1; // version
commitment[0] = IDO_PARAMS_VERSION; // version
commitment[1..33].copy_from_slice(&[0xAA; 32]); // orbPoolParamsCategory
commitment[33..65].copy_from_slice(&[0xBB; 32]); // delphiCategory
commitment[65..97].copy_from_slice(&[0xCC; 32]); // platformFeeNfth
@ -3425,6 +3455,7 @@ mod tests {
commitment[117..121]
.copy_from_slice(&encode_padded_vm_number(&Integer::from(1000i64), 4).unwrap());
let parsed = parse_ido_params_nft_commitment(&commitment).expect("should parse");
assert_eq!(parsed.version, IDO_PARAMS_VERSION);
assert_eq!(parsed.orbPoolParamsCategory, vec![0xAA; 32]);
assert_eq!(parsed.delphiCategory, vec![0xBB; 32]);
assert_eq!(parsed.platformFeeNfth, vec![0xCC; 32]);