sigma_mint 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a7066884c595e6013111476407b65e48778e29c6fbe7a28c1b3b525fac944522
4
+ data.tar.gz: 9336dc029423e66c5e58872d270240ae1ed34c7854f5551a321444e7ac8e66cc
5
+ SHA512:
6
+ metadata.gz: ea7777f22c196ed6cd7beaae5a3b38b1e09d41025a1108c0924f3d4da65b7cc8c9a03b05ca8f7e3ca1284bb06966a04fee39500eb89af070d12c7c6ae57783f8
7
+ data.tar.gz: 82e4679bab22c836f050fa1430b41a15e3995043f602da3f6090176f6d03efa4844a40e085913af51c5f1e0dd462e05fb49b093d65712c05be50df992857cdfb
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Dark Lord of Programming
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,72 @@
1
+ # sigma_mint
2
+ Ergo assets utility library
3
+
4
+ ### Installation
5
+ This library uses [sigma_rb](https://github.com/thedlop/sigma_rb) which has extra installation instructions, please ensure it installs correctly first.
6
+
7
+ Add to Gemfile
8
+ `gem 'sigma_mint', '0.1.1'`
9
+
10
+ ### Usage
11
+ In an effort to save time I will share code that called this lib below. This code is straight from my [two tens](https://twotens.art) project source.
12
+
13
+
14
+ #### PinataClient
15
+ We can use Pinata REST API to pin files on ipfs. This requires a Pinata account (free up to 1GB) and a corresponding JWT to use when making requests. The following ENV variables are required:
16
+ ```
17
+ # Required for PinataClient
18
+ PINATA_JWT=
19
+ ```
20
+
21
+ For pinning images on ipfs via `PinataClient`:
22
+
23
+ ```
24
+ pc = SigmaMint::PinataClient.new
25
+ ...
26
+ # png_path is a string path to the file we wish pin
27
+ # nft_config is a ruby hash (dictionary) that contains some metadata
28
+ # config is also a ruby hash (dictionary) that contains other metadata
29
+ r = pc.pin!(png_path, "#{nft_config[:name]}", metadata: {set: config[:name], number: nft_config[:set_number]})
30
+
31
+ # you may need the ipfs_hash or sha later, it is a requirement for minting an nft picture in ErgoNodeClient
32
+ ipfs_hash = JSON.parse(r.body)['IpfsHash']
33
+ ```
34
+
35
+ #### ErgoNodeClient
36
+ We can utilize the Ergo Node API to mint a picture nft with royalty. Now this requires running your own node, or a node you have API access and wallet access to. The following ENV variables
37
+ are required, you set them by adding them to your `.env` file or adding them at runtime.
38
+ ```
39
+ # Required for ErgoNodeClient
40
+ ERGO_NODE_URL=
41
+ ERGO_NODE_API_KEY=
42
+ ERGO_NODE_WALLET_PASSWORD=
43
+ ```
44
+ For minting a picture nft via `ErgoNodeClient` :
45
+
46
+ ```
47
+ # require at top of file
48
+ require 'sigma_mint'
49
+ ...
50
+
51
+ enc = SigmaMint::ErgoNodeClient.new
52
+ # nft_config is a ruby hash (dictionary) that contains some metadata about this nft
53
+ # enft_description and enft_name are strings
54
+ # royalty is an integer
55
+ enft = SigmaMint::ErgoNft.new(name: enft_name,
56
+ description: enft_description,
57
+ royalty: royalty,
58
+ ipfs_hash: nft_config[:ipfs_hash],
59
+ sha: nft_config[:scaled_sha],
60
+ minted: false)
61
+
62
+ # since we are making private calls to our node we must unlock it first
63
+ enc.unlock_wallet
64
+
65
+ # generate and sign nft tranasactions and send to node
66
+ # real can be set to false if you want to see the generated transaction without sending it to the node (default false)
67
+ # minting address is the node wallet address that will do the minting
68
+ # minting_address_ergo_tree is the ergo_tree representation of the minting address
69
+ # you can get it view `ErgoNodeClient#address_to_tree`
70
+ r = enc.mint_picture_nft(minting_address, enft, address_ergo_tree: minting_address_ergo_tree, real: real)
71
+ ...
72
+ ```
@@ -0,0 +1,25 @@
1
+ module SigmaMint
2
+ class ErgoNft
3
+ attr_accessor :name, :description, :royalty, :sha, :ipfs_hash, :minted
4
+
5
+ def initialize(name: nil,
6
+ description: nil,
7
+ royalty: 0,
8
+ ipfs_hash: nil,
9
+ sha: nil,
10
+ minted: true)
11
+
12
+
13
+ @name = name
14
+ @description = description
15
+ @royalty = royalty
16
+ @sha = sha
17
+ @ipfs_hash = ipfs_hash
18
+ @minted = minted
19
+ end
20
+
21
+ def ipfs_link
22
+ "https://gateway.pinata.cloud/ipfs/#{@ipfs_hash}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,223 @@
1
+ require 'sigma'
2
+ require 'sigma_mint'
3
+
4
+ module SigmaMint
5
+ # Simple Ergo Node Client for minting assets
6
+ #
7
+ # Some ENV Variables are required:
8
+ # - ERGO_NODE_API_KEY : API Key for node
9
+ # - ERGO_NODE_URL : Node url (ex: https://paidincrypto.io , http://127.0.0.1 )
10
+ # - ERGO_NODE_WALLET_PASSWORD : Node wallet password
11
+ class ErgoNodeClient
12
+ class MissingEnvVarError < StandardError; end
13
+
14
+ def initialize
15
+ if ENV['ERGO_NODE_API_KEY'].nil?
16
+ raise MissingEnvVarError.new("ERGO_NODE_API_KEY is blank, required for ErgoNodeClient")
17
+ end
18
+
19
+ if ENV['ERGO_NODE_URL'].nil?
20
+ raise MissingEnvVarError.new("ERGO_NODE_URL is blank, required for ErgoNodeClient")
21
+ end
22
+
23
+ @conn = Faraday.new(
24
+ url: ENV['ERGO_NODE_URL'],
25
+ headers: {'api_key' => ENV['ERGO_NODE_API_KEY'],
26
+ 'Accept' => 'application/json',
27
+ 'Content-Type' => 'application/json'
28
+ },
29
+ )
30
+ end
31
+
32
+ def unlock_wallet
33
+ if ENV['ERGO_NODE_WALLET_PASSWORD'].nil?
34
+ raise MissingEnvVarError.new("ERGO_NODE_WALLET_PASSWORD is blank, required for ErgoNodeClient")
35
+ end
36
+
37
+ params = {pass: ENV['ERGO_NODE_WALLET_PASSWORD']}
38
+ @conn.post('/wallet/unlock', params.to_json)
39
+ end
40
+
41
+ def address_to_tree(address)
42
+ @conn.get("/script/addressToTree/#{address}")
43
+ end
44
+
45
+ ErgoEncodedNFT = Struct.new(
46
+ :royalty,
47
+ :address_ergo_tree,
48
+ :sha,
49
+ :ipfs_link,
50
+ keyword_init: :true,
51
+ )
52
+
53
+ def burn_assets(token_ids)
54
+ assets = token_ids.map do |token_id|
55
+ { tokenId: token_id,
56
+ amount: 1}
57
+ end
58
+ params = {
59
+ requests: [
60
+ {
61
+ assetsToBurn: assets,
62
+ }
63
+ ],
64
+ fee: 1000000,
65
+ }
66
+
67
+ r = @conn.post('/wallet/transaction/send', params.to_json)
68
+ puts r.body
69
+ r
70
+ end
71
+
72
+ def send_nfts(to_address, token_ids)
73
+ assets = token_ids.map do |token_id|
74
+ { tokenId: token_id,
75
+ amount: 1}
76
+ end
77
+ params = {
78
+ requests: [
79
+ {address: to_address,
80
+ value: 100000 * token_ids.count,
81
+ assets: assets,
82
+ }
83
+ ],
84
+ fee: 1000000,
85
+ }
86
+ r = @conn.post('/wallet/transaction/send', params.to_json)
87
+ puts r.body
88
+ r
89
+ end
90
+
91
+ def send_transaction(ergo_transaction)
92
+ @conn.post('/transactions', ergo_transaction.to_json)
93
+ end
94
+
95
+ def get_serialized_box_with_pool(box_id)
96
+ @conn.get("/utxo/withPool/byIdBinary/#{box_id}", {})
97
+ end
98
+
99
+ def get_unspent_boxes(min_confirmations: 0, min_inclusion_height: 0)
100
+ params = {
101
+ minConfirmations: min_confirmations,
102
+ minInclusionHeight: min_inclusion_height,
103
+ }
104
+
105
+ r = @conn.get('/wallet/boxes/unspent', params)
106
+ return r
107
+ end
108
+
109
+ def mint_picture_nft(address, ergo_nft, real: false, address_ergo_tree: nil)
110
+
111
+ if address_ergo_tree.nil?
112
+ r = address_to_tree(address)
113
+ address_ergo_tree = JSON.parse(r.body)['tree']
114
+ end
115
+
116
+ # get encoded values
117
+ encoded = get_encoded_values(ergo_nft, address_ergo_tree: address_ergo_tree)
118
+
119
+ royalty_params = {
120
+ requests: [],
121
+ fee: Sigma::TxBuilder.suggested_tx_fee.to_i64,
122
+ }
123
+
124
+ royalty_request = {
125
+ address: address,
126
+ value: 2000000,
127
+ registers: {
128
+ R4: encoded.royalty,
129
+ R5: encoded.address_ergo_tree,
130
+ }
131
+ }
132
+
133
+ royalty_params[:requests] = [royalty_request]
134
+
135
+ # generate royalty txn
136
+ r = @conn.post('/wallet/transaction/generate', royalty_params.to_json)
137
+ royalty_txn = JSON.parse(r.body)
138
+ box_id = royalty_txn['outputs'][0]['boxId']
139
+ puts "Royalty BoxID: #{box_id}"
140
+
141
+ royalty_box_raw_bytes = nil
142
+
143
+ # send royalty txn
144
+ if real
145
+ r = send_transaction(royalty_txn)
146
+
147
+ # grab royalty box id
148
+ waiting_for_box_count = 0
149
+ waiting_for_box_max = 5
150
+ loop do
151
+ if waiting_for_box_count > waiting_for_box_max
152
+ raise 'Royalty Box never showed up #{box_id}'
153
+ end
154
+ r = get_serialized_box_with_pool(box_id)
155
+ break if r.status == 200
156
+ waiting_for_box_count += 1
157
+ puts "Waiting for Royalty Box #{waiting_for_box_count}"
158
+ sleep 1
159
+ end
160
+
161
+ royalty_box_raw_bytes = JSON.parse(r.body)['bytes']
162
+ puts "Royalty Box Bytes: #{royalty_box_raw_bytes}"
163
+ end
164
+
165
+ nft_request = {
166
+ address: address,
167
+ amount: 1,
168
+ name: ergo_nft.name,
169
+ description: ergo_nft.description,
170
+ decimals: 0,
171
+ registers: {
172
+ R7: "0e020101", # PICTURE NFT
173
+ R8: encoded.sha,
174
+ R9: encoded.ipfs_link,
175
+ },
176
+ }
177
+
178
+ nft_params = {
179
+ requests: [nft_request],
180
+ fee: Sigma::TxBuilder.suggested_tx_fee.to_i64,
181
+ inputsRaw: [royalty_box_raw_bytes],
182
+ }
183
+
184
+ if real
185
+ # finally generate nft transaction with royalty box as input
186
+ @conn.post('/wallet/transaction/generate', nft_params.to_json)
187
+ else
188
+ puts "NFT Mint dry run, would send: \n#{nft_params.inspect}"
189
+ end
190
+ end
191
+
192
+ # @param ergo_nft [ErgoNft]
193
+ def get_encoded_values(ergo_nft, address_ergo_tree: nil)
194
+ raise "address_ergo_tree required!" if address_ergo_tree.nil?
195
+
196
+ #puts "Royalty: #{ergo_nft.royalty} => #{Sigma::Constant.with_i32(ergo_nft.royalty * 10).to_base16_string}"
197
+ # NOTE from EIP-24: (Royalty) R4 of this box is an Int, showing 1000 * royalty percentage of the artwork. e.g, 20 for 2% royalty.
198
+ ErgoEncodedNFT.new(
199
+ ipfs_link: sigma_encode_str(ergo_nft.ipfs_link),
200
+ royalty: Sigma::Constant.with_i32(ergo_nft.royalty * 10).to_base16_string,
201
+ sha: sigma_encode_hex_str(ergo_nft.sha),
202
+ address_ergo_tree: sigma_encode_hex_str(address_ergo_tree),
203
+ )
204
+ end
205
+
206
+ # Some pre-encoded values (sha, address_ergo_tree) are already hex-encoded
207
+ def hex_to_str(hex_str)
208
+ hex_str.scan(/../).map { |hc| hc.hex.chr }.join
209
+ end
210
+
211
+ # Convert hex -> str before encoding with Sigma::Constant
212
+ def sigma_encode_hex_str(hex_str)
213
+ str = hex_to_str(hex_str)
214
+ sigma_encode_str(str)
215
+ end
216
+
217
+ def sigma_encode_str(str)
218
+ Sigma::Constant.with_bytes(
219
+ str.each_byte.map { |c| c }
220
+ ).to_base16_string
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,57 @@
1
+ require 'dotenv/load'
2
+ require 'faraday'
3
+ require 'faraday/net_http'
4
+ require 'faraday/multipart'
5
+ Faraday.default_adapter = :net_http
6
+
7
+ module SigmaMint
8
+ # Simple Pinata Client for pinning files to IPFS
9
+ class PinataClient
10
+ def initialize
11
+ # https://docs.pinata.cloud/#connecting-to-the-api
12
+ @conn = Faraday.new(
13
+ url: 'https://api.pinata.cloud',
14
+ headers: {'Authorization' => "Bearer #{ENV['PINATA_JWT']}"},
15
+ ) do |f|
16
+ f.request :multipart, {}
17
+ end
18
+ end
19
+
20
+ # https://docs.pinata.cloud/api-pinning/pin-file
21
+ def pin!(filepath, name, metadata: {}, cid_version: '1', mime_type: 'image/png')
22
+ file = Faraday::Multipart::FilePart.new(filepath, mime_type)
23
+
24
+ pinata_options = {
25
+ cidVersion: cid_version,
26
+ wrapWithDirectory: false,
27
+ customPinPolicy: {
28
+ regions: [
29
+ {
30
+ id: 'FRA1',
31
+ desiredReplicationCount: 2
32
+ },
33
+ {
34
+ id: 'NYC1',
35
+ desiredReplicationCount: 2
36
+ }]}
37
+ }
38
+
39
+ pinata_metadata = {
40
+ name: name,
41
+ keyvalues: metadata
42
+ }
43
+
44
+ params = {
45
+ file: file,
46
+ pinataOptions: pinata_options,
47
+ pinataMetadata: pinata_metadata,
48
+ }
49
+
50
+ @conn.post('/pinning/pinFileToIPFS', params)
51
+ end
52
+ end
53
+ end
54
+
55
+ #pc = PinataClient.new
56
+ #testpng = "scratch/test_pngs/test_scaled_bf.png"
57
+ #puts pc.pin!(testpng, "pintest_1", metadata: {set: 'Alpha', number: '1'})
data/lib/sigma_mint.rb ADDED
@@ -0,0 +1,5 @@
1
+ module SigmaMint
2
+ require_relative 'sigma_mint/pinata_client'
3
+ require_relative 'sigma_mint/ergo_node_client'
4
+ require_relative 'sigma_mint/ergo_nft'
5
+ end
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'sigma_mint'
3
+ s.version = '0.1.1'
4
+ s.summary = "Simple ERGO asset creaiton library"
5
+ s.description = "Provides basic utilities for minting assets on the ERGO blockchain."
6
+ s.authors = ["Dark Lord of Programming"]
7
+ s.email = 'thedlop@sent.com'
8
+ s.homepage = 'https://github.com/thedlop/sigma_mint'
9
+ s.license = 'MIT'
10
+ s.files = Dir.glob("{lib}/**/*")
11
+ s.files += %w(sigma_mint.gemspec README.md LICENSE)
12
+ s.add_dependency 'dotenv', '~> 2.7.6'
13
+ s.add_dependency 'sigma_rb', '0.1.5'
14
+ s.add_dependency 'faraday', '~> 2.2.0'
15
+ s.add_dependency 'faraday-multipart', '~> 1.0.3'
16
+ s.add_development_dependency 'test-unit', '~> 3.5'
17
+ s.add_development_dependency 'yard', '~> 0.9.20'
18
+ s.test_files = Dir["tests/**/*.rb"]
19
+ s.require_paths = ["lib"]
20
+ s.required_ruby_version = '>= 3.0.1'
21
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sigma_mint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Dark Lord of Programming
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dotenv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.7.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.7.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: sigma_rb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.2.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.2.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday-multipart
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: test-unit
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.9.20
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.9.20
97
+ description: Provides basic utilities for minting assets on the ERGO blockchain.
98
+ email: thedlop@sent.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - LICENSE
104
+ - README.md
105
+ - lib/sigma_mint.rb
106
+ - lib/sigma_mint/ergo_nft.rb
107
+ - lib/sigma_mint/ergo_node_client.rb
108
+ - lib/sigma_mint/pinata_client.rb
109
+ - sigma_mint.gemspec
110
+ homepage: https://github.com/thedlop/sigma_mint
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 3.0.1
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubygems_version: 3.2.33
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: Simple ERGO asset creaiton library
133
+ test_files: []