diff --git a/src/db/ido/mod.rs b/src/db/ido/mod.rs index e71c85b..d928e96 100644 --- a/src/db/ido/mod.rs +++ b/src/db/ido/mod.rs @@ -89,11 +89,10 @@ const CHIPNET_IDO_PARAMS_NFT_CATEGORY: &[u8; 32] = &[ // TODO:: set the ido params nft category for mainnet const MAINNET_IDO_PARAMS_NFT_CATEGORY: &[u8; 32] = &[0u8; 32]; -// (OP_PUSH18 "CauldronIdo-2026Q2" OP_DROP), found at the start of every ido +// (OP_PUSH8 "CldIdo00" OP_DROP), found at the start of every ido // state machine redeem script. pub const IDO_SIGNATURE: &[u8] = &[ - 0x12, 0x43, 0x61, 0x75, 0x6c, 0x64, 0x72, 0x6f, 0x6e, 0x49, 0x64, 0x6f, 0x2d, 0x32, 0x30, 0x32, - 0x36, 0x51, 0x32, 0x75, + 0x08, 0x43, 0x6c, 0x64, 0x49, 0x64, 0x6f, 0x30, 0x30, 0x75, ]; static OFFERING_CONTRACT: LazyLock> = LazyLock::new(|| { @@ -103,7 +102,7 @@ static OFFERING_ENTRY_CONTRACT: LazyLock> = LazyLock::new(|| { hex::decode("827701209dc0519dc0ce01207f7500ce01207f758800cf517f755f845288c0cf517f7501208401008763c0c878c88876c9529d687551").unwrap() }); static LAUNCHER_CONTRACT: LazyLock> = LazyLock::new(|| { - hex::decode("c0009d00c852c88852c9529d00c854c88854c9549d00c855c88855c9559d00ce01207f7551ce78527e8851cf517f755f8401008853ce01207f7555798853cf567f75817600a06953d100876453d101207f7552798791696851d0547aa07651d0557aa09b63537952799f696851cf557f77547f758100a16301205c797e56797e556085537956807e0058807e52d058807e0058807e00d28800d154798800d3009d00cd788851cd788851cc52c6a26951d152ce8851d352d09d51d2008852d10088c4549d75675279517e00d18800d3009d766355ca827755ca7853947f77527f758155ca527953945279947f77787f75526085557956807e51cf557f77547f757e0058807e52d058807e0058807e00d28802aa20c101147f755a79827751807e5a797e01207e60797e52797eaa7e01877e00cd8801205f797e59797e52cd8852cc52c6a26952d152ce8852d352d09d52d2008854d10088c4559d6d756754ca827754ca7853947f77527f758154ca527953945279947f77787f7551d07600a06354d152ce8854d3789d54d20088012060797e5a797e54cd8855d10088c4569d6754d10088c4559d6852567956807e51cf557f77547f757e0058807e7858807e0058807e00d28801205f797e5e7981009c630100776802aa20c101147f755e79827751807e5e797e5d79827751807e5d797e5c79827751807e5c797e01207e5b797e547e60797e52797e01207e0112797e54797eaa7e01877e00cd8852d152ce8852d352d05379949d52cc52c6a269520052807e5d797e52cd8852d200886d6d756802aa2056798277518057797e5a797eaa7e01877e51cd88545b797e51d28851cc51c6a26951d153798851d3009d686d6d6d6d6d6d51").unwrap() + hex::decode("c0009d00c852c88852c9529d00c854c88854c9549d00c855c88855c9559d00ce01207f7551ce78527e8851cf517f755f8401008853ce01207f7555798853cf567f75817600a06953d100876453d101207f7552798791696851d0547aa07651d0557aa09b63537952799f696851cf557f77547f758100a16301205c797e56797e556085537956807e0058807e52d058807e0058807e00d28800d154798800d3009d00cd788851cd788851cc52c6a26951d152ce8851d352d09d51d2008852d10088c4549d75675279517e00d18800d3009d766355ca827755ca7853947f77527f758155ca527953945279947f77787f75526085557956807e51cf557f77547f757e0058807e52d058807e0058807e00d28802aa20c15a7f755a79827751807e5a797e01207e60797e52797eaa7e01877e00cd8801205f797e59797e52cd8852cc52c6a26952d152ce8852d352d09d52d2008854d10088c4559d6d756754ca827754ca7853947f77527f758154ca527953945279947f77787f7551d07600a06354d152ce8854d3789d54d20088012060797e5a797e54cd8855d10088c4569d6754d10088c4559d6852567956807e51cf557f77547f757e0058807e7858807e0058807e00d28801205f797e5e7981009c630100776802aa20c15a7f755e79827751807e5e797e5d79827751807e5d797e5c79827751807e5c797e01207e5b797e547e60797e52797e01207e0112797e54797eaa7e01877e00cd8852d152ce8852d352d05379949d52cc52c6a269520052807e5d797e52cd8852d200886d6d756802aa2056798277518057797e5a797eaa7e01877e51cd88545b797e51d28851cc51c6a26951d153798851d3009d686d6d6d6d6d6d51").unwrap() }); static DISTRIBUTOR_DEPLOY_CONTRACT: LazyLock> = LazyLock::new(|| { hex::decode("c0009d00c852c88852c9529d00cf517f77567f758100ce01207f7551ce01207f75788851cf517f755f84538851cf517f7551ca51ca82770136940120947f7701207f750000537960840100876451cf517f77567f75817b757c51cf577f77587f758177687800a2697600a26952d352d051d0949d587a810000567901208401008764527900a26951c6765479950400e1f50596537a757c6b7c6c765379947b757c53cc5279a26953d1008854cc5379a26954d10088756753d05379950400e1f505967b757c53d0527994777600a06353d3789d53d153ce886753d10088687800a06354d352799d54d153ce886754d100886868547900a063012056797e5e797e51cd8851d1587988565551807e5c797e597956799356807e51d28851d3009d55d351d09d55d152ce88525152807e5f797e55cd8855d200886751d351d09d51d152ce88012056797e5d797e51cd8851d20088687600a06301205a797e5d797e53cd886753cd016a88687c00a06301205a797e5c797e54cd886754cd016a886800cf577f77547f75817651a06300cf517f7500cf517f77567f757e788c54807e00cf5b7f77587f758153799358807e00cf01137f77587f757e00cf011b7f77587f758155799358807e00d28800ce00d18800c700cd8800d000d39d52cf52d28852ce52d18852c752cd88675500cf517f77567f757e00cf5b7f77587f758153799358807e00cf01137f77587f757e00cf011b7f77587f758155799358807e00d28800d158798800d3009d01205a797e5d797e00cd8852d100885457790120840100876475536876ce59798876cf517f755f845488756855557a00a063755668c4789e6376d10088c4788b9d686d6d6d6d6d6d6d7551").unwrap() @@ -122,13 +121,13 @@ static OFFERING_INTEGRATED_TIMELOCKED_P2NFTH_CONTRACT: LazyLock> = LazyL }); static IDO_INITIATOR_CONTRACT: LazyLock> = LazyLock::new(|| { - hex::decode("c0ce827701209dc0cf827700a0695579827701209dc0c85779c8885679c99dc0c85779c8885679c99d5479529376ca827778ca7853947f77527f75817bca7b53945279947f777c7f7501157f7701207f75c0cfc0ce7eaa88547ac852d1827701209d52d300a06902aa20c101147f755479827751807e54797e5379827751807e537a7e01207e7b7e01207e52d17e01207e547a7e587e52d358807e537a7eaa7e01877e57cd8857ccc0c6a26957d1c0ce8857d2c0cf8857d3c0d09d525752807e7c7e7658cd8858cc02e803a26958d1008859cd8859cc78c6a26959d178ce8859d278cf8859d37cd09c").unwrap() + hex::decode("c0ce827701209dc0cf827700a0695579827701209dc0c85779c8885679c99dc0c85779c8885679c99d5479529376ca827778ca7853947f77527f75817bca7b53945279947f777c7f755b7f7701207f75c0cfc0ce7eaa88547ac852d1827701209d52d300a06902aa20c15a7f755479827751807e54797e5379827751807e537a7e01207e7b7e01207e52d17e01207e547a7e587e52d358807e537a7eaa7e01877e57cd8857ccc0c6a26957d1c0ce8857d2c0cf8857d3c0d09d525752807e7c7e7658cd8858cc02e803a26958d1008859cd8859cc78c6a26959d178ce8859d278cf8859d37cd09c").unwrap() }); static IDO_POSTLAUNCH_CONTRACT: LazyLock> = LazyLock::new(|| { hex::decode("5e79009c63c0009dc0c9009e69c0cdc0c788c0ccc0c6a269c0d1c0ce88c0d3c0d09dc0d2c0cf88520052807e5e7a7ec0c851c88851c9589d7651cd8851cc51c6a26951d151ce8851d351d09d7652cd8852d10088c0c852c88852c9599d53cd8853cc52c6a26953d152ce8853d352d09d54d10088c4559d6d6d6d6d6d6d6d51675e79519c63c0009dc0c9009dc0cdc0c788c0d1c0ce88c0d3c0d09dc0d2c0cf88c0c853c88853c9539d53cd53c78853cc53c6a26953d153ce8853d353d09d54ce5c7a8854cf517f755f84558854cd54c78854cc54c6a26954d154ce8854d354d09d54d254cf8854cf5f7f77587f758154cf577f77587f758151d07b9f52d07b9f9b690120c0cfc0ce7eaa7e5c7a7e000000556576c75579876376ce5d798763527978d093537a757c6b7c6c6776ce60798763537978d093547a757c6b7c6b7c6c6c6776ce008868686ec6937b757c6776ce008868768b7776c3a266c0ccc0c6537a93a269c0ccc0c6567a8193a269c0c851c88851c9519d51cd51c78851cc51c6a26951d0537a937600a06351d15e798851d378a26951d200886751d1008868c0c852c88852c9529d52cd52c78852cc52c6a26952d0537a937600a06352d15b798852d378a26952d200886752d10088686d6d6d6d6d6d6d6d51675e7a529d5e7a92c0009dc0c9009d5a79827701209d55ce5d7a8855cf517f755f845588c0c851c88851c9519d55cf5f7f77587f75817600a06351d078a26951ce5d79886851cf0088c0c852c88852c9529d55cf577f77587f75817600a06352d078a26952ce5b79886852cf0088c0c853c88853c9539d53ce5e7988000055cf517f756084010087635d7981547994765d79950400e1f505967653d0a06353d07768765d79950500e87648179655cf01177f77587f758194760500e8764817955e79967800a07800a09a6378567a757c6b7c6b7c6b7c6b7c6c6c6c6c765379a16376557a757c6b7c6b7c6b7c6c6c6c675279557a757c6b7c6b7c6b7c6c6c6c68686d6d68c0c651c69352c69353c69352d053799451d053d093537994012001127a7e0113797e5c7900a26952795d7a950400e1f50596537a7894567900a0567900a09a6300d10111798800d357799d00d2008851d10113798851d356799d51d2008859796302e007b27554cd016a8854d1008800cd53798851cd5379886754ce01207f755c798854cf827701279d54cf517f7501008854ce54d18854cf517f7701207f7554cf01217f77527f754cc5c0768b78c878c88876c95279c98b9d76c7517f77587f527f527f527f587f587f75557a81557a81557a81557a81557a81557a815779ce5879d1885779c65879cc9d5679ce5779d1885679c65779cc9d5779d05879d3949003a08601765a955279587a9578938c7c967b567a955279938c7b9655795279937b527993557aa2695679cd5779c788013e7858807e5679c7597f777e5679cd885679d37c947651a2695579d351a269567ad0557a945479935579d0547993957c7b94537a93537ad3537a9395a12052797e4cb778cf7bce7eaac0768b78c878c88876c95279c98b9d76c776517f77587f75817600a2695479567987635379d35479d05279949d5279d35379d09d5379ce5479d1885379c65479cc9d5279ce5379d1885279c65379cc9d5379cd5479c7885279cd597f77527f75768100a269013e0058807e787e53795b7f777e5479cd78886d6778011f7f7701207f75557978887800a0635479cd012058797e0778cf7bce7eaa877e885479d15579ce885479d352799d6875686d6d6d517eaa00cd07ca537f7776aa2052797e0f8802c4007f7b63756777680089008a7e8808000000000000000052797e0200007e60797e0800000000000000007e0800000000000000007e2000000000000000000000000000000000000000000000000000000000000000007e013e787e0e75c08c76c8c0c888c0c97cc98b9c7e51cd78886d6d75686700d1008800cd016a8851d1008851cd016a8854d1008854cd016a8868537900a063527952cd8852d35479a26952d10113798852d200886752cd016a8852d10088687600a063527953cd8853d3789d53d10111798853d200886753cd016a8853d100886801205e7a7e01137a7e55cd8855cc557aa2697800a06355d35279a26955d15f798855d200886755d1008868566576c49f766b6376d100876476d101207f757655ce01207f7587916976c0ce01207f758791697568768b77686c91666d6d6d6d6d6d6d6d6d75516868").unwrap() }); static IDO_PREINIT_CONTRACT: LazyLock> = LazyLock::new(|| { - hex::decode("c0519dc0c800c88800c9529dc0c852c88852c9539dc0c853c88853c9549dc0c854c88854c9559dc0c855c88855c9569dc0c856c88856c9579d5c79009e63c0c858c88858c9599d67c0c858c88858c9599d68c0c857c88857c9589d597981009c5d79009c9b5b7981009c9b63c0d1008852cd00c78852d1008853cd52c78853d1008854cd53c78854d1008855cd54c78855d1008856cd55c78856d1008857cd56c78857d100885c79009e6359cd58c78859cc58c6a26959d158ce8859d358d09d59d258cf8867597981009e5c79009e9a6459cd58c78859d10088686858cd57c78858d100880302010054797ec101587f775b7981009c6301005f79009e63015177685e79009c6300cd53798800d10088760251207e5e797e01207e5d797e52797e7b757c67c0c859c88859c9009d00cd016a8800d100885acd5c79885ad1c0c8885ad3009d5ad20100885bd10088c45c9d760200207e5e797e01207ec0c87e52797e7b757c6875675e79009c635d79009c6300cd52798800d10088030051205d797e01207e5c797e787e7767c0c859c88859c9009d53795579950400e1f50596547978935479789455795279a06902aa20012060797e5d797e5c797eaa7e01877e5c798277009c6302a91401200111797e5c797ea97e01877e776800cd788800d1c0c88800d352799d00d2008859cd56798859d1c0c88859d353799d59d2008858c7827758c77853947f77527f758158c7527953945279947f77787f7576827700a06376517f75768176014b9f788b547982779f9a635279788b7f77537a757c6b7c6c5279517f757b757c6878016a8764016a537a757c6b7c6c686d67016a77685acd78885ad100885bd10088c45c9d035100200114797e01207e0113797e58797e587a757c6b7c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6c6d6d6d7568675d79009c6300cd52798800d10088035151205d797e01207e5c797e787e7767c0c859c88859c9009d00cd016a8800d100885acd5279885ad1c0c8885ad3009d5ad201ff885bd10088c45c9d03510020c0c87e01207e5c797e787e7768686802aa20c101147f7552797eaa7e01877ec0cd886d67c0c859c88859c95a9d55ca827755ca7853947f77527f758155ca527953945279947f77787f75012302aa2001205f797e5d797e5b797eaa7e01877e7e5b798277009c63011702a914012060797e5b797ea97e01877e7e776800cd02aa20c101147f7553797e54797eaa7e01877e8800d1008800ca827700ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686800ca537953945379945279947f7702aa20030200005d797e52797eaa7e01877e51cd8851d1008852ca827752ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686852ca537953945379945279947f7702aa20030200000111797e707c0114937f757e01207e01ff0119797eaa7e707c0135937f777eaa7e01877e52cd8852d1008853ca827753ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686853ca537953945379945279947f7702aa20030200000115797e52797eaa7e01877e53cd8853d1008854ca827754ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686854ca537953945379945279947f7702aa20030200000119797e52797eaa7e01877e54cd8854d1008857c7827757c77853947f77527f75815178529302ff00a063755367785293014ba0637552686857c7537953945379945279947f7755cd03020000011d797e52797e8855d1008803020000011c797e56cd8856cc58c6a26956d158ce8856d3011a799d56d2008856ca827756ca7853947f77527f758156ca527953945279947f77787f7557cd02aa20c101147f7501207e01000128797eaa7e53797eaa7e01877e8857cc59c6a26957d159ce8857d359d09d57d259cf88525752807e011f797e58cd8858d158ce8858d358d0011e79949d58d200886d6d6d6d6d6d6d6d6d6d6d6d6d75686d6d6d6d6d6d7551").unwrap() + hex::decode("c0519dc0c800c88800c9529dc0c852c88852c9539dc0c853c88853c9549dc0c854c88854c9559dc0c855c88855c9569dc0c856c88856c9579d5c79009e63c0c858c88858c9599d67c0c858c88858c9599d68c0c857c88857c9589d597981009c5d79009c9b5b7981009c9b63c0d1008852cd00c78852d1008853cd52c78853d1008854cd53c78854d1008855cd54c78855d1008856cd55c78856d1008857cd56c78857d100885c79009e6359cd58c78859cc58c6a26959d158ce8859d358d09d59d258cf8867597981009e5c79009e9a6459cd58c78859d10088686858cd57c78858d100880302010054797ec1014e7f775b7981009c6301005f79009e63015177685e79009c6300cd53798800d10088760251207e5e797e01207e5d797e52797e7b757c67c0c859c88859c9009d00cd016a8800d100885acd5c79885ad1c0c8885ad3009d5ad20100885bd10088c45c9d760200207e5e797e01207ec0c87e52797e7b757c6875675e79009c635d79009c6300cd52798800d10088030051205d797e01207e5c797e787e7767c0c859c88859c9009d53795579950400e1f50596547978935479789455795279a06902aa20012060797e5d797e5c797eaa7e01877e5c798277009c6302a91401200111797e5c797ea97e01877e776800cd788800d1c0c88800d352799d00d2008859cd56798859d1c0c88859d353799d59d2008858c7827758c77853947f77527f758158c7527953945279947f77787f7576827700a06376517f75768176014b9f788b547982779f9a635279788b7f77537a757c6b7c6c5279517f757b757c6878016a8764016a537a757c6b7c6c686d67016a77685acd78885ad100885bd10088c45c9d035100200114797e01207e0113797e58797e587a757c6b7c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6c6d6d6d7568675d79009c6300cd52798800d10088035151205d797e01207e5c797e787e7767c0c859c88859c9009d00cd016a8800d100885acd5279885ad1c0c8885ad3009d5ad201ff885bd10088c45c9d03510020c0c87e01207e5c797e787e7768686802aa20c15a7f7552797eaa7e01877ec0cd886d67c0c859c88859c95a9d55ca827755ca7853947f77527f758155ca527953945279947f77787f75012302aa2001205f797e5d797e5b797eaa7e01877e7e5b798277009c63011702a914012060797e5b797ea97e01877e7e776800cd02aa20c15a7f7553797e54797eaa7e01877e8800d1008800ca827700ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686800ca537953945379945279947f7702aa20030200005d797e52797eaa7e01877e51cd8851d1008852ca827752ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686852ca537953945379945279947f7702aa20030200000111797e707c5a937f757e01207e01ff0119797eaa7e707c012b937f777eaa7e01877e52cd8852d1008853ca827753ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686853ca537953945379945279947f7702aa20030200000115797e52797eaa7e01877e53cd8853d1008854ca827754ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686854ca537953945379945279947f7702aa20030200000119797e52797eaa7e01877e54cd8854d1008857c7827757c77853947f77527f75815178529302ff00a063755367785293014ba0637552686857c7537953945379945279947f7755cd03020000011d797e52797e8855d1008803020000011c797e56cd8856cc58c6a26956d158ce8856d3011a799d56d2008856ca827756ca7853947f77527f758156ca527953945279947f77787f7557cd02aa20c15a7f7501207e01000128797eaa7e53797eaa7e01877e8857cc59c6a26957d159ce8857d359d09d57d259cf88525752807e011f797e58cd8858d158ce8858d358d0011e79949d58d200886d6d6d6d6d6d6d6d6d6d6d6d6d75686d6d6d6d6d6d7551").unwrap() }); static STORAGE_CONTRACT: LazyLock> = @@ -299,7 +298,8 @@ pub struct IdoPreInitParameters { #[serde_as(as = "serde_with::hex::Hex")] orbPoolParamsCategory: Vec, // Minimum swap fee of the permanent liquidity pool, baked into the - // postlaunch bytecode (a free parameter, not part of the announcement). + // postlaunch bytecode. Since the "CldIdo00" contract revision it is also + // carried in the preinit announcement; the two copies are cross-checked. #[serde_as(as = "IntegerAsStr")] permanentLiquidityMinFee: Integer, } @@ -711,6 +711,8 @@ fn build_partial_postlaunch_bytecode(params: &PartialPostlaunchParameters) -> Ve .to_bytes(), ); bytecode.extend_from_slice(&bigint_to_push_opcode(params.platformFee)); + // permanentLiquidityMinFee is a 2-byte announcement field, so it always fits + // this 2-byte push. let min_fee_bytes = encode_padded_vm_number(params.permanentLiquidityMinFee, 2).unwrap(); let execution_fee_bytes = encode_padded_vm_number(params.executionFee, 4).unwrap(); bytecode.extend_from_slice( @@ -725,76 +727,6 @@ fn build_partial_postlaunch_bytecode(params: &PartialPostlaunchParameters) -> Ve bytecode } -/// Parameters the preinit announcement does not carry but that are baked into -/// the partial postlaunch bytecode stored in output #7. They are extracted from -/// the stored bytecode itself; the caller then rebuilds the expected bytecode -/// with them and byte-compares, which validates every other baked field. -struct PartialPostlaunchExtractedParams { - permanentLiquidityMinFee: Integer, - /// display byte order (the stored push is VM order and gets reversed) - orbPoolParamsCategory: Vec, -} - -fn extract_params_from_partial_ido_initiator_bytecode( - partial_ido_initiator_bytecode: &[u8], -) -> Result { - // The partial ido initiator bytecode starts with a push of the partial - // postlaunch bytecode (see build_partial_ido_initiator_bytecode). - let script = ScriptBuf::from(partial_ido_initiator_bytecode.to_vec()); - let postlaunch_partial = match script.instructions().next() { - Some(Ok(Instruction::PushBytes(data))) => data.as_bytes().to_vec(), - _ => { - return Err(anyhow::anyhow!( - "partial ido initiator bytecode should start with a push of the partial postlaunch bytecode" - )) - } - }; - // pushes: [0] xTokenCategory(32), [1] permanentLiquidityShare, [2] price, - // [3] platformFeeNFTH(32), [4] platformFee, [5] permanentLiquidityMinFee(2), - // [6] orbPoolParamsCategory(32), [7] executionFee(4), then the contract - // body. executionFee is carried by the announcement, so only its shape is - // checked here; the full byte-compare of output #7 validates its value. - let pl_script = ScriptBuf::from(postlaunch_partial); - let inst_list: Vec<_> = pl_script.instructions().take(8).collect(); - if inst_list.len() < 8 { - return Err(anyhow::anyhow!( - "partial postlaunch bytecode has too few instructions" - )); - } - let min_fee = match &inst_list[5] { - Ok(Instruction::PushBytes(data)) if data.as_bytes().len() == 2 => { - vm_number_to_bigint(data.as_bytes()) - } - _ => { - return Err(anyhow::anyhow!( - "partial postlaunch bytecode: expecting a 2-byte permanentLiquidityMinFee push" - )) - } - }; - let orb_pool_cat = match &inst_list[6] { - Ok(Instruction::PushBytes(data)) if data.as_bytes().len() == 32 => { - data.as_bytes().iter().copied().rev().collect() - } - _ => { - return Err(anyhow::anyhow!( - "partial postlaunch bytecode: expecting a 32-byte orbPoolParamsCategory push" - )) - } - }; - match &inst_list[7] { - Ok(Instruction::PushBytes(data)) if data.as_bytes().len() == 4 => {} - _ => { - return Err(anyhow::anyhow!( - "partial postlaunch bytecode: expecting a 4-byte executionFee push" - )) - } - } - Ok(PartialPostlaunchExtractedParams { - permanentLiquidityMinFee: min_fee, - orbPoolParamsCategory: orb_pool_cat, - }) -} - fn build_partial_ido_initiator_bytecode( postLaunchPartialBytecode: &[u8], permanentPoolOTokenReserveOutpointIndex: &Integer, @@ -1285,16 +1217,17 @@ pub async fn has_indexed_tx(pool: &SqlitePool, txid: &[u8]) -> Result { // Prefix of the announcement OP_RETURN carried in the last output of a preinit // broadcast tx. -pub const IDO_PREINIT_ANNOUNCEMENT_SIGNATURE: &[u8; 15] = &[ - 0x6a, 0x4c, 0xbb, // OP_RETURN OP_PUSHDATA1 (187) - 0x43, 0x61, 0x75, 0x6c, 0x64, 0x72, 0x6f, 0x6e, 0x49, 0x64, 0x6f, 0x30, // CauldronIdo0 +pub const IDO_PREINIT_ANNOUNCEMENT_SIGNATURE: &[u8; 11] = &[ + 0x6a, 0x4c, 0xb9, // OP_RETURN OP_PUSHDATA1 (185) + 0x43, 0x6c, 0x64, 0x49, 0x64, 0x6f, 0x30, 0x30, // CldIdo00 ]; fn is_preinit_broadcast(tx: &Transaction) -> bool { // The announcement OP_RETURN is carried in the last output (the first output is the Delphi NFT). if let Some(out) = tx.output.last() { let bytes = out.script_pubkey.as_bytes(); - bytes.len() >= 15 && &bytes[0..15] == IDO_PREINIT_ANNOUNCEMENT_SIGNATURE + let sig = IDO_PREINIT_ANNOUNCEMENT_SIGNATURE; + bytes.len() >= sig.len() && &bytes[0..sig.len()] == sig } else { false } @@ -1443,13 +1376,13 @@ fn parse_ido_preinit_tx_params( match &instructions[1] { Ok(Instruction::PushBytes(data)) => { let data = data.as_bytes(); - if data.len() < 187 { + if data.len() < 185 { errors.push(anyhow::anyhow!("Incorrect announcement.mainData size")); return None; } let mut is_valid_ido = true; // preinit_parameters.offeredTokenTotalSupply < Integer::from(0) - let offered_token_is_in_supply = vm_number_to_bigint(&data[170..178]) < 0; + let offered_token_is_in_supply = vm_number_to_bigint(&data[168..176]) < 0; let offeringBcmrStorageOut = tx.output.get(8); if offeringBcmrStorageOut.is_none() { @@ -1490,35 +1423,48 @@ fn parse_ido_preinit_tx_params( } } - // permanentLiquidityMinFee and orbPoolParamsCategory are not part - // of the announcement; they are baked into the partial postlaunch - // bytecode stored in output #7 — extract them from there. The - // full byte-comparison of output #7 happens later in - // parse_ido_preinit_tx using these extracted values. - let extracted_postlaunch_params = match tx.output.get(7) { - Some(out) => { - match extract_data_from_storage_script_with_data_and_size( - &out.script_pubkey, - ) - .and_then(|data| extract_params_from_partial_ido_initiator_bytecode(&data)) - { - Ok(value) => value, - Err(err) => { - errors.push(anyhow::anyhow!( - "failed to extract postlaunch params from ido initiator bytecode storage: {}", - err - )); - return None; + // The ORB IdoParams NFT is spent via use() at input #1 and + // preserved at output #10. Its commitment is the only readable + // on-chain source for orbPoolParamsCategory: that value is baked + // into the partial postlaunch bytecode, but the postlaunch lives + // in output #7 as a P2SH32 hash, so it cannot be read back out. + // permanentLiquidityMinFee, by contrast, is carried in the + // announcement (data[166..168]). Both feed the output #7 rebuild + // in parse_ido_preinit_tx, which is what actually proves the ido + // initiator storage was built with these inputs. The preinit + // covenant does not reference the NFT on-chain, so enforcing the + // match is entirely the indexer's job; a missing NFT or any + // mismatched parameter means this is not an ORB IDO. + let mut ido_params_commitment: Option = None; + match tx.output.get(10).and_then(|out| out.token.as_ref()) { + Some(token) if token.has_nft() => { + let category: Vec = token.id.to_blob().iter().copied().rev().collect(); + if category != ido_params_category { + invalid_ido_reasons.push(anyhow::anyhow!( + "output#10 nft category != IDO_PARAMS_NFT_CATEGORY" + )); + is_valid_ido = false; + } else { + match parse_ido_params_nft_commitment(&token.commitment) { + Ok(value) => { + ido_params_commitment = Some(value); + } + Err(err) => { + invalid_ido_reasons.push(anyhow::anyhow!( + "failed to parse the ido params nft commitment: {}", + err + )); + is_valid_ido = false; + } } } } - None => { - errors.push(anyhow::anyhow!( - "ido initiator bytecode storage not defined!" - )); - return None; + _ => { + invalid_ido_reasons + .push(anyhow::anyhow!("ido params nft is missing at output#10")); + is_valid_ido = false; } - }; + } let preinit_parameters = IdoPreInitParameters { preInitBcmr: IdoPreInitBcmrParameters { @@ -1527,29 +1473,36 @@ fn parse_ido_preinit_tx_params( }, authguardLockingBytecode, offering: IdoParametersOffering { - platformFeeNFTH: data[12..44].to_vec(), - platformFeeNumerator: vm_number_to_bigint(&data[44..48]), - delphiCategory: data[48..80].to_vec(), + platformFeeNFTH: data[8..40].to_vec(), + platformFeeNumerator: vm_number_to_bigint(&data[40..44]), + delphiCategory: data[44..76].to_vec(), launchConditions: IdoParametersOfferingLaunchConditions { - expiresAt: vm_number_to_bigint(&data[80..86]), - deployThreshold: vm_number_to_bigint(&data[86..94]), - immediateDeployThreshold: vm_number_to_bigint(&data[94..102]), + expiresAt: vm_number_to_bigint(&data[76..82]), + deployThreshold: vm_number_to_bigint(&data[82..90]), + immediateDeployThreshold: vm_number_to_bigint(&data[90..98]), }, offer: IdoParametersOfferingOffer { - maxDiscountRateNumerator: vm_number_to_bigint(&data[102..106]), - discountAnnualRateNumerator: vm_number_to_bigint(&data[106..110]), - priceNumerator: vm_number_to_bigint(&data[110..126]), - minOffer: vm_number_to_bigint(&data[126..130]), - xTokenCategory: data[130..162].to_vec(), + maxDiscountRateNumerator: vm_number_to_bigint(&data[98..102]), + discountAnnualRateNumerator: vm_number_to_bigint(&data[102..106]), + priceNumerator: vm_number_to_bigint(&data[106..122]), + minOffer: vm_number_to_bigint(&data[122..126]), + xTokenCategory: data[126..158].to_vec(), }, - executionFee: vm_number_to_bigint(&data[162..166]), + executionFee: vm_number_to_bigint(&data[158..162]), }, - permanentLiquidityShareNumerator: vm_number_to_bigint(&data[166..170]), - offeredTokenTotalSupply: vm_number_to_bigint(&data[170..178]), - offeredTokenAmount: vm_number_to_bigint(&data[178..186]), - authguardNftOutputIndex: vm_number_to_bigint(&data[186..187]), - orbPoolParamsCategory: extracted_postlaunch_params.orbPoolParamsCategory, - permanentLiquidityMinFee: extracted_postlaunch_params.permanentLiquidityMinFee, + permanentLiquidityShareNumerator: vm_number_to_bigint(&data[162..166]), + offeredTokenTotalSupply: vm_number_to_bigint(&data[168..176]), + offeredTokenAmount: vm_number_to_bigint(&data[176..184]), + authguardNftOutputIndex: vm_number_to_bigint(&data[184..185]), + // From the ORB IdoParams NFT commitment (output #10). Absent + // when the NFT is missing/invalid, in which case is_valid_ido + // is already false and the output #7 rebuild will not match. + orbPoolParamsCategory: ido_params_commitment + .as_ref() + .map(|c| c.orbPoolParamsCategory.clone()) + .unwrap_or_default(), + // Carried in the announcement OP_RETURN (2 bytes). + permanentLiquidityMinFee: vm_number_to_bigint(&data[166..168]), }; // xToken must be a non-native token; an all-zero category (the @@ -1608,43 +1561,8 @@ fn parse_ido_preinit_tx_params( )); is_valid_ido = false; } - // The ORB IdoParams NFT must be included in the preinit: spent - // via use() at input #1 and preserved at output #10. Its - // commitment pins the economic parameters, and since the preinit - // covenant does not reference the NFT on-chain, enforcing the - // match is entirely the indexer's job. A missing NFT or any - // mismatched parameter means this is not an ORB IDO; parameters - // are never assumed from defaults. - let mut ido_params_commitment: Option = None; - match tx.output.get(10).and_then(|out| out.token.as_ref()) { - Some(token) if token.has_nft() => { - let category: Vec = token.id.to_blob().iter().copied().rev().collect(); - if category != ido_params_category { - invalid_ido_reasons.push(anyhow::anyhow!( - "output#10 nft category != IDO_PARAMS_NFT_CATEGORY" - )); - is_valid_ido = false; - } else { - match parse_ido_params_nft_commitment(&token.commitment) { - Ok(value) => { - ido_params_commitment = Some(value); - } - Err(err) => { - invalid_ido_reasons.push(anyhow::anyhow!( - "failed to parse the ido params nft commitment: {}", - err - )); - is_valid_ido = false; - } - } - } - } - _ => { - invalid_ido_reasons - .push(anyhow::anyhow!("ido params nft is missing at output#10")); - is_valid_ido = false; - } - } + // The IdoParams NFT commitment (parsed above) pins the economic + // parameters the announcement must agree with. 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 @@ -1683,14 +1601,11 @@ fn parse_ido_preinit_tx_params( )); is_valid_ido = false; } - if preinit_parameters.orbPoolParamsCategory - != ido_params.orbPoolParamsCategory - { - invalid_ido_reasons.push(anyhow::anyhow!( - "orbPoolParamsCategory baked in the postlaunch bytecode != ido_params.orbPoolParamsCategory" - )); - is_valid_ido = false; - } + // orbPoolParamsCategory is taken directly from this + // commitment (see preinit_parameters above), so there is + // nothing to compare here — the output #7 rebuild in + // parse_ido_preinit_tx proves the postlaunch bytecode was + // baked with it. // The permanent-liquidity minimums are pinned by the IdoParams // NFT (numerators over PERMANENT_LIQUIDITY_SHARE_DENOMINATOR, // directly comparable to permanentLiquidityShareNumerator). @@ -2006,6 +1921,10 @@ fn parse_ido_preinit_tx( } if let Some(idoInitiatorBytecodeStorageOut) = tx.output.get(7) { + // Rebuild output #7 from the announcement values plus the NFT-sourced + // orbPoolParamsCategory and compare the P2SH32 hash. This is the only + // way to verify the postlaunch bytecode baked into output #7 (which is + // stored as a hash) was built with the right inputs. let bytecode = build_storage_script_with_data_and_size( 1, &build_partial_ido_initiator_bytecode( @@ -3425,11 +3344,14 @@ mod tests { hex::decode("020000000a4416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a402000000fddb01514dd7010201008178c99dc8c0c8874dc801124361756c64726f6e49646f2d32303236513275088178c99dc8c0c88736827701209dc0519dc0ce01207f7500ce01207f758800cf517f755f845288c0cf517f7501208401008763c0c878c88876c9529d68755104002d310103404b4c059f06241f7e021c4800c0ce01207f75c0519c637600ce8800cf517f755f845188675879827701209dc0009d00d000d394765479a269765579950500e876481796005c7900a063577900a069587900a0695c79587995048033e1015a7995a169785d7995587995048033e1010400e1f5059596776e947b757c7800a0696854790087535e7900a06376608577687863760120857768005f7900a0635f795680547958807e77686e7e51d28851d15779517e8851d356799d02aa20012060797e5e797eaa7e01877e51cd8852796351cc5579a26952d100876452d101207f75577987916968c4539d6752d158798852d35579a26902aa20525152807e5f797eaa7e01877e52cd8853d100876453d101207f7557798791696854d100876454d101207f75577987916968c4559d6800cf517f77547f758100cc00c6527993a26900cd00c78800cf557f77547f758100cf557f75788b54807e00d28800ce00d1886d6d6d6d686d6d6d6d6d51c60175000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a401000000fd0a094d0709124361756c64726f6e49646f2d32303236513275000020000000000000000000000000000000000000000000000000000000000000000020bfa57ca201b401db5496995a69a333e9354e8782fbf176eabec3f891a29aa94700004cb520314e518b4207e9252edc443abea7dede08a93165975a451fd802a847aee86f1c0602c400024a0151894c87011701504c814768747470733a2f2f7733732e6c696e6b2f697066732f75415655534944464f55597443422d6b6c4c7478454f72366e337434497154466c6c31704648396743714565753647386338697066733a2f2f75415655534944464f55597443422d6b6c4c7478454f72366e337434497154466c6c31704648396743714565753647386352894df5010902a9147ca97e01877e57897c6b00c08851d100887c635ab2756d51cd016a88674d0301404142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f6b007c8253a26365537f7c76011299527f77816c766b7c7f77517f75785c99527f77013f84816c766b7c7f77517f757e785699527f77013f84816c766b7c7f77517f757e7c527f77013f84816c766b7c7f77517f757e7b7c7e7c82539f666882760087636d677d537c94007c807e76011299527f77816c766b7c7f77517f75785c99527f77013f84816c766b7c7f77517f757e785699527f77013f84816c766b7c7f77517f757e7c527f77013f84816c766b7c7f77517f757e7c51937f757e686c7554893a10303132333435363738396162636465667c006b65517f7c5279785f84817f77517f756c7e6b52797c5499817f77517f756c7e6b82009c666d6c5689097f7b827b7c7f777e7e538978a8886b518ac0ce568a65766c537a538a6b74519c66756c766ba804015512207c7e548a01757c7e6b528a6c7c6b65766c537a538a6b74519c66756c6ca87c7e076a0442434d52207c7e51cd886801206c0f51ce8851d0009d6300cdc0c78868517e7e578a00cd8800ce00d18800d2008800d000d38800c600cca26952d10088c453870f51ce8851d0009d6300cdc0c7886851088178c99dc8c0c8870480c3c90104800fd92d0500e40b5402c0519dc0c800c88800c9529dc0c852c88852c9539dc0c853c88853c9549dc0c854c88854c9559dc0c855c88855c9569dc0c856c88856c9579d5c79009e63c0c858c88858c9599d67c0c858c88858c9599d68c0c857c88857c9589d597981009c5d79009c9b5b7981009c9b63c0d1008852cd00c78852d1008853cd52c78853d1008854cd53c78854d1008855cd54c78855d1008856cd55c78856d1008857cd56c78857d100885c79009e6359cd58c78859cc58c6a26959d158ce8859d358d09d59d258cf8867597981009e5c79009e9a6459cd58c78859d10088686858cd57c78858d1008802aa200302010055797eaa7e01877ec101587f775b7981009c6301005f79009e63015177685e79009c6300cd53798800d10088760251207e5e797e01207e5d797e52797e7b757c67c0c859c88859c9009d00cd016a8800d100885acd5c79885ad1c0c8885ad3009d5ad20100885bd10088c45c9d760200207e5e797e01207ec0c87e52797e7b757c6875675e79009c635d79009c6300cd52798800d10088030051205d797e01207e5c797e787e7767c0c859c88859c9009d53795579950400e1f50596547978935479789455795279a06902aa20012060797e5d797e5c797eaa7e01877e5c798277009c6302a91401200111797e5c797ea97e01877e776800cd788800d1c0c88800d352799d00d2008859cd56798859d1c0c88859d353799d59d2008858c7827758c77853947f77527f758158c7527953945279947f77787f7576827700a06376517f75768176014b9f788b547982779f9a635279788b7f77537a757c6b7c6c5279517f757b757c6878016a8764016a537a757c6b7c6c686d67016a77685acd78885ad100885bd10088c45c9d035100200114797e01207e0113797e58797e587a757c6b7c6b7c6b7c6b7c6b7c6b7c6c6c6c6c6c6c6d6d6d7568675d79009c6300cd52798800d10088035151205d797e01207e5c797e787e7767c0c859c88859c9009d00cd016a8800d100885acd5279885ad1c0c8885ad3009d5ad201ff885bd10088c45c9d03510020c0c87e01207e5c797e787e7768686802aa20c101147f7552797eaa7e01877ec0cd886d67c0c859c88859c95a9d55ca827755ca7853947f77527f758155ca527953945279947f77787f75012302aa2001205f797e5d797e5b797eaa7e01877e7e5b798277009c63011702a914012060797e5b797ea97e01877e7e776800cd02aa20c101147f7553797e54797eaa7e01877e8800d1008800ca827700ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686800ca537953945379945279947f7702aa20030200005d797e52797eaa7e01877e51cd8851d1008852ca827752ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686852ca537953945379945279947f7702aa20030200000111797e707c0114937f757e01207e01187901ff7eaa7e707c0135937f777eaa7e01877e52cd8852d1008853ca827753ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686853ca537953945379945279947f7702aa20030200000115797e52797eaa7e01877e53cd8853d1008854ca827754ca7853947f77527f75815178529302ff00a063755367785293014ba0637552686854ca537953945379945279947f7702aa20030200000119797e52797eaa7e01877e54cd8854d1008857c7827757c77853947f77527f75815178529302ff00a063755367785293014ba0637552686857c7537953945379945279947f7755cd03020000011d797e52797e8855d1008802aa2003020000011d797eaa7e01877e56cd8856cc58c6a26956d158ce8856d3011a799d56d2008856ca827756ca7853947f77527f758156ca527953945279947f77787f7557cd02aa20c101147f7501207e01277901007eaa7e53797eaa7e01877e8857cc59c6a26957d159ce8857d359d09d57d259cf8802aa20525752807e0120797eaa7e01877e58cd8858d158ce8858d358d0011e79949d58d200886d6d6d6d6d6d6d6d6d6d6d6d6d75686d6d6d6d6d6d7551000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a403000000fdb503514db1030201008178c99dc8c0c8874da203124361756c64726f6e49646f2d3230323651327520000000000000000000000000000000000000000000000000000000000000000020ab3aba01311b672808aa06f5e7f332d930e4ed0c99407343f7fc5d743d15ff5504809698003dc0ce00ce01207f758800cf517f755f84528800cf577f77547f758151a169c0cf517f7701207f7502aa2001207b7e7b7eaa7e01877e52cd8852ccc0c6a2088178c99dc8c0c8873fc0cf527f7701207f75c0cf01227f77815479ce01207f757b88537acf567f75817600a0699f6978ce7bcf7eaa88c0cf517f77517f7581c0c85279c8887cc99c0778ce7bcf7eaa87209a66f2918e9a845e263d71d126949ca0ef35476eb13d382482aacfc94082941b046cac326a021c48021c48c0009d00c852c88852c9529d00c854c88854c9549d00c855c88855c9559d00ce01207f7551ce78527e8851cf517f755f8401008853ce01207f7555798853cf567f75817600a06953d100876453d101207f7552798791696851d0547aa07651d0557aa09b63537952799f696851cf557f77547f758100a16302aa2001205d797e57797eaa7e01877e556085537956807e0058807e52d058807e0058807e00d28800d154798800d3009d00cd788851cd788851cc52c6a26951d152ce8851d352d09d51d2008852d10088c4549d75675279517e00d18800d3009d766355ca827755ca7853947f77527f758155ca527953945279947f77787f75526085557956807e51cf557f77547f757e0058807e52d058807e0058807e00d28802aa20c101147f755a79827751807e5a797e01207e60797e52797eaa7e01877e00cd8802aa20012060797e5a797eaa7e01877e52cd8852cc52c6a26952d152ce8852d352d09d52d2008854d10088c4559d6d756754ca827754ca7853947f77527f758154ca527953945279947f77787f7551d07600a06354d152ce8854d3789d54d2008802aa2001200111797e5b797eaa7e01877e54cd8855d10088c4569d6754d10088c4559d6852567956807e51cf557f77547f757e0058807e7858807e0058807e00d28802aa20c101147f755d79827751807e5d797e5c79827751807e5c797e5b79827751807e5b797e01207e5a797e547e5f797e01207e60797e01207e0111797e53797eaa7e01877e00cd8852d152ce8852d352d05279949d52cc52c6a26902aa20520052807e5d797eaa7e01877e52cd8852d200886d6d6802aa2056798277518057797e5a797eaa7e01877e51cd88545b797e51d28851cc51c6a26951d153798851d3009d686d6d6d6d6d6d51a00375000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a404000000fdb902514db5020201008178c99dc8c0c8874da602c0009d00c852c88852c9529d00cf517f77567f758100ce01207f7551ce01207f75788851cf517f755f84538851cf517f7551ca51ca82770136940120947f7701207f750000537960840100876451cf517f77567f75817b757c51cf577f77587f758177687800a2697600a26952d352d051d0949d587a810000567901208401008764527900a26951c6765479950400e1f50596537a757c6b7c6c765379947b757c53cc5279a26953d1008854cc5379a26954d10088756753d05379950400e1f505967b757c53d0527994777600a06353d3789d53d153ce886753d10088687800a06354d352799d54d153ce886754d100886868547900a06302aa20012057797e5f797eaa7e01877e51cd8851d1587988565551807e5c797e597956799356807e51d28851d3009d55d351d09d55d152ce8802aa20525152807e60797eaa7e01877e55cd8855d200886751d351d09d51d152ce8802aa20012057797e5e797eaa7e01877e51cd8851d20088687600a06302aa2001205b797e5e797eaa7e01877e53cd886753cd016a88687c00a06302aa2001205b797e5d797eaa7e01877e54cd886754cd016a886800cf577f77547f75817651a06300cf517f7500cf517f77567f757e788c54807e00cf5b7f77587f758153799358807e00cf01137f77587f757e00cf011b7f77587f758155799358807e00d28800ce00d18800c700cd8800d000d39d52cf52d28852ce52d18852c752cd88675500cf517f77567f757e00cf5b7f77587f758153799358807e00cf01137f77587f757e00cf011b7f77587f758155799358807e00d28800d158798800d3009d02aa2001205b797e5e797eaa7e01877e00cd8852d100885457790120840100876475536876ce59798876cf517f755f845488756855557a00a063755668c4789e6376d10088c4788b9d686d6d6d6d6d6d6d7551a40275000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a405000000fd4701514d43010201008178c99dc8c0c8874d3401c0009d00ce01207f7551ce01207f75788851cf517f755f84538851ca51ca82770136940120947f7701207f7551cf517f75760120840100876451cc51c6a26951d100886751d352d09d51d152ce886802aa200120537a7e55797eaa7e01877e51cd8800cf577f77547f75817651a06300cf517f7500cf517f77567f757e788c54807e00cf5b7f77587f757e00cf01137f77587f757e00cf011b7f77587f757e00d28800ce00d18800c700cd88c4529e6352d10088c4539d686755608500cf517f77567f757e00cf5b7f77587f757e00cf01137f77587f757e00cf011b7f77587f757e00d288527900d18800d3009d02aa20012055797e56797eaa7e01877e00cd8852d100885352790120840100876475526876ce54798876cf517f755f845488c4539e6353d10088c4549d6875686d6d7551320175000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a406000000fd0f03514d0b030201008178c99dc8c0c8874dfc02088178c99dc8c0c887575655545352510450c30000c0c9009dc0c85b79c8885a79c9577a9d5979ce827701209d567900a263c0c85b79c8885a79c957799d68c0c800d1788800d2578800d3009d00cd5a7a88c08bc0c878c88876c9547a9d76ca827778ca7853947f77527f75817bca7b53945279947f777c7f75c05293c0c878c88876c9557a9d76ca827778ca7853947f77527f75817bca7b53945279947f777c7f75c05393c0c878c88876c9567a9d76ca827778ca7853947f77527f75815178529302ff00a063755367785293014ba063755268685379ca537a5394537a947b947f7702aa20525352807e5b797e7b7eaa7e01877e54cd8854cc7cc6a26954d10088c05493c0c878c88876c9567a9d76ca827778ca7853947f77527f75815178529302ff00a063755367785293014ba063755268685379ca537a5394537a947b947f7702aa20525352807e5a797e7b7eaa7e01877e55cd8855cc7cc6a26955d100885779d07600a0690100557a7e04000000007e51d28851d15479527e8851d3789d51cd02aa20547aaa7e01877e885153d28853d153798853d3009d53cd02aa20537aaa7e01877e8802aa20525352807e567a7eaa7e01877e52cd8852cc5579c6a26952d1557ace8852d39d52d20088c05593c0c878c88876c9537a9d76c7827778c77853947f77527f75817bc77b53945279947f777c7f7576827700a06376517f75768176014b9f788b547982779f9a635279788b7f77537a757c6b7c6c5279517f757b757c6878016a8764016a537a757c6b7c6c686d67016a776856cd8856d1008857d100876457d101207f75788791696857cd567f75066a0442434d52879169c4589e6358d100876458d101207f75788791696858cd567f75066a0442434d52879169c4599e6359d100876459d101207f75788791696859cd567f75066a0442434d52879169c45a9e635ad10087645ad101207f7578879169685acd567f75066a0442434d52879169c45b9e635bd10087645bd101207f7578879169685bcd567f75066a0442434d52879169c45c9d686868686d7551fa0275000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a407000000fd2506514d21060201008178c99dc8c0c8874d12064d09050480c3c901059f06241f7e5879009c63c0009dc0c9009e69c0cdc0c788c0ccc0c6a269c0d1c0ce88c0d3c0d09dc0d2c0cf8802aa20520052807e597a7eaa7e01877ec0c851c88851c9589d7651cd8851cc51c6a26951d151ce8851d351d09dc0c852c88852c9599d52cd8852cc52c6a26952d152ce8852d352d09dc353a06353ce0088c3549d6853d10088c4549d6d6d6d6d51675879519c63c0009dc0c9009dc0cdc0c788c0d1c0ce88c0d3c0d09dc0d2c0cf88c0c852c88852c9529d52cd52c78852cc52c6a26952d152ce8852d352d09d02aa200120c0cec0cf7eaa7e587a7eaa7e01877e00005376c75479876376ce59798763527978d093537a757c6b7c6c6776ce0088686ec6937b757c6776ce008868768bc378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c378a06376c75579876376ce5a798763537978d093547a757c6b7c6b7c6c6c6776ce008868527978c693537a757c6b7c6c6776ce008868768b77c3789d6868686868686868c0ccc0c6547a93a269c0c851c88851c9519d51cd51c78851cc51c6a26951d0537a937600a06351d159798851d378a26951d200886751d100886853d10088c4549d6d6d6d6d6d6d5167587a529dc0009dc0c9009d5379827701209d53ce567a8853cf517f755f845588c0c653cf577f77587f7581a269c0c851c88851c9519d53cf5f7f77587f75817600a06351d078a2696851d000a06351ce5679886851cf0088c0c852c88852c9529d52ce567988000053cf517f75608401008763557981537994765679950400e1f505967652d0a06352d07768765679950500e87648179653cf01177f77587f758194760500e8764817955779967802e803a27800a09a6378567a757c6b7c6b7c6b7c6b7c6c6c6c6c765379a16376557a757c6b7c6b7c6b7c6c6c6c675279557a757c6b7c6b7c6b7c6c6c6c68686d6d68c0c651c69352c69352799451d052d093527994537900a0537900a09a6302aa2005746376a914000114807e2b88ac67c0d1c0ce88c25288c0cdc0c788c0c6c0d095c0c6c0cc9490539502e80396c0cc7c94c0d3957ca2687eaa7e01877e00cd8800cc54799d00d15a798800d353799d00d20088096a0653554d4d4f4e14000114807e51cd8851cc009d51d100886700d1008800cd016a8851d1008851cd016a886802aa2001205a7a7e5b7a7eaa7e01877e52cd8852cc7ba2697600a06352d378a26952d158798852d200886752d100886853d10088c4549d6d6d6d6d75516868088178c99dc8c0c8870778ce7bcf7eaa875800c0ce827701209dc0cf827700a0695579827701209dc0c85779c8885679c99dc0c85779c8885679c99d5479529376ca827778ca7853947f77527f75817bca7b53945279947f777c7f7501157f7701207f75c0cec0cf7eaa88547ac852d1827701209d52d300a06902aa20c101147f755479827751807e54797e5379827751807e537a7e01207e7b7e01207e52d17e01207e547a7e587e52d358807e537a7eaa7e01877e57cd8857ccc0c6a26957d1c0ce8857d2c0cf8857d3c0d09d02aa20525752807e7b7eaa7e01877e7658cd8858cc02e803a26958d1008859cd8859cc78c6a26959d178ce8859d278cf8859d37cd09c100675000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a4080000000151000000004416b98e44076018160375d7b46f243739c5eb945bfa5d0a6b97332a7da4b7a409000000015100000000d9d78914b49e4313f22b27e1ea03dd7c2963775ce944e0b81c1ad71490e865710100000064411de3a0757e6c479e5c20ca63f5d7f6e2029f27d7349d61be689a99a655afd2024b4d23d1e736d7119ed226dbd66597521e2fd2b8be6f640e3e0b51cc2ce0a1fe612102c1547ca5906ae616000ff9e82a8808ae72e3b10280ea388bc4c53c698e1b72a7ffffffff0be80300000000000023aa204358e719285ca59fffedcff34a5d1a39b32711ec2ad21c51765aeaa79d60409887e80300000000000023aa20890580b8a7a29eca5c414ddf82057cb65924950acb189661cf2727136eb4a49887e80300000000000023aa20a8e6aed82ff79e824aa86d14cc469704bda8e8c20057ff06d4948004e6ade0d287e80300000000000023aa20de0a3c5fe9a1d0fea9ca1d0e82628ab14c61214d17d3f9df4b175fec46a52b5e87e80300000000000023aa20f69ff67df71b9ecd4b6b39af393551468bb2a46fb261e2e5a61cfb27dbcbb25087e80300000000000023aa20e5b1d9865bf78f7cfc02e60ba0bcf327e0d3949f455a37dd8870979c3fc0f42d87e80300000000000023aa208b3c520007a78d00dcebfd2e8bcda6020ed7f7ed7d5f0d687ac325b0fc3e309187e80300000000000023aa20beafa293c3d712876b389da252b1f7f584a8f8b10da2e526e38a34856f1c15e487b0040000000000000f0201008178c99dc8c0c88702000075b004000000000000c50201008178c99dc8c0c8874cb70c0602c400024a0104011701506a0442434d5220314e518b4207e9252edc443abea7dede08a93165975a451fd802a847aee86f1c4768747470733a2f2f7733732e6c696e6b2f697066732f75415655534944464f55597443422d6b6c4c7478454f72366e337434497154466c6c31704648396743714565753647386338697066733a2f2f75415655534944464f55597443422d6b6c4c7478454f72366e337434497154466c6c317046483967437145657536473863b50075586e0400000000001976a91495570c60bbdeaf648a259bb177911169e060a76788ac00000000").unwrap() }); + // The fixture carries the legacy "CauldronIdo0" announcement signature. The + // "CldIdo00" contract revision shortened it, so the detector must no longer + // recognize the old announcement. There is no backward compatibility. #[test] - fn preinit_detect() { + fn legacy_preinit_announcement_not_detected() { let tx: Transaction = bitcoincash::consensus::deserialize(&PREINIT_TEST_TX01).expect("should be valid"); - assert!(is_preinit_broadcast(&tx)); + assert!(!is_preinit_broadcast(&tx)); } // The fixture predates the ORB IdoParams generation (native-BCH xToken, no @@ -3481,14 +3403,17 @@ mod tests { assert!(parse_ido_params_nft_commitment(&commitment[..89]).is_err()); } - // Round-trip: build the partial ido initiator bytecode, then extract the - // two parameters the announcement does not carry back out of it. + // build_partial_postlaunch_bytecode bakes permanentLiquidityMinFee as a + // 2-byte push and orbPoolParamsCategory as a 32-byte push reversed into VM + // order, in the positions the postlaunch covenant expects. This is the + // layout the output #7 rebuild relies on to prove the storage was built with + // the announcement's minFee and the NFT's orbPoolParamsCategory. #[test] - fn extract_postlaunch_params_roundtrip() { + fn partial_postlaunch_bakes_min_fee_and_orb_category() { let x_token_category = vec![0x11u8; 32]; let orb_pool_params_category: Vec = (0u8..32).collect(); let min_fee = Integer::from(1000i64); - let partial_postlaunch = build_partial_postlaunch_bytecode(&PartialPostlaunchParameters { + let partial = build_partial_postlaunch_bytecode(&PartialPostlaunchParameters { xTokenCategory: &x_token_category, permanentLiquidityShare: &Integer::from(20_000_000i64), price: &Integer::from(1_700_000_000_000i64), @@ -3498,15 +3423,24 @@ mod tests { orbPoolParamsCategory: &orb_pool_params_category, executionFee: &Integer::from(100_000i64), }); - let partial_initiator = build_partial_ido_initiator_bytecode( - &partial_postlaunch, - &Integer::from(8i64), - &Integer::from(0i64), - ); - let extracted = extract_params_from_partial_ido_initiator_bytecode(&partial_initiator) - .expect("should extract"); - assert_eq!(extracted.permanentLiquidityMinFee, min_fee); - assert_eq!(extracted.orbPoolParamsCategory, orb_pool_params_category); + let script = ScriptBuf::from(partial); + let inst: Vec<_> = script.instructions().take(8).collect(); + // [5] permanentLiquidityMinFee: a 2-byte push carrying the fee value. + match &inst[5] { + Ok(Instruction::PushBytes(d)) => { + assert_eq!(d.as_bytes().len(), 2); + assert_eq!(vm_number_to_bigint(d.as_bytes()), min_fee); + } + _ => panic!("expected a 2-byte permanentLiquidityMinFee push"), + } + // [6] orbPoolParamsCategory: 32 bytes, reversed into VM order. + match &inst[6] { + Ok(Instruction::PushBytes(d)) => { + let restored: Vec = d.as_bytes().iter().copied().rev().collect(); + assert_eq!(restored, orb_pool_params_category); + } + _ => panic!("expected a 32-byte orbPoolParamsCategory push"), + } } #[test]