ink_primitives/
abi.rs

1// Copyright (C) ink! contributors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Abstractions for ABI representation and encoding/decoding.
16
17use ink_prelude::vec::Vec;
18
19use crate::sol::{
20    SolDecode,
21    SolEncode,
22};
23
24/// ABI spec for encoding/decoding contract calls.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub enum Abi {
27    /// ink! ABI spec (uses Parity's SCALE codec for input/output encode/decode).
28    Ink,
29    /// Solidity ABI encoding.
30    Sol,
31}
32
33/// Marker type for ink! ABI and SCALE encoding.
34///
35/// Used with [`AbiEncodeWith`], [`AbiDecodeWith`] and `DecodeMessageResult`.
36#[derive(Debug, Default, Clone, Copy)]
37pub struct Ink;
38
39/// Marker type for Solidity ABI.
40///
41/// Used with [`AbiEncodeWith`], [`AbiDecodeWith`] and `DecodeMessageResult`.
42#[derive(Debug, Default, Clone, Copy)]
43pub struct Sol;
44
45/// Trait for ABI-specific encoding with support for both slice and vector buffers.
46pub trait AbiEncodeWith<Abi> {
47    /// Encodes the data into a new vector.
48    fn encode_with(&self) -> Vec<u8>;
49
50    /// Encodes the data into a fixed-size buffer, returning the number of bytes written.
51    fn encode_to_slice(&self, buffer: &mut [u8]) -> usize;
52
53    /// Encodes the data into a dynamically resizing vector.
54    fn encode_to_vec(&self, buffer: &mut Vec<u8>);
55
56    /// Encodes the value as a topic (i.e. an indexed event parameter).
57    ///
58    /// # Note
59    ///
60    /// The provided hashing function depends on the ABI i.e.
61    /// - BLAKE2b for ink! ABI
62    /// - Keccak-256 for Solidity ABI
63    fn encode_topic<H>(&self, hasher: H) -> [u8; 32]
64    where
65        H: Fn(&[u8], &mut [u8; 32]);
66}
67
68/// Trait for ABI-specific decoding.
69pub trait AbiDecodeWith<Abi>: Sized {
70    /// The error type that can occur during decoding.
71    type Error: core::fmt::Debug;
72
73    /// Decodes the data from a buffer using the provided ABI.
74    fn decode_with(buffer: &[u8]) -> Result<Self, Self::Error>;
75}
76
77impl<T: scale::Encode> AbiEncodeWith<Ink> for T {
78    fn encode_with(&self) -> Vec<u8> {
79        scale::Encode::encode(self)
80    }
81
82    fn encode_to_slice(&self, buffer: &mut [u8]) -> usize {
83        let mut sized_output = SizedOutput::from(buffer);
84        scale::Encode::encode_to(self, &mut sized_output);
85        sized_output.len()
86    }
87
88    fn encode_to_vec(&self, buffer: &mut Vec<u8>) {
89        scale::Encode::encode_to(self, buffer);
90    }
91
92    fn encode_topic<H>(&self, hasher: H) -> [u8; 32]
93    where
94        H: Fn(&[u8], &mut [u8; 32]),
95    {
96        let encoded = scale::Encode::encode(self);
97        let len = encoded.len();
98        let mut output = [0u8; 32];
99        if encoded.len() <= 32 {
100            output.as_mut()[0..len].copy_from_slice(&encoded);
101        } else {
102            hasher(&encoded, &mut output);
103        }
104        output
105    }
106}
107
108impl<T: scale::Decode> AbiDecodeWith<Ink> for T {
109    type Error = scale::Error;
110    fn decode_with(buffer: &[u8]) -> Result<Self, Self::Error> {
111        scale::Decode::decode(&mut &buffer[..])
112    }
113}
114
115impl<T> AbiEncodeWith<Sol> for T
116where
117    T: for<'a> SolEncode<'a>,
118{
119    fn encode_with(&self) -> Vec<u8> {
120        SolEncode::encode(self)
121    }
122
123    fn encode_to_slice(&self, buffer: &mut [u8]) -> usize {
124        SolEncode::encode_to(self, buffer)
125    }
126
127    fn encode_to_vec(&self, buffer: &mut Vec<u8>) {
128        buffer.extend(SolEncode::encode(self));
129    }
130
131    fn encode_topic<H>(&self, hasher: H) -> [u8; 32]
132    where
133        H: Fn(&[u8], &mut [u8; 32]),
134    {
135        SolEncode::encode_topic(self, hasher)
136    }
137}
138
139impl<T: SolDecode> AbiDecodeWith<Sol> for T {
140    type Error = crate::sol::Error;
141    fn decode_with(buffer: &[u8]) -> Result<Self, Self::Error> {
142        T::decode(buffer)
143    }
144}
145
146/// A `scale::Output` implementing buffer that tracks the number of bytes written.
147pub struct SizedOutput<'a> {
148    buffer: &'a mut [u8],
149    offset: usize,
150}
151
152impl<'a> From<&'a mut [u8]> for SizedOutput<'a> {
153    fn from(buffer: &'a mut [u8]) -> Self {
154        Self { buffer, offset: 0 }
155    }
156}
157
158#[allow(clippy::len_without_is_empty)]
159impl<'a> SizedOutput<'a> {
160    /// Returns the number of bytes written to the buffer.
161    pub fn len(&self) -> usize {
162        self.offset
163    }
164}
165
166impl scale::Output for SizedOutput<'_> {
167    fn write(&mut self, bytes: &[u8]) {
168        let start = self.offset;
169        let len = bytes.len();
170        debug_assert!(
171            len <= self.buffer.len(),
172            "output buffer overflowed, encoded len is {} but buffer len is {}",
173            len,
174            self.buffer.len()
175        );
176        self.buffer[start..start.checked_add(len).unwrap()].copy_from_slice(bytes);
177        self.offset = self.offset.checked_add(len).unwrap();
178    }
179}