flow_client 0.1.1 → 0.2.2

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.
data/bin/setup CHANGED
File without changes
@@ -0,0 +1,12 @@
1
+ version: "3"
2
+ services:
3
+ emulator:
4
+ image: gcr.io/flow-container-registry/emulator:latest
5
+ ports:
6
+ - "8080:8080"
7
+ - "3569:3569"
8
+ environment:
9
+ - FLOW_SERVICEPRIVATEKEY=4d9287571c8bff7482ffc27ef68d5b4990f9bd009a1e9fa812aae08ba167d57f
10
+ - FLOW_SERVICEKEYSIGALGO=ECDSA_P256
11
+ - FLOW_SERVICEKEYHASHALGO=SHA3_256
12
+ - FLOW_VERBOSE=true
data/flow.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "emulators": {
3
+ "default": {
4
+ "port": 3569,
5
+ "serviceAccount": "emulator-account"
6
+ }
7
+ },
8
+ "contracts": {},
9
+ "networks": {
10
+ "emulator": "127.0.0.1:3569",
11
+ "mainnet": "access.mainnet.nodes.onflow.org:9000",
12
+ "testnet": "access.devnet.nodes.onflow.org:9000"
13
+ },
14
+ "accounts": {
15
+ "emulator-account": {
16
+ "address": "f8d6e0586b0a20c7",
17
+ "key": "38a7eef2053e165c2d635fb471fb26c91d9da2914a8f13c58ec210412adb2067"
18
+ }
19
+ },
20
+ "deployments": {}
21
+ }
data/flow_client.gemspec CHANGED
@@ -7,7 +7,6 @@ Gem::Specification.new do |spec|
7
7
  spec.version = FlowClient::VERSION
8
8
  spec.authors = ["Nico du Plessis"]
9
9
  spec.email = ["nico@glucode.com"]
10
-
11
10
  spec.summary = "A Ruby client for the Flow blockchain"
12
11
  spec.description = "A Ruby client for the Flow blockchain"
13
12
  spec.homepage = "https://github.com/glucode/flow_client"
@@ -30,14 +29,11 @@ Gem::Specification.new do |spec|
30
29
  spec.require_paths = ["lib"]
31
30
 
32
31
  # Uncomment to register a new dependency of your gem
33
- spec.add_dependency "digest-sha3"
34
- spec.add_dependency "ecdsa"
35
32
  spec.add_dependency "grpc"
36
33
  spec.add_dependency "grpc-tools"
37
34
  spec.add_dependency "json"
38
35
  spec.add_dependency "openssl"
39
36
  spec.add_dependency "rlp"
40
- spec.add_dependency "rspec"
41
37
 
42
38
  # For more information and examples about making a new gem, checkout our
43
39
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -0,0 +1,144 @@
1
+ /**
2
+
3
+ ## The Flow Non-Fungible Token standard
4
+
5
+ ## `NonFungibleToken` contract interface
6
+
7
+ The interface that all non-fungible token contracts could conform to.
8
+ If a user wants to deploy a new nft contract, their contract would need
9
+ to implement the NonFungibleToken interface.
10
+
11
+ Their contract would have to follow all the rules and naming
12
+ that the interface specifies.
13
+
14
+ ## `NFT` resource
15
+
16
+ The core resource type that represents an NFT in the smart contract.
17
+
18
+ ## `Collection` Resource
19
+
20
+ The resource that stores a user's NFT collection.
21
+ It includes a few functions to allow the owner to easily
22
+ move tokens in and out of the collection.
23
+
24
+ ## `Provider` and `Receiver` resource interfaces
25
+
26
+ These interfaces declare functions with some pre and post conditions
27
+ that require the Collection to follow certain naming and behavior standards.
28
+
29
+ They are separate because it gives the user the ability to share a reference
30
+ to their Collection that only exposes the fields and functions in one or more
31
+ of the interfaces. It also gives users the ability to make custom resources
32
+ that implement these interfaces to do various things with the tokens.
33
+
34
+ By using resources and interfaces, users of NFT smart contracts can send
35
+ and receive tokens peer-to-peer, without having to interact with a central ledger
36
+ smart contract.
37
+
38
+ To send an NFT to another user, a user would simply withdraw the NFT
39
+ from their Collection, then call the deposit function on another user's
40
+ Collection to complete the transfer.
41
+
42
+ */
43
+
44
+ // The main NFT contract interface. Other NFT contracts will
45
+ // import and implement this interface
46
+ //
47
+ pub contract interface NonFungibleToken {
48
+
49
+ // The total number of tokens of this type in existence
50
+ pub var totalSupply: UInt64
51
+
52
+ // Event that emitted when the NFT contract is initialized
53
+ //
54
+ pub event ContractInitialized()
55
+
56
+ // Event that is emitted when a token is withdrawn,
57
+ // indicating the owner of the collection that it was withdrawn from.
58
+ //
59
+ // If the collection is not in an account's storage, `from` will be `nil`.
60
+ //
61
+ pub event Withdraw(id: UInt64, from: Address?)
62
+
63
+ // Event that emitted when a token is deposited to a collection.
64
+ //
65
+ // It indicates the owner of the collection that it was deposited to.
66
+ //
67
+ pub event Deposit(id: UInt64, to: Address?)
68
+
69
+ // Interface that the NFTs have to conform to
70
+ //
71
+ pub resource interface INFT {
72
+ // The unique ID that each NFT has
73
+ pub let id: UInt64
74
+ }
75
+
76
+ // Requirement that all conforming NFT smart contracts have
77
+ // to define a resource called NFT that conforms to INFT
78
+ pub resource NFT: INFT {
79
+ pub let id: UInt64
80
+ }
81
+
82
+ // Interface to mediate withdraws from the Collection
83
+ //
84
+ pub resource interface Provider {
85
+ // withdraw removes an NFT from the collection and moves it to the caller
86
+ pub fun withdraw(withdrawID: UInt64): @NFT {
87
+ post {
88
+ result.id == withdrawID: "The ID of the withdrawn token must be the same as the requested ID"
89
+ }
90
+ }
91
+ }
92
+
93
+ // Interface to mediate deposits to the Collection
94
+ //
95
+ pub resource interface Receiver {
96
+
97
+ // deposit takes an NFT as an argument and adds it to the Collection
98
+ //
99
+ pub fun deposit(token: @NFT)
100
+ }
101
+
102
+ // Interface that an account would commonly
103
+ // publish for their collection
104
+ pub resource interface CollectionPublic {
105
+ pub fun deposit(token: @NFT)
106
+ pub fun getIDs(): [UInt64]
107
+ pub fun borrowNFT(id: UInt64): &NFT
108
+ }
109
+
110
+ // Requirement for the the concrete resource type
111
+ // to be declared in the implementing contract
112
+ //
113
+ pub resource Collection: Provider, Receiver, CollectionPublic {
114
+
115
+ // Dictionary to hold the NFTs in the Collection
116
+ pub var ownedNFTs: @{UInt64: NFT}
117
+
118
+ // withdraw removes an NFT from the collection and moves it to the caller
119
+ pub fun withdraw(withdrawID: UInt64): @NFT
120
+
121
+ // deposit takes a NFT and adds it to the collections dictionary
122
+ // and adds the ID to the id array
123
+ pub fun deposit(token: @NFT)
124
+
125
+ // getIDs returns an array of the IDs that are in the collection
126
+ pub fun getIDs(): [UInt64]
127
+
128
+ // Returns a borrowed reference to an NFT in the collection
129
+ // so that the caller can read data and call methods from it
130
+ pub fun borrowNFT(id: UInt64): &NFT {
131
+ pre {
132
+ self.ownedNFTs[id] != nil: "NFT does not exist in the collection!"
133
+ }
134
+ }
135
+ }
136
+
137
+ // createEmptyCollection creates an empty Collection
138
+ // and returns it to the caller so that they can own NFTs
139
+ pub fun createEmptyCollection(): @Collection {
140
+ post {
141
+ result.getIDs().length == 0: "The created collection must be empty!"
142
+ }
143
+ }
144
+ }
@@ -0,0 +1,16 @@
1
+ transaction(publicKey: String, weight: UFix64) {
2
+ prepare(signer: AuthAccount) {
3
+ // signer.addPublicKey(publicKey.decodeHex())
4
+
5
+ let key = PublicKey(
6
+ publicKey: publicKey.decodeHex(),
7
+ signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
8
+ )
9
+
10
+ signer.keys.add(
11
+ publicKey: key,
12
+ hashAlgorithm: HashAlgorithm.SHA3_256,
13
+ weight: weight
14
+ )
15
+ }
16
+ }
@@ -0,0 +1,5 @@
1
+ transaction(name: String, code: String) {
2
+ prepare(signer: AuthAccount) {
3
+ signer.contracts.add(name: name, code: code.decodeHex())
4
+ }
5
+ }
@@ -0,0 +1,21 @@
1
+ transaction(publicKeys: [String], contracts: {String: String}) {
2
+ prepare(signer: AuthAccount) {
3
+ let acct = AuthAccount(payer: signer)
4
+ for keyHex in publicKeys {
5
+ let key = PublicKey(
6
+ publicKey: keyHex.decodeHex(),
7
+ signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
8
+ )
9
+
10
+ acct.keys.add(
11
+ publicKey: key,
12
+ hashAlgorithm: HashAlgorithm.SHA3_256,
13
+ weight: 1000.0
14
+ )
15
+ }
16
+
17
+ for contract in contracts.keys {
18
+ acct.contracts.add(name: contract, code: contracts[contract]!.decodeHex())
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,5 @@
1
+ transaction(name: String) {
2
+ prepare(signer: AuthAccount) {
3
+ signer.contracts.remove(name: name)
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ transaction(name: String, code: String) {
2
+ prepare(signer: AuthAccount) {
3
+ signer.contracts.update__experimental(name: name, code: code.decodeHex())
4
+ }
5
+ }
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowClient
4
+ class AccountKey
5
+ attr_accessor :public_key,
6
+ :index, :sequence_number,
7
+ :sign_algo, :hash_algo,
8
+ :weight, :revoked
9
+
10
+ def initialize(public_key: nil, index: nil, sequence_number: nil, weight: 1000, revoked: false, hash_algo: FlowClient::Crypto::HashAlgos::SHA3_256)
11
+ @public_key = public_key
12
+ @index = index
13
+ @sequence_number = sequence_number
14
+ @weight = weight
15
+ @revoked = revoked
16
+ @hash_algo = hash_algo
17
+ end
18
+ end
19
+
20
+ class Account
21
+ attr_accessor :address, :balance, :keys, :contracts
22
+
23
+ def initialize(address: nil, balance: nil, keys: [], contracts: {})
24
+ @keys = keys
25
+ @address = address
26
+ @balance = balance
27
+ @contracts = contracts
28
+ @contracts = {}
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlowClient
4
+ # Represents a block
5
+ class Block
6
+ attr_accessor :id,
7
+ :parent_id,
8
+ :height,
9
+ :timestamp,
10
+ :collection_guarantees,
11
+ :block_seals,
12
+ :signatures
13
+
14
+ def initialize
15
+ @id = nil
16
+ @parent_id = nil
17
+ @height = nil
18
+ @timestamp = nil
19
+ @collection_guarantees = []
20
+ @block_seals = []
21
+ @signatures = []
22
+ end
23
+
24
+ def self.parse_grpc_block_response(block_response)
25
+ block = Block.new
26
+ block.id = block_response.block.id.unpack1("H*")
27
+ block.parent_id = block_response.block.parent_id.unpack1("H*")
28
+ block.height = block_response.block.height
29
+ block.timestamp = FlowClient::Utils.parse_protobuf_timestamp(block_response.block.timestamp)
30
+ block.collection_guarantees = block_response.block.collection_guarantees.to_a.map do |cg|
31
+ FlowClient::CollectionGuarantee.parse_grpc_type(cg)
32
+ end
33
+ block.block_seals = block_response.block.block_seals.to_a.map do |seal|
34
+ FlowClient::BlockSeal.parse_grpc_type(seal)
35
+ end
36
+ block.signatures = block_response.block.signatures.to_a.map { |sig| sig.unpack1("H*") }
37
+ block
38
+ end
39
+ end
40
+
41
+ # Represents a block seal
42
+ class BlockSeal
43
+ attr_accessor :block_id,
44
+ :execution_receipt_id,
45
+ :execution_receipt_signatures,
46
+ :result_approval_signatures
47
+
48
+ def initialize
49
+ @block_id = nil
50
+ @execution_receipt_id = nil
51
+ @execution_receipt_signatures = []
52
+ @result_approval_signatures = []
53
+ end
54
+
55
+ def self.parse_grpc_type(grpc_type)
56
+ block_seal = BlockSeal.new
57
+ block_seal.block_id = grpc_type.block_id.unpack1("H*")
58
+ block_seal.execution_receipt_id = grpc_type.execution_receipt_id.unpack1("H*")
59
+ block_seal.execution_receipt_signatures = grpc_type.execution_receipt_signatures.to_a.map do |sig|
60
+ sig.unpack1("H*")
61
+ end
62
+ block_seal.result_approval_signatures = grpc_type.result_approval_signatures.to_a.map { |sig| sig.unpack1("H*") }
63
+ block_seal
64
+ end
65
+ end
66
+
67
+ # Represents a block header
68
+ class BlockHeader
69
+ attr_accessor :id, :parent_id, :height, :timestamp
70
+
71
+ def initialize; end
72
+
73
+ def self.parse_grpc_type(grpc_type)
74
+ header = BlockHeader.new
75
+ header.id = grpc_type.id.unpack1("H*")
76
+ header.height = grpc_type.height
77
+ header.timestamp = FlowClient::Utils.parse_protobuf_timestamp(grpc_type.timestamp)
78
+ header
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
4
+
5
+ module FlowClient
6
+ module CadenceType
7
+ # Returns an OpenStruct representing a Cadence String type
8
+ #
9
+ # @example
10
+ # @arg = FlowClient::CadenceType.String("Hello world!")
11
+ #
12
+ # @param [String] the string value
13
+ #
14
+ # @returns [OpenStruct] the Cadence String struct
15
+ def self.String(value)
16
+ OpenStruct.new(type: "String", value: value.to_s)
17
+ end
18
+
19
+ # Returns an OpenStruct representing a Cadence Optional type
20
+ #
21
+ # @example
22
+ # @arg = FlowClient::CadenceType.Optional("Hello world!")
23
+ # @arg = FlowClient::CadenceType.Optional()
24
+ #
25
+ # @param [String] the string value
26
+ #
27
+ # @returns [OpenStruct] the Cadence Optional struct
28
+ def self.Optional(value = nil)
29
+ OpenStruct.new(type: "Optional", value: value)
30
+ end
31
+
32
+ # Returns an OpenStruct representing a Cadence Void type
33
+ #
34
+ # @example
35
+ # @arg = FlowClient::CadenceType.Void()
36
+ #
37
+ # @returns [OpenStruct] the Cadence Void struct
38
+ def self.Void
39
+ OpenStruct.new(type: "Void")
40
+ end
41
+
42
+ def self.Bool(bool)
43
+ OpenStruct.new(type: "Bool", value: bool.to_s.downcase == "true")
44
+ end
45
+
46
+ def self.Address(address)
47
+ OpenStruct.new(type: "Address", value: address.to_s)
48
+ end
49
+
50
+ def self.Int(value)
51
+ OpenStruct.new(type: "Int", value: value.to_s)
52
+ end
53
+
54
+ def self.UInt(value)
55
+ OpenStruct.new(type: "UInt", value: value.to_s)
56
+ end
57
+
58
+ def self.Int8(value)
59
+ OpenStruct.new(type: "Int8", value: value.to_s)
60
+ end
61
+
62
+ def self.UInt8(value)
63
+ OpenStruct.new(type: "UInt8", value: value.to_s)
64
+ end
65
+
66
+ def self.Int16(value)
67
+ OpenStruct.new(type: "Int16", value: value.to_s)
68
+ end
69
+
70
+ def self.UInt16(value)
71
+ OpenStruct.new(type: "UInt16", value: value.to_s)
72
+ end
73
+
74
+ def self.Int32(value)
75
+ OpenStruct.new(type: "Int32", value: value.to_s)
76
+ end
77
+
78
+ def self.UInt32(value)
79
+ OpenStruct.new(type: "UInt32", value: value.to_s)
80
+ end
81
+
82
+ def self.Int64(value)
83
+ OpenStruct.new(type: "Int64", value: value.to_s)
84
+ end
85
+
86
+ def self.UInt64(value)
87
+ OpenStruct.new(type: "UInt64", value: value.to_s)
88
+ end
89
+
90
+ def self.Int128(value)
91
+ OpenStruct.new(type: "Int128", value: value.to_s)
92
+ end
93
+
94
+ def self.UInt128(value)
95
+ OpenStruct.new(type: "UInt128", value: value.to_s)
96
+ end
97
+
98
+ def self.Int256(value)
99
+ OpenStruct.new(type: "Int256", value: value.to_s)
100
+ end
101
+
102
+ def self.UInt256(value)
103
+ OpenStruct.new(type: "UInt256", value: value.to_s)
104
+ end
105
+
106
+ def self.Word8(value)
107
+ OpenStruct.new(type: "Word8", value: value.to_s)
108
+ end
109
+
110
+ def self.Word16(value)
111
+ OpenStruct.new(type: "Word16", value: value.to_s)
112
+ end
113
+
114
+ def self.Word32(value)
115
+ OpenStruct.new(type: "Word32", value: value.to_s)
116
+ end
117
+
118
+ def self.Word64(value)
119
+ OpenStruct.new(type: "Word64", value: value.to_s)
120
+ end
121
+
122
+ def self.Fix64(value)
123
+ OpenStruct.new(type: "Fix64", value: value.to_s)
124
+ end
125
+
126
+ def self.UFix64(value)
127
+ OpenStruct.new(type: "UFix64", value: value.to_s)
128
+ end
129
+
130
+ def self.Array(values)
131
+ OpenStruct.new(type: "Array", value: values)
132
+ end
133
+
134
+ def self.Dictionary(values)
135
+ OpenStruct.new(type: "Dictionary", value: values)
136
+ end
137
+
138
+ def self.DictionaryValue(key, value)
139
+ OpenStruct.new(key: key, value: value)
140
+ end
141
+
142
+ def self.Path(domain, identifier)
143
+ unless %w[storage private public].include? domain.to_s.downcase
144
+ raise raise ArgumentError, "Domain can only be one of storage, private or public"
145
+ end
146
+
147
+ OpenStruct.new(
148
+ type: "Path",
149
+ value: OpenStruct.new(domain: domain, identifier: identifier)
150
+ )
151
+ end
152
+
153
+ def self.Type(type_value)
154
+ OpenStruct.new(type: "Type", value: OpenStruct.new(staticType: type_value.to_s))
155
+ end
156
+
157
+ def self.Capability(path, address, borrow_type)
158
+ OpenStruct.new(
159
+ type: "Capability",
160
+ value: OpenStruct.new(
161
+ path: path.to_s,
162
+ address: address.to_s,
163
+ borrowType: borrow_type.to_s
164
+ )
165
+ )
166
+ end
167
+
168
+ def self.Composite(type, value)
169
+ valid_types = %i[struct resource event contract enum]
170
+ unless valid_types.include? type
171
+ raise ArgumentError, "incorrect type, expected :struct, :resource, :event, :contract or :enum"
172
+ end
173
+
174
+ OpenStruct.new(type: type.to_s.capitalize, value: value)
175
+ end
176
+
177
+ def self.CompositeValue(id, fields)
178
+ OpenStruct.new(id: id, fields: fields)
179
+ end
180
+
181
+ def self.Field(name, value)
182
+ OpenStruct.new(name: name, value: value)
183
+ end
184
+ end
185
+ end