Getting Started

0. Compile ink! contract with debug info

For cargo-contract, see 3.2.

1. Introduce Trap in inkpad


#![allow(unused)]
fn main() {
/// A trap code describing the reason for a trap.
///
/// All trap instructions have an explicit trap code.
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TrapCode {
    /// The current stack space was exhausted.
    StackOverflow,

    /// An out-of-bounds memory access.
    MemoryOutOfBounds,

    /// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
    HeapMisaligned,

    /// An out-of-bounds access to a table.
    TableOutOfBounds,

    /// Indirect call to a null table entry.
    IndirectCallToNull,

    /// Signature mismatch on indirect call.
    BadSignature,

    /// An integer arithmetic operation caused an overflow.
    IntegerOverflow,

    /// An integer division by zero.
    IntegerDivisionByZero,

    /// Failed float-to-int conversion.
    BadConversionToInteger,

    /// Code that was supposed to have been unreachable was reached.
    UnreachableCodeReached,

    /// Execution has potentially run too long and may be interrupted.
    Interrupt,

    /// HostError
    HostError(Box<Error>),

    // Unknown Error
    Unknown,

    // Termination
    Termination,

    // Restoration
    Restoration,
}

}

inkpad supports Trap both from wasmi and wasmtime

2. Embed panic in contract


#![allow(unused)]
#![cfg_attr(not(feature = "std"), no_std)]

fn main() {
use ink_lang as ink;

#[ink::contract]
mod flipper_trap {
    #[ink(storage)]
    pub struct FlipperTrap {
        value: bool,
    }

    impl FlipperTrap {
        #[ink(constructor)]
        pub fn new(init_value: bool) -> Self {
            Self { value: init_value }
        }

        #[ink(constructor)]
        pub fn default() -> Self {
            Self::new(Default::default())
        }

        #[ink(message)]
        pub fn flip(&mut self) {
            panic!("trap here");
            self.value = !self.value;
        }

        /// Simply returns the current value of our `bool`.
        #[ink(message)]
        pub fn get(&self) -> bool {
            self.value
        }
    }
}

}

3. Catch the trap in contract


#![allow(unused)]
fn main() {
#[test]
fn test_flipper_trap() {
    // Assume we compile the contract in `2.` to `flipper_trap.contract`
    let mut rt = Runtime::contract(
        include_bytes!("flipper_trap.contract"),
        Some(Instance),
    )
    .expect("Create runtime failed");

    rt.deploy("default", vec![], None).expect("Deploy failed");
    assert_eq!(rt.call("get", vec![], None), Ok(Some(vec![0])));

    if let Some(inkpad_runtime::Error::CallContractFailed {
        error: inkpad_executor::Error::Trap(Trap { code, .. }),
    }) = rt.call("flip", vec![], None).err()
    {
        assert_eq!(code, TrapCode::UnreachableCodeReached);
    } else {
        panic!("Call flipper_trap with unexpected error");
    }
}

}