ethereum-contract-abi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/3.0 +0 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +21 -0
- data/README.md +27 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/ethereum_contract_abi.gemspec +15 -0
- data/lib/ethereum-contract-abi.rb +24 -0
- data/lib/ethereum-contract-abi/constants.rb +6 -0
- data/lib/ethereum-contract-abi/contract.rb +59 -0
- data/lib/ethereum-contract-abi/contract/abi_types/address.rb +29 -0
- data/lib/ethereum-contract-abi/contract/abi_types/base_type.rb +35 -0
- data/lib/ethereum-contract-abi/contract/abi_types/bool.rb +33 -0
- data/lib/ethereum-contract-abi/contract/abi_types/bytes.rb +42 -0
- data/lib/ethereum-contract-abi/contract/abi_types/fixed.rb +46 -0
- data/lib/ethereum-contract-abi/contract/abi_types/int.rb +48 -0
- data/lib/ethereum-contract-abi/contract/abi_types/string.rb +44 -0
- data/lib/ethereum-contract-abi/contract/abi_types/uint.rb +30 -0
- data/lib/ethereum-contract-abi/contract/eip/constants.rb +10 -0
- data/lib/ethereum-contract-abi/contract/eip/erc1155_metadata_interface.rb +30 -0
- data/lib/ethereum-contract-abi/contract/eip/erc165_interface.rb +30 -0
- data/lib/ethereum-contract-abi/contract/eip/erc721_enumerable_interface.rb +46 -0
- data/lib/ethereum-contract-abi/contract/eip/erc721_metadata_interface.rb +46 -0
- data/lib/ethereum-contract-abi/contract/function.rb +60 -0
- data/lib/ethereum-contract-abi/contract/input.rb +16 -0
- data/lib/ethereum-contract-abi/contract/output.rb +16 -0
- data/lib/ethereum-contract-abi/contract/parsers/abi_type_parser.rb +40 -0
- data/lib/ethereum-contract-abi/contract/parsers/contract_parser.rb +29 -0
- data/lib/ethereum-contract-abi/contract/parsers/function_parser.rb +42 -0
- data/lib/ethereum-contract-abi/decoders/function_decoder.rb +37 -0
- data/lib/ethereum-contract-abi/decoders/int_decoder.rb +11 -0
- data/lib/ethereum-contract-abi/decoders/string_decoder.rb +16 -0
- data/lib/ethereum-contract-abi/encoders/bytes_encoder.rb +31 -0
- data/lib/ethereum-contract-abi/encoders/decimal_encoder.rb +14 -0
- data/lib/ethereum-contract-abi/encoders/int_encoder.rb +16 -0
- data/lib/ethereum-contract-abi/util.rb +24 -0
- metadata +82 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: be70442c2225b17ac53a7d627051bba9388a2ff7ef3f6f9163c05ece75ec792a
|
|
4
|
+
data.tar.gz: e476f57e5107298dda5ea8a3dbf0fe9c30b593145b13fac1cc014dedf0b4a5df
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 521f0496cb783a54a79090ff6b1fd7536fcd325cede0a6a46ed732646e2969679e0f49d53d2733f66067524f359f4efcc5eb32707511d88f347d24fedcaf7670
|
|
7
|
+
data.tar.gz: '09c33f3ede2778e8fe8ceeb9379f5084884c2094c04ebb9fe065554699ca832b921283d1a40df78d3f825fd2e35be789190df6b74bdc87da794de62560387b08'
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.idea/
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/3.0
ADDED
|
File without changes
|
data/Gemfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gem 'rake'
|
|
6
|
+
|
|
7
|
+
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
|
8
|
+
|
|
9
|
+
gem "rspec", "~> 3.10"
|
|
10
|
+
|
|
11
|
+
gem "json", "~> 2.5"
|
|
12
|
+
|
|
13
|
+
git 'https://github.com/evtaylor/sha3-pure-ruby.git' do
|
|
14
|
+
gem 'sha3-pure-ruby'
|
|
15
|
+
end
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
GIT
|
|
2
|
+
remote: https://github.com/evtaylor/sha3-pure-ruby.git
|
|
3
|
+
revision: c3e764863f4926db33308d42e0fe7ec8ce3b72ce
|
|
4
|
+
specs:
|
|
5
|
+
sha3-pure-ruby (1.0.0)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
diff-lcs (1.4.4)
|
|
11
|
+
json (2.5.1)
|
|
12
|
+
rake (13.0.3)
|
|
13
|
+
rspec (3.10.0)
|
|
14
|
+
rspec-core (~> 3.10.0)
|
|
15
|
+
rspec-expectations (~> 3.10.0)
|
|
16
|
+
rspec-mocks (~> 3.10.0)
|
|
17
|
+
rspec-core (3.10.1)
|
|
18
|
+
rspec-support (~> 3.10.0)
|
|
19
|
+
rspec-expectations (3.10.1)
|
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
21
|
+
rspec-support (~> 3.10.0)
|
|
22
|
+
rspec-mocks (3.10.2)
|
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
24
|
+
rspec-support (~> 3.10.0)
|
|
25
|
+
rspec-support (3.10.2)
|
|
26
|
+
|
|
27
|
+
PLATFORMS
|
|
28
|
+
x64-mingw32
|
|
29
|
+
|
|
30
|
+
DEPENDENCIES
|
|
31
|
+
json (~> 2.5)
|
|
32
|
+
rake
|
|
33
|
+
rspec (~> 3.10)
|
|
34
|
+
sha3-pure-ruby!
|
|
35
|
+
|
|
36
|
+
BUNDLED WITH
|
|
37
|
+
2.2.3
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Evan Taylor
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# ethereum-contract-abi
|
|
2
|
+
|
|
3
|
+
A library for interacting with Ethereum smart contracts via the Contract Application Binary Interface (ABI).
|
|
4
|
+
|
|
5
|
+
For more info see: https://docs.soliditylang.org/en/latest/abi-spec.html
|
|
6
|
+
|
|
7
|
+
Can encode function calls with types:
|
|
8
|
+
- ✅ `address`
|
|
9
|
+
- ✅ `bool`
|
|
10
|
+
- ✅ `bytes`
|
|
11
|
+
- ✅ `fixed`
|
|
12
|
+
- ✅ `int`
|
|
13
|
+
- ✅ `uint`
|
|
14
|
+
- ✅ `string`
|
|
15
|
+
- ❌ `<type>[]`
|
|
16
|
+
- ❌ `tuple`
|
|
17
|
+
|
|
18
|
+
Can decode function calls with types:
|
|
19
|
+
- ❌ `address`
|
|
20
|
+
- ❌ `bool`
|
|
21
|
+
- ❌ `bytes`
|
|
22
|
+
- ❌ `fixed`
|
|
23
|
+
- ✅ `int`
|
|
24
|
+
- ✅ `uint`
|
|
25
|
+
- ✅ `string`
|
|
26
|
+
- ❌ `<type>[]`
|
|
27
|
+
- ❌ `tuple`
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "ethereum-contract-abi"
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
+
# require "pry"
|
|
12
|
+
# Pry.start
|
|
13
|
+
|
|
14
|
+
require "irb"
|
|
15
|
+
IRB.start(__FILE__)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'ethereum-contract-abi'
|
|
3
|
+
s.version = '0.1.0'
|
|
4
|
+
s.summary = "Ethereum contract ABI encoder and decoder"
|
|
5
|
+
s.description = "A library for interacting with Ethereum smart contracts via the Contract Application Binary Interface (ABI)"
|
|
6
|
+
s.authors = ["Evan Taylor"]
|
|
7
|
+
s.email = 'evan@evantaylor.ca'
|
|
8
|
+
s.homepage = "https://evantaylor.ca"
|
|
9
|
+
s.license = 'MIT'
|
|
10
|
+
|
|
11
|
+
s.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
12
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
13
|
+
end
|
|
14
|
+
s.require_paths = ["lib"]
|
|
15
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'bundler/setup'
|
|
2
|
+
require 'ethereum-contract-abi/contract/parsers/contract_parser'
|
|
3
|
+
require 'ethereum-contract-abi/contract/eip/erc165_interface'
|
|
4
|
+
require 'ethereum-contract-abi/contract/eip/erc721_metadata_interface'
|
|
5
|
+
require 'ethereum-contract-abi/contract/eip/erc1155_metadata_interface'
|
|
6
|
+
|
|
7
|
+
module EthereumContractABI
|
|
8
|
+
def self.contract_from_json(json_string)
|
|
9
|
+
ContractParser.from_json(json_string)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.get_interface(interface_id)
|
|
13
|
+
case interface_id
|
|
14
|
+
when EIP::ERC721_METADATA_ID
|
|
15
|
+
EIP::ERC721MetadataInterface
|
|
16
|
+
when EIP::ERC1155_METADATA_ID
|
|
17
|
+
EIP::ERC1155MetadataInterface
|
|
18
|
+
when EIP::ERC165_ID
|
|
19
|
+
EIP::ERC165Interface
|
|
20
|
+
else
|
|
21
|
+
raise ArgumentError.new('Unknown interface identifier')
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/contract/eip/constants'
|
|
2
|
+
require 'ethereum-contract-abi/contract/eip/erc721_metadata_interface'
|
|
3
|
+
require 'ethereum-contract-abi/contract/eip/erc721_enumerable_interface'
|
|
4
|
+
require 'ethereum-contract-abi/contract/eip/erc1155_metadata_interface'
|
|
5
|
+
|
|
6
|
+
include EthereumContractABI::ContractInterface
|
|
7
|
+
|
|
8
|
+
module EthereumContractABI
|
|
9
|
+
class Contract
|
|
10
|
+
def initialize(functions, events)
|
|
11
|
+
@functions = functions.to_h { |func| [func.name, func]}
|
|
12
|
+
@events = events
|
|
13
|
+
|
|
14
|
+
functions.each do |f|
|
|
15
|
+
self.class.send(:define_method, f.name) { function(f.name) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def functions
|
|
20
|
+
@functions.values
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def function(name)
|
|
24
|
+
@functions.dig(name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def function_exists?(name)
|
|
28
|
+
@functions.key? name
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def implements_interface(identifier)
|
|
32
|
+
case identifier
|
|
33
|
+
when EIP::ERC721_METADATA_ID
|
|
34
|
+
EIP::ERC721MetadataInterface.is_implemented_by?(self)
|
|
35
|
+
when EIP::ERC721_ENUMERABLE_ID
|
|
36
|
+
EIP::ERC721EnumerableInterface.is_implemented_by?(self)
|
|
37
|
+
when EIP::ERC1155_METADATA_ID
|
|
38
|
+
EIP::ERC1155MetadataInterface.is_implemented_by?(self)
|
|
39
|
+
else
|
|
40
|
+
raise ArgumentError.new('Unknown interface identifier')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def has_function?(func)
|
|
45
|
+
return false unless function_exists?(func.name)
|
|
46
|
+
contract_func = function(func.name)
|
|
47
|
+
return false unless contract_func.inputs.size == func.inputs.size
|
|
48
|
+
return false unless contract_func.outputs.size == func.outputs.size
|
|
49
|
+
|
|
50
|
+
input_type_strings = contract_func.inputs.map {|i| i.type.to_s }
|
|
51
|
+
expected_input_type_strings = func.inputs.map {|i| i.type.to_s }
|
|
52
|
+
output_type_string = contract_func.outputs.map {|i| i.type.to_s }
|
|
53
|
+
expected_output_type_string = func.outputs.map {|i| i.type.to_s }
|
|
54
|
+
|
|
55
|
+
Set.new(expected_input_type_strings) == Set.new(input_type_strings) &&
|
|
56
|
+
Set.new(expected_output_type_string) == Set.new(output_type_string)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/encoders/int_encoder'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/uint'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Encoders
|
|
5
|
+
include EthereumContractABI::ContractInterface::AbiTypes
|
|
6
|
+
|
|
7
|
+
module EthereumContractABI
|
|
8
|
+
module ContractInterface
|
|
9
|
+
module AbiTypes
|
|
10
|
+
class Address < Uint
|
|
11
|
+
def initialize
|
|
12
|
+
super(160)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def is_dynamic
|
|
16
|
+
false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
"address"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.from_string(string_type)
|
|
24
|
+
string_type === 'address' ? self.new : nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module EthereumContractABI
|
|
2
|
+
module ContractInterface
|
|
3
|
+
module AbiTypes
|
|
4
|
+
class BaseType
|
|
5
|
+
def to_s
|
|
6
|
+
raise NotImplementedError.new('to_s not implemented on type')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def is_dynamic
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def bytesize
|
|
14
|
+
32
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def encode_value(value)
|
|
18
|
+
raise NotImplementedError.new('encode_value not implemented on type')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def decode_value(value)
|
|
22
|
+
raise NotImplementedError.new('decode_value not implemented on type')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.from_string(string_type)
|
|
26
|
+
raise NotImplementedError.new('from_string not implemented on type')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def is_type_equal(type)
|
|
30
|
+
to_s == type.to_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/encoders/int_encoder'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/base_type'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Encoders
|
|
5
|
+
|
|
6
|
+
module EthereumContractABI
|
|
7
|
+
module ContractInterface
|
|
8
|
+
module AbiTypes
|
|
9
|
+
class Bool < BaseType
|
|
10
|
+
def to_s
|
|
11
|
+
"bool"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def is_dynamic
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def valid_value?(bool)
|
|
19
|
+
[true, false].include? bool
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def encode_value(bool)
|
|
23
|
+
raise ArgumentError unless valid_value?(bool)
|
|
24
|
+
IntEncoder.encode(bool ? 1 : 0)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_string(string_type)
|
|
28
|
+
string_type === 'bool' ? self.new : nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/encoders/bytes_encoder'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/base_type'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Encoders
|
|
5
|
+
|
|
6
|
+
module EthereumContractABI
|
|
7
|
+
module ContractInterface
|
|
8
|
+
module AbiTypes
|
|
9
|
+
class Bytes < BaseType
|
|
10
|
+
def initialize(num_bytes = nil)
|
|
11
|
+
@num_bytes = num_bytes
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def is_dynamic
|
|
15
|
+
@num_bytes.nil?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def bytesize
|
|
19
|
+
@num_bytes.nil? ? nil : (@num_bytes.to_f/32.0).ceil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_s
|
|
23
|
+
"bytes#{@num_bytes}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def valid_value?(value)
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def encode_value(bytes)
|
|
31
|
+
BytesEncoder.encode(bytes, @num_bytes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.from_string(string_type)
|
|
35
|
+
/(?<is_bytes>bytes)(?<bytes>\d+)?/ =~ string_type
|
|
36
|
+
return nil unless is_bytes
|
|
37
|
+
bytes ? self.new(bytes.to_i) : self.new
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/encoders/decimal_encoder'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/base_type'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Encoders
|
|
5
|
+
|
|
6
|
+
module EthereumContractABI
|
|
7
|
+
module ContractInterface
|
|
8
|
+
module AbiTypes
|
|
9
|
+
class Fixed < BaseType
|
|
10
|
+
def initialize(bits = 128, precision = 18)
|
|
11
|
+
raise ArgumentError.new("8 must be a factor of bits") unless bits % 8 === 0
|
|
12
|
+
raise ArgumentError.new("bits must be: 8 <= bits <= 256") unless 8 <= bits && bits <= 256
|
|
13
|
+
raise ArgumentError.new("precision must be: 0 < precision <= 80") unless 0 < precision && precision <= 80
|
|
14
|
+
@bits = bits
|
|
15
|
+
@precision = precision
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def is_dynamic
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_s
|
|
23
|
+
"fixed#{@bits}x#{@precision}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def valid_value?(number)
|
|
27
|
+
return false unless number.is_a? Numeric
|
|
28
|
+
return false unless number.round(0).bit_length <= @bits
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def encode_value(number)
|
|
33
|
+
raise ArgumentError unless valid_value?(number)
|
|
34
|
+
DecimalEncoder.encode_value(number, @precision)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.from_string(string_type)
|
|
38
|
+
/(?<type>fixed)((?<bits>\d+)x(?<precision>\d+))?/ =~ string_type
|
|
39
|
+
return nil unless type
|
|
40
|
+
|
|
41
|
+
bits && precision ? self.new(bits.to_i, precision.to_i) : self.new
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/encoders/int_encoder'
|
|
2
|
+
require 'ethereum-contract-abi/decoders/int_decoder'
|
|
3
|
+
require 'ethereum-contract-abi/contract/abi_types/base_type'
|
|
4
|
+
|
|
5
|
+
include EthereumContractABI::Encoders
|
|
6
|
+
include EthereumContractABI::Decoders
|
|
7
|
+
|
|
8
|
+
module EthereumContractABI
|
|
9
|
+
module ContractInterface
|
|
10
|
+
module AbiTypes
|
|
11
|
+
class Int < BaseType
|
|
12
|
+
def initialize(bits = 256)
|
|
13
|
+
raise ArgumentError.new("8 must be a factor of bits") unless bits % 8 === 0
|
|
14
|
+
@bits = bits
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def is_dynamic
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
"int#{@bits}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def valid_value?(number)
|
|
26
|
+
return false unless number.is_a? Numeric
|
|
27
|
+
return false unless number.bit_length <= @bits
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def encode_value(number)
|
|
32
|
+
raise ArgumentError unless valid_value?(number)
|
|
33
|
+
IntEncoder.encode(number)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def decode_value(value)
|
|
37
|
+
IntDecoder.decode(value)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.from_string(string_type)
|
|
41
|
+
/(?<is_int>^int)(?<bits>\d+)?/ =~ string_type
|
|
42
|
+
return nil unless is_int
|
|
43
|
+
bits ? self.new(bits.to_i) : self.new
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/encoders/bytes_encoder'
|
|
2
|
+
require 'ethereum-contract-abi/decoders/string_decoder'
|
|
3
|
+
require 'ethereum-contract-abi/contract/abi_types/base_type'
|
|
4
|
+
|
|
5
|
+
include EthereumContractABI::Encoders
|
|
6
|
+
include EthereumContractABI::Decoders
|
|
7
|
+
|
|
8
|
+
module EthereumContractABI
|
|
9
|
+
module ContractInterface
|
|
10
|
+
module AbiTypes
|
|
11
|
+
class String < BaseType
|
|
12
|
+
def to_s
|
|
13
|
+
"string"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def is_dynamic
|
|
17
|
+
true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def bytesize
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def valid_value?(value)
|
|
25
|
+
return false unless value.is_a? String
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def encode_value(value)
|
|
30
|
+
raise ArgumentError unless valid_value?(value)
|
|
31
|
+
BytesEncoder.encode(value)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def decode_value(value)
|
|
35
|
+
StringDecoder.decode(value)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.from_string(string_type)
|
|
39
|
+
string_type === 'string' ? self.new : nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/encoders/int_encoder'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/int'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Encoders
|
|
5
|
+
|
|
6
|
+
module EthereumContractABI
|
|
7
|
+
module ContractInterface
|
|
8
|
+
module AbiTypes
|
|
9
|
+
class Uint < Int
|
|
10
|
+
def to_s
|
|
11
|
+
"uint#{@bits}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def is_dynamic
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def valid_value?(number)
|
|
19
|
+
super(number) && number >= 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.from_string(string_type)
|
|
23
|
+
/(?<is_uint>uint)(?<bits>\d+)?/ =~ string_type
|
|
24
|
+
return nil unless is_uint
|
|
25
|
+
bits ? self.new(bits.to_i) : self.new
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/contract/abi_types/string'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/uint'
|
|
3
|
+
require 'ethereum-contract-abi/contract/output'
|
|
4
|
+
require 'ethereum-contract-abi/contract/function'
|
|
5
|
+
|
|
6
|
+
include EthereumContractABI::ContractInterface
|
|
7
|
+
|
|
8
|
+
module EthereumContractABI
|
|
9
|
+
module ContractInterface
|
|
10
|
+
module EIP
|
|
11
|
+
class ERC1155MetadataInterface
|
|
12
|
+
|
|
13
|
+
def self.is_implemented_by?(contract)
|
|
14
|
+
contract.has_function?(self.uri)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.functions
|
|
18
|
+
[self.uri]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.uri
|
|
22
|
+
function_name = 'uri'
|
|
23
|
+
inputs = [Input.new('_id', AbiTypes::Uint.new)]
|
|
24
|
+
outputs = [Output.new(AbiTypes::String.new)]
|
|
25
|
+
Function.new(function_name, inputs, outputs)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/contract/abi_types/bytes'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/bool'
|
|
3
|
+
require 'ethereum-contract-abi/contract/output'
|
|
4
|
+
require 'ethereum-contract-abi/contract/function'
|
|
5
|
+
|
|
6
|
+
include EthereumContractABI::ContractInterface
|
|
7
|
+
|
|
8
|
+
module EthereumContractABI
|
|
9
|
+
module ContractInterface
|
|
10
|
+
module EIP
|
|
11
|
+
class ERC165Interface
|
|
12
|
+
|
|
13
|
+
def self.is_implemented_by?(contract)
|
|
14
|
+
contract.has_function?(self.supportsInterface)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.functions
|
|
18
|
+
[self.supportsInterface]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.supportsInterface
|
|
22
|
+
function_name = 'supportsInterface'
|
|
23
|
+
inputs = [Input.new( 'interfaceID', AbiTypes::Bytes.new(4))]
|
|
24
|
+
outputs = [Output.new(AbiTypes::Bool.new)]
|
|
25
|
+
Function.new(function_name, inputs, outputs)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/contract/abi_types/address'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/uint'
|
|
3
|
+
require 'ethereum-contract-abi/contract/output'
|
|
4
|
+
require 'ethereum-contract-abi/contract/function'
|
|
5
|
+
|
|
6
|
+
include EthereumContractABI::ContractInterface
|
|
7
|
+
|
|
8
|
+
module EthereumContractABI
|
|
9
|
+
module ContractInterface
|
|
10
|
+
module EIP
|
|
11
|
+
class ERC721EnumerableInterface
|
|
12
|
+
|
|
13
|
+
def self.is_implemented_by?(contract)
|
|
14
|
+
contract.has_function?(self.totalSupply) &&
|
|
15
|
+
contract.has_function?(self.tokenByIndex) &&
|
|
16
|
+
contract.has_function?(self.tokenOfOwnerByIndex)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.totalSupply
|
|
20
|
+
function_name = 'totalSupply'
|
|
21
|
+
inputs = []
|
|
22
|
+
outputs = [Output.new(AbiTypes::Uint.new)]
|
|
23
|
+
Function.new(function_name, inputs, outputs)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.tokenByIndex
|
|
27
|
+
function_name = 'tokenByIndex'
|
|
28
|
+
inputs = [Input.new('_index', AbiTypes::Uint.new)]
|
|
29
|
+
outputs = [Output.new(AbiTypes::Uint.new)]
|
|
30
|
+
Function.new(function_name, inputs, outputs)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.tokenOfOwnerByIndex
|
|
34
|
+
function_name = 'tokenOfOwnerByIndex'
|
|
35
|
+
inputs = [Input.new('_owner', AbiTypes::Address.new), Input.new('_index', AbiTypes::Uint.new)]
|
|
36
|
+
outputs = [Output.new(AbiTypes::Uint.new)]
|
|
37
|
+
Function.new(function_name, inputs, outputs)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.functions
|
|
41
|
+
[self.totalSupply, self.tokenByIndex, self.tokenOfOwnerByIndex]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/contract/abi_types/string'
|
|
2
|
+
require 'ethereum-contract-abi/contract/abi_types/uint'
|
|
3
|
+
require 'ethereum-contract-abi/contract/output'
|
|
4
|
+
require 'ethereum-contract-abi/contract/function'
|
|
5
|
+
|
|
6
|
+
include EthereumContractABI::ContractInterface
|
|
7
|
+
|
|
8
|
+
module EthereumContractABI
|
|
9
|
+
module ContractInterface
|
|
10
|
+
module EIP
|
|
11
|
+
class ERC721MetadataInterface
|
|
12
|
+
|
|
13
|
+
def self.is_implemented_by?(contract)
|
|
14
|
+
contract.has_function?(self.name) &&
|
|
15
|
+
contract.has_function?(self.symbol) &&
|
|
16
|
+
contract.has_function?(self.tokenURI)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.name
|
|
20
|
+
function_name = 'name'
|
|
21
|
+
inputs = []
|
|
22
|
+
outputs = [Output.new(AbiTypes::String.new, '_name')]
|
|
23
|
+
Function.new(function_name, inputs, outputs)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.symbol
|
|
27
|
+
function_name = 'symbol'
|
|
28
|
+
inputs = []
|
|
29
|
+
outputs = [Output.new(AbiTypes::String.new, '_symbol')]
|
|
30
|
+
Function.new(function_name, inputs, outputs)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.tokenURI
|
|
34
|
+
function_name = 'tokenURI'
|
|
35
|
+
inputs = [Input.new('_tokenId', AbiTypes::Uint.new)]
|
|
36
|
+
outputs = [Output.new(AbiTypes::String.new)]
|
|
37
|
+
Function.new(function_name, inputs, outputs)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.functions
|
|
41
|
+
[self.name, self.symbol, self.tokenURI]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
require 'openssl'
|
|
3
|
+
require 'sha3-pure-ruby'
|
|
4
|
+
require 'ethereum-contract-abi/util'
|
|
5
|
+
require 'ethereum-contract-abi/decoders/function_decoder'
|
|
6
|
+
|
|
7
|
+
include EthereumContractABI::Decoders
|
|
8
|
+
|
|
9
|
+
module EthereumContractABI
|
|
10
|
+
module ContractInterface
|
|
11
|
+
class Function
|
|
12
|
+
attr_reader :name, :inputs, :outputs
|
|
13
|
+
|
|
14
|
+
def initialize(name, inputs, outputs)
|
|
15
|
+
@name = name
|
|
16
|
+
@inputs = inputs
|
|
17
|
+
@outputs = outputs
|
|
18
|
+
@decoder = FunctionDecoder.new(outputs)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def signature
|
|
22
|
+
params = @inputs.map { |i| i.type.to_s }
|
|
23
|
+
"#{@name}(#{params.join(",")})"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def method_id
|
|
27
|
+
hash = [Digest::SHA3.new(256, true).hexdigest(signature)].pack("H*")
|
|
28
|
+
hash.slice(0..3)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def valid_args?(args)
|
|
32
|
+
return false unless args.size === @inputs.size
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def encode_call(*args)
|
|
37
|
+
raise ArgumentError.new("Invalid function arguments") unless valid_args?(args)
|
|
38
|
+
encoded_args = @inputs.zip(args).map do |input, arg|
|
|
39
|
+
input.encode_value(arg)
|
|
40
|
+
end
|
|
41
|
+
EthereumContractABI::Util.fromHexByteString(method_id + encoded_args.join(''))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def decode_output(output_string)
|
|
45
|
+
output_hex_string = EthereumContractABI::Util.remove_hex_prefix(output_string)
|
|
46
|
+
if has_any_dynamic_outputs
|
|
47
|
+
@decoder.decode_dynamic_output(output_hex_string)
|
|
48
|
+
else
|
|
49
|
+
@decoder.decode_static_output(output_hex_string)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def has_any_dynamic_outputs
|
|
56
|
+
@outputs.find { |o| o.type.is_dynamic }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'ethereum-contract-abi/contract'
|
|
3
|
+
require 'ethereum-contract-abi/contract/abi_types/uint'
|
|
4
|
+
require 'ethereum-contract-abi/contract/abi_types/bool'
|
|
5
|
+
require 'ethereum-contract-abi/contract/abi_types/string'
|
|
6
|
+
require 'ethereum-contract-abi/contract/abi_types/fixed'
|
|
7
|
+
require 'ethereum-contract-abi/contract/abi_types/bytes'
|
|
8
|
+
require 'ethereum-contract-abi/contract/abi_types/address'
|
|
9
|
+
|
|
10
|
+
include EthereumContractABI::ContractInterface::AbiTypes
|
|
11
|
+
|
|
12
|
+
module EthereumContractABI
|
|
13
|
+
module ContractInterface
|
|
14
|
+
module Parsers
|
|
15
|
+
class AbiTypeParser
|
|
16
|
+
def self.from_string(string_type)
|
|
17
|
+
uint = Uint.from_string(string_type)
|
|
18
|
+
return uint unless uint.nil?
|
|
19
|
+
|
|
20
|
+
bool = Bool.from_string(string_type)
|
|
21
|
+
return bool unless bool.nil?
|
|
22
|
+
|
|
23
|
+
decimal = Fixed.from_string(string_type)
|
|
24
|
+
return decimal unless decimal.nil?
|
|
25
|
+
|
|
26
|
+
str = EthereumContractABI::ContractInterface::AbiTypes::String.from_string(string_type)
|
|
27
|
+
return str unless str.nil?
|
|
28
|
+
|
|
29
|
+
bytes = Bytes.from_string(string_type)
|
|
30
|
+
return bytes unless bytes.nil?
|
|
31
|
+
|
|
32
|
+
address = Address.from_string(string_type)
|
|
33
|
+
return address unless address.nil?
|
|
34
|
+
|
|
35
|
+
raise ArgumentError.new('Unknown type')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'ethereum-contract-abi/contract'
|
|
3
|
+
require 'ethereum-contract-abi/contract/parsers/function_parser'
|
|
4
|
+
|
|
5
|
+
include EthereumContractABI::ContractInterface::Parsers
|
|
6
|
+
|
|
7
|
+
module EthereumContractABI
|
|
8
|
+
module ContractInterface
|
|
9
|
+
module Parsers
|
|
10
|
+
class ContractParser
|
|
11
|
+
def self.from_json(json_string)
|
|
12
|
+
parsed = JSON.parse(json_string)
|
|
13
|
+
functions = parsed.select { |interface| interface["type"] === "function"}
|
|
14
|
+
.map { |f_hash| f_hash.transform_keys(&:to_sym) }
|
|
15
|
+
.filter_map do |f_hash|
|
|
16
|
+
begin
|
|
17
|
+
FunctionParser.from_hash(f_hash)
|
|
18
|
+
rescue ArgumentError
|
|
19
|
+
# Error parsing contract function, all ABI types are not yet implemented
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
events = parsed.select { |interface| interface["type"] === "event"}
|
|
24
|
+
EthereumContractABI::Contract.new(functions, events)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'ethereum-contract-abi/contract'
|
|
3
|
+
require 'ethereum-contract-abi/contract/input'
|
|
4
|
+
require 'ethereum-contract-abi/contract/output'
|
|
5
|
+
require 'ethereum-contract-abi/contract/parsers/abi_type_parser'
|
|
6
|
+
require 'ethereum-contract-abi/contract/function'
|
|
7
|
+
|
|
8
|
+
include EthereumContractABI::ContractInterface
|
|
9
|
+
|
|
10
|
+
module EthereumContractABI
|
|
11
|
+
module ContractInterface
|
|
12
|
+
module Parsers
|
|
13
|
+
class FunctionParser
|
|
14
|
+
def self.from_hash(function_hash)
|
|
15
|
+
function_hash = function_hash.transform_keys(&:to_sym)
|
|
16
|
+
name = function_hash[:name]
|
|
17
|
+
inputs = self.get_inputs(function_hash[:inputs])
|
|
18
|
+
outputs = self.get_inputs(function_hash[:outputs])
|
|
19
|
+
Function.new(name, inputs, outputs)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def self.get_inputs(inputs)
|
|
25
|
+
inputs.map do |input_hash|
|
|
26
|
+
hash = input_hash.transform_keys(&:to_sym)
|
|
27
|
+
type = AbiTypeParser.from_string(hash[:type])
|
|
28
|
+
Input.new(name, type)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.get_outputs(outputs)
|
|
33
|
+
outputs.map do |input_hash|
|
|
34
|
+
hash = input_hash.transform_keys(&:to_sym)
|
|
35
|
+
type = AbiTypeParser.from_string(hash[:type])
|
|
36
|
+
Output.new(name, type)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/util'
|
|
2
|
+
require 'ethereum-contract-abi/decoders/int_decoder'
|
|
3
|
+
require 'ethereum-contract-abi/decoders/string_decoder'
|
|
4
|
+
|
|
5
|
+
module EthereumContractABI
|
|
6
|
+
module Decoders
|
|
7
|
+
class FunctionDecoder
|
|
8
|
+
def initialize(function_outputs)
|
|
9
|
+
@function_outputs = function_outputs
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def decode_dynamic_output(encoded_output)
|
|
13
|
+
hex = Util.toHexByteString(encoded_output)
|
|
14
|
+
@function_outputs.map.with_index do |output, index|
|
|
15
|
+
head = get_head_by_index(encoded_output, index)
|
|
16
|
+
head_offset = IntDecoder.decode(head)
|
|
17
|
+
output_bytes = hex.byteslice(head_offset, hex.bytesize)
|
|
18
|
+
output.type.decode_value(Util.fromHexByteString(output_bytes, with_prefix: false))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def decode_static_output(encoded_output)
|
|
23
|
+
@function_outputs.map.with_index do |output, index|
|
|
24
|
+
bytes = encoded_output.slice!(0, output.type.bytesize * 2)
|
|
25
|
+
output.type.decode_value(bytes)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def get_head_by_index(encoded_output, index)
|
|
32
|
+
first_byte = index * 64
|
|
33
|
+
encoded_output.byteslice(first_byte, 64)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/util'
|
|
2
|
+
require 'ethereum-contract-abi/decoders/int_decoder'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Decoders
|
|
5
|
+
|
|
6
|
+
module EthereumContractABI
|
|
7
|
+
module Decoders
|
|
8
|
+
class StringDecoder
|
|
9
|
+
def self.decode(string, num_bytes = nil)
|
|
10
|
+
hex = [string].pack('H*')
|
|
11
|
+
length = IntDecoder.decode(hex.byteslice(0, 32).unpack("H*").first)
|
|
12
|
+
hex.byteslice(32..hex.bytesize).byteslice(0, length).strip
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/constants'
|
|
2
|
+
require 'ethereum-contract-abi/encoders/int_encoder'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Encoders
|
|
5
|
+
|
|
6
|
+
module EthereumContractABI
|
|
7
|
+
module Encoders
|
|
8
|
+
class BytesEncoder
|
|
9
|
+
def self.encode(bytes_to_encode, num_bytes = nil)
|
|
10
|
+
if bytes_to_encode.methods.include?(:start_with?) && bytes_to_encode.start_with?("0x")
|
|
11
|
+
bytes_to_encode = Util.toHexByteString(bytes_to_encode.slice(2, bytes_to_encode.length))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
unless num_bytes.nil?
|
|
15
|
+
raise ArgumentError.new("Too many bytes to encode") unless bytes_to_encode.bytesize <= num_bytes
|
|
16
|
+
return self.encode_bytes(bytes_to_encode)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
num_bytes_encoded = IntEncoder.encode(bytes_to_encode.bytesize)
|
|
20
|
+
num_bytes_encoded + self.encode_bytes(bytes_to_encode)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def self.encode_bytes(bytes_to_encode)
|
|
26
|
+
final_length = (bytes_to_encode.bytesize / 32.to_f).ceil * 32
|
|
27
|
+
bytes_to_encode + Constants::BYTE_ZERO * (final_length - bytes_to_encode.bytesize)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/constants'
|
|
2
|
+
require 'ethereum-contract-abi/encoders/int_encoder'
|
|
3
|
+
|
|
4
|
+
include EthereumContractABI::Encoders
|
|
5
|
+
|
|
6
|
+
module EthereumContractABI
|
|
7
|
+
module Encoders
|
|
8
|
+
class DecimalEncoder
|
|
9
|
+
def self.encode_value(decimal_number, precision)
|
|
10
|
+
IntEncoder.encode((decimal_number * 10**precision).to_i)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'ethereum-contract-abi/constants'
|
|
2
|
+
|
|
3
|
+
module EthereumContractABI
|
|
4
|
+
module Encoders
|
|
5
|
+
class IntEncoder
|
|
6
|
+
def self.encode(int)
|
|
7
|
+
hex = int < 0 ? (int & 0xffff).to_s(16).rjust(2, "0") : int.to_s(16).rjust(2, "0")
|
|
8
|
+
# converting to byte string requires each byte is represented by exactly 2 hex characters
|
|
9
|
+
adjusted_hex = hex.length % 2 != 0 ? "0" + hex : hex
|
|
10
|
+
int_hex_bytes = Util.toHexByteString(adjusted_hex)
|
|
11
|
+
pad_byte = int < 0 ? Constants::BYTE_ONE : Constants::BYTE_ZERO
|
|
12
|
+
pad_byte * (32 - int_hex_bytes.bytesize) + int_hex_bytes
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module EthereumContractABI
|
|
2
|
+
class Util
|
|
3
|
+
|
|
4
|
+
def self.toHexByteString(str)
|
|
5
|
+
if str.start_with?("0x")
|
|
6
|
+
return [str.slice(2, str.length)].pack("H*")
|
|
7
|
+
end
|
|
8
|
+
[str].pack("H*")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.fromHexByteString(str, with_prefix: true)
|
|
12
|
+
prefix = with_prefix ? "0x" : ""
|
|
13
|
+
prefix + str.unpack("H*").first
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.remove_hex_prefix(str)
|
|
17
|
+
str.slice(2, str.length)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self. keccak_hash(str)
|
|
21
|
+
[Digest::SHA3.new(256, true ).hexdigest(signature)].pack("H*")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ethereum-contract-abi
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Evan Taylor
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-06-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A library for interacting with Ethereum smart contracts via the Contract
|
|
14
|
+
Application Binary Interface (ABI)
|
|
15
|
+
email: evan@evantaylor.ca
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- ".gitignore"
|
|
21
|
+
- ".rspec"
|
|
22
|
+
- '3.0'
|
|
23
|
+
- Gemfile
|
|
24
|
+
- Gemfile.lock
|
|
25
|
+
- LICENSE
|
|
26
|
+
- README.md
|
|
27
|
+
- Rakefile
|
|
28
|
+
- bin/console
|
|
29
|
+
- ethereum_contract_abi.gemspec
|
|
30
|
+
- lib/ethereum-contract-abi.rb
|
|
31
|
+
- lib/ethereum-contract-abi/constants.rb
|
|
32
|
+
- lib/ethereum-contract-abi/contract.rb
|
|
33
|
+
- lib/ethereum-contract-abi/contract/abi_types/address.rb
|
|
34
|
+
- lib/ethereum-contract-abi/contract/abi_types/base_type.rb
|
|
35
|
+
- lib/ethereum-contract-abi/contract/abi_types/bool.rb
|
|
36
|
+
- lib/ethereum-contract-abi/contract/abi_types/bytes.rb
|
|
37
|
+
- lib/ethereum-contract-abi/contract/abi_types/fixed.rb
|
|
38
|
+
- lib/ethereum-contract-abi/contract/abi_types/int.rb
|
|
39
|
+
- lib/ethereum-contract-abi/contract/abi_types/string.rb
|
|
40
|
+
- lib/ethereum-contract-abi/contract/abi_types/uint.rb
|
|
41
|
+
- lib/ethereum-contract-abi/contract/eip/constants.rb
|
|
42
|
+
- lib/ethereum-contract-abi/contract/eip/erc1155_metadata_interface.rb
|
|
43
|
+
- lib/ethereum-contract-abi/contract/eip/erc165_interface.rb
|
|
44
|
+
- lib/ethereum-contract-abi/contract/eip/erc721_enumerable_interface.rb
|
|
45
|
+
- lib/ethereum-contract-abi/contract/eip/erc721_metadata_interface.rb
|
|
46
|
+
- lib/ethereum-contract-abi/contract/function.rb
|
|
47
|
+
- lib/ethereum-contract-abi/contract/input.rb
|
|
48
|
+
- lib/ethereum-contract-abi/contract/output.rb
|
|
49
|
+
- lib/ethereum-contract-abi/contract/parsers/abi_type_parser.rb
|
|
50
|
+
- lib/ethereum-contract-abi/contract/parsers/contract_parser.rb
|
|
51
|
+
- lib/ethereum-contract-abi/contract/parsers/function_parser.rb
|
|
52
|
+
- lib/ethereum-contract-abi/decoders/function_decoder.rb
|
|
53
|
+
- lib/ethereum-contract-abi/decoders/int_decoder.rb
|
|
54
|
+
- lib/ethereum-contract-abi/decoders/string_decoder.rb
|
|
55
|
+
- lib/ethereum-contract-abi/encoders/bytes_encoder.rb
|
|
56
|
+
- lib/ethereum-contract-abi/encoders/decimal_encoder.rb
|
|
57
|
+
- lib/ethereum-contract-abi/encoders/int_encoder.rb
|
|
58
|
+
- lib/ethereum-contract-abi/util.rb
|
|
59
|
+
homepage: https://evantaylor.ca
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata: {}
|
|
63
|
+
post_install_message:
|
|
64
|
+
rdoc_options: []
|
|
65
|
+
require_paths:
|
|
66
|
+
- lib
|
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '0'
|
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
requirements: []
|
|
78
|
+
rubygems_version: 3.2.3
|
|
79
|
+
signing_key:
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: Ethereum contract ABI encoder and decoder
|
|
82
|
+
test_files: []
|