ido: widen the announcement minOffer field to 8 bytes

Tracks the libriften change growing the preinit announcement main data
push from 185 to 189 bytes: minOffer now reads at [122..130) and every
later field shifts by 4. The announcement signature's OP_PUSHDATA1
length byte moves to 0xbd, so only new-format announcements are
detected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hossein Zoda 2026-08-07 03:36:44 +00:00
parent c2b1b5be09
commit eaa977f98a

View file

@ -1249,7 +1249,7 @@ pub async fn has_indexed_tx(pool: &SqlitePool, txid: &[u8]) -> Result<bool> {
// Prefix of the announcement OP_RETURN carried in the last output of a preinit
// broadcast tx.
pub const IDO_PREINIT_ANNOUNCEMENT_SIGNATURE: &[u8; 11] = &[
0x6a, 0x4c, 0xb9, // OP_RETURN OP_PUSHDATA1 (185)
0x6a, 0x4c, 0xbd, // OP_RETURN OP_PUSHDATA1 (189)
0x43, 0x6c, 0x64, 0x49, 0x64, 0x6f, 0x30, 0x30, // CldIdo00
];
@ -1407,13 +1407,13 @@ fn parse_ido_preinit_tx_params(
match &instructions[1] {
Ok(Instruction::PushBytes(data)) => {
let data = data.as_bytes();
if data.len() < 185 {
if data.len() < 189 {
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[168..176]) < 0;
let offered_token_is_in_supply = vm_number_to_bigint(&data[172..180]) < 0;
let offeringBcmrStorageOut = tx.output.get(8);
if offeringBcmrStorageOut.is_none() {
@ -1461,7 +1461,7 @@ fn parse_ido_preinit_tx_params(
// 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
// announcement (data[170..172]). 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
@ -1518,23 +1518,23 @@ fn parse_ido_preinit_tx_params(
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]),
minOffer: vm_number_to_bigint(&data[122..130]),
// On-chain in VM order; kept in display/UI order. An
// all-zero category is the native-BCH sentinel and
// normalizes to None (libriften
// isNativeXTokenCategory).
xTokenCategory: if data[126..158].iter().all(|&b| b == 0) {
xTokenCategory: if data[130..162].iter().all(|&b| b == 0) {
None
} else {
Some(data[126..158].iter().copied().rev().collect())
Some(data[130..162].iter().copied().rev().collect())
},
},
executionFee: vm_number_to_bigint(&data[158..162]),
executionFee: vm_number_to_bigint(&data[162..166]),
},
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]),
permanentLiquidityShareNumerator: vm_number_to_bigint(&data[166..170]),
offeredTokenTotalSupply: vm_number_to_bigint(&data[172..180]),
offeredTokenAmount: vm_number_to_bigint(&data[180..188]),
authguardNftOutputIndex: vm_number_to_bigint(&data[188..189]),
// 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.
@ -1543,7 +1543,7 @@ fn parse_ido_preinit_tx_params(
.map(|c| c.permanentPoolPlatformNfth.clone())
.unwrap_or_default(),
// Carried in the announcement OP_RETURN (2 bytes).
permanentLiquidityMinFee: vm_number_to_bigint(&data[166..168]),
permanentLiquidityMinFee: vm_number_to_bigint(&data[170..172]),
};
// The xToken may be a real token or native BCH (`None`, the