1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Contract function call builder.

use std::string::ToString;

use crate::signature::short_signature;
use crate::{decode, encode, Bytes, Error, Param, ParamType, Result, Token};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
/// The state mutability of the function
/// https://solidity.readthedocs.io/en/v0.6.6/abi-spec.html#json
pub enum StateMutability {
	/// Specified to not read blockchain state
	Pure,
	/// Specified to not modify the blockchain state
	View,
	/// Function does not accept Ether
	Nonpayable,
	/// Function accepts Ether
	Payable,
}

/// Contract function specification.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Function {
	/// Function name.
	pub name: String,
	/// Function input.
	pub inputs: Vec<Param>,
	/// Function output.
	pub outputs: Vec<Param>,
	/// Function state mutability
	pub state_mutability: StateMutability,
}

impl Function {
	/// Returns all input params of given function.
	fn input_param_types(&self) -> Vec<ParamType> {
		self.inputs.iter().map(|p| p.kind.clone()).collect()
	}

	/// Returns all output params of given function.
	fn output_param_types(&self) -> Vec<ParamType> {
		self.outputs.iter().map(|p| p.kind.clone()).collect()
	}

	/// Prepares ABI function call with given input params.
	pub fn encode_input(&self, tokens: &[Token]) -> Result<Bytes> {
		let params = self.input_param_types();

		if !Token::types_check(tokens, &params) {
			return Err(Error::InvalidData);
		}

		let signed = short_signature(&self.name, &params).to_vec();
		let encoded = encode(tokens);
		Ok(signed.into_iter().chain(encoded.into_iter()).collect())
	}

	/// Parses the ABI function output to list of tokens.
	pub fn decode_output(&self, data: &[u8]) -> Result<Vec<Token>> {
		decode(&self.output_param_types(), &data)
	}

	/// Parses the ABI function input to a list of tokens.
	pub fn decode_input(&self, data: &[u8]) -> Result<Vec<Token>> {
		decode(&self.input_param_types(), &data)
	}

	/// Returns a signature that uniquely identifies this function.
	///
	/// Examples:
	/// - `functionName()`
	/// - `functionName():(uint256)`
	/// - `functionName(bool):(uint256,string)`
	/// - `functionName(uint256,bytes32):(string,uint256)`
	pub fn signature(&self) -> String {
		let inputs = self.inputs.iter().map(|p| p.kind.to_string()).collect::<Vec<_>>().join(",");

		let outputs = self.outputs.iter().map(|p| p.kind.to_string()).collect::<Vec<_>>().join(",");

		match (inputs.len(), outputs.len()) {
			(_, 0) => format!("{}({})", self.name, inputs),
			(_, _) => format!("{}({}):({})", self.name, inputs, outputs),
		}
	}
}

#[cfg(test)]
mod tests {
	use crate::{Function, StateMutability, Param, ParamType, Token};
	use hex_literal::hex;

	#[test]
	fn test_function_encode_call() {
		let interface = Function {
			name: "baz".to_owned(),
			inputs: vec![
				Param { name: "a".to_owned(), kind: ParamType::Uint(32) },
				Param { name: "b".to_owned(), kind: ParamType::Bool },
			],
			outputs: vec![],
			state_mutability: StateMutability::Pure,
		};

		let func = Function::from(interface);
		let mut uint = [0u8; 32];
		uint[31] = 69;
		let encoded = func.encode_input(&[Token::Uint(uint.into()), Token::Bool(true)]).unwrap();
		let expected = hex!("cdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001").to_vec();
		assert_eq!(encoded, expected);
	}
}