Words Counted: A Ruby Natural Language Processor.

WordsCounted

We are all in the gutter, but some of us are looking at the stars.

-- Oscar Wilde

WordsCounted is a Ruby NLP (natural language processor). WordsCounted lets you implement powerful tokensation strategies with a very flexible tokeniser class.

Are you using WordsCounted to do something interesting? Please tell me about it.

 

Demo

Visit this website for one example of what you can do with WordsCounted.

Features

  • Out of the box, get the following data from any string or readable file, or URL:
    • Token count and unique token count
    • Token densities, frequencies, and lengths
    • Char count and average chars per token
    • The longest tokens and their lengths
    • The most frequent tokens and their frequencies.
  • A flexible way to exclude tokens from the tokeniser. You can pass a string, regexp, symbol, lambda, or an array of any combination of those types for powerful tokenisation strategies.
  • Pass your own regexp rules to the tokeniser if you prefer. The default regexp filters special characters but keeps hyphens and apostrophes. It also plays nicely with diacritics (UTF and unicode characters): Bayrūt is treated as ["Bayrūt"] and not ["Bayr", "ū", "t"], for example.
  • Opens and reads files. Pass in a file path or a url instead of a string.

Installation

Add this line to your application's Gemfile:

gem 'words_counted'

And then execute:

$ bundle

Or install it yourself as:

$ gem install words_counted

Usage

Pass in a string or a file path, and an optional filter and/or regexp.

counter = WordsCounted.count(
  "We are all in the gutter, but some of us are looking at the stars."
)

# Using a file
counter = WordsCounted.from_file("path/or/url/to/my/file.txt")

.count and .from_file are convenience methods that take an input, tokenise it, and return an instance of WordsCounted::Counter initialized with the tokens. The WordsCounted::Tokeniser and WordsCounted::Counter classes can be used alone, however.

API

WordsCounted

WordsCounted.count(input, options = {})

Tokenises input and initializes a WordsCounted::Counter object with the resulting tokens.

counter = WordsCounted.count("Hello Beirut!")

Accepts two options: exclude and regexp. See Excluding tokens from the analyser and Passing in a custom regexp respectively.

WordsCounted.from_file(path, options = {})

Reads and tokenises a file, and initializes a WordsCounted::Counter object with the resulting tokens.

counter = WordsCounted.from_file("hello_beirut.txt")

Accepts the same options as .count.

Tokeniser

The tokeniser allows you to tokenise text in a variety of ways. You can pass in your own rules for tokenisation, and apply a powerful filter with any combination of rules as long as they can boil down into a lambda.

Out of the box the tokeniser includes only alpha chars. Hyphenated tokens and tokens with apostrophes are considered a single token.

#tokenise([pattern: TOKEN_REGEXP, exclude: nil])

tokeniser = WordsCounted::Tokeniser.new("Hello Beirut!").tokenise

# With `exclude`
tokeniser = WordsCounted::Tokeniser.new("Hello Beirut!").tokenise(exclude: "hello")

# With `pattern`
tokeniser = WordsCounted::Tokeniser.new("I <3 Beirut!").tokenise(pattern: /[a-z]/i)

See Excluding tokens from the analyser and Passing in a custom regexp for more information.

Counter

The WordsCounted::Counter class allows you to collect various statistics from an array of tokens.

#token_count

Returns the token count of a given string.

counter.token_count #=> 15

#token_frequency

Returns a sorted (unstable) two-dimensional array where each element is a token and its frequency. The array is sorted by frequency in descending order.

counter.token_frequency

[
  ["the", 2],
  ["are", 2],
  ["we",  1],
  # ...
  ["all", 1]
]

#most_frequent_tokens

Returns a hash where each key-value pair is a token and its frequency.

counter.most_frequent_tokens

{ "are" => 2, "the" => 2 }

#token_lengths

Returns a sorted (unstable) two-dimentional array where each element contains a token and its length. The array is sorted by length in descending order.

counter.token_lengths

[
  ["looking", 7],
  ["gutter",  6],
  ["stars",   5],
  # ...
  ["in",      2]
]

#longest_tokens

Returns a hash where each key-value pair is a token and its length.

counter.longest_tokens

{ "looking" => 7 }

#token_density([ precision: 2 ])

Returns a sorted (unstable) two-dimentional array where each element contains a token and its density as a float, rounded to a precision of two. The array is sorted by density in descending order. It accepts a precision argument, which must be a float.

counter.token_density

[
  ["are",     0.13],
  ["the",     0.13],
  ["but",     0.07 ],
  # ...
  ["we",      0.07 ]
]

#char_count

Returns the char count of tokens.

counter.char_count #=> 76

#average_chars_per_token([ precision: 2 ])

Returns the average char count per token rounded to two decimal places. Accepts a precision argument which defaults to two. Precision must be a float.

counter.average_chars_per_token #=> 4

#uniq_token_count

Returns the number of unique tokens.

counter.uniq_token_count #=> 13

Excluding tokens from the tokeniser

You can exclude anything you want from the input by passing the exclude option. The exclude option accepts a variety of filters and is extremely flexible.

  1. A space-delimited string. The filter will normalise the string.
  2. A regular expression.
  3. A lambda.
  4. A symbol that names a predicate method. For example :odd?.
  5. An array of any combination of the above.
tokeniser =
  WordsCounted::Tokeniser.new(
    "Magnificent! That was magnificent, Trevor."
  )

# Using a string
tokeniser.tokenise(exclude: "was magnificent")
# => ["that", "trevor"]

# Using a regular expression
tokeniser.tokenise(exclude: /trevor/)
# => ["magnificent", "that", "was", "magnificent"]

# Using a lambda
tokeniser.tokenise(exclude: ->(t) { t.length < 4 })
# => ["magnificent", "that", "magnificent", "trevor"]

# Using symbol
tokeniser = WordsCounted::Tokeniser.new("Hello! محمد")
tokeniser.tokenise(exclude: :ascii_only?)
# => ["محمد"]

# Using an array
tokeniser = WordsCounted::Tokeniser.new(
  "Hello! اسماءنا هي محمد، كارولينا، سامي، وداني"
)
tokeniser.tokenise(
  exclude: [:ascii_only?, /محمد/, ->(t) { t.length > 6}, "و"]
)
# => ["هي", "سامي", "وداني"]

Passing in a custom regexp

The default regexp accounts for letters, hyphenated tokens, and apostrophes. This means twenty-one is treated as one token. So is Mohamad's.

/[\p{Alpha}\-']+/

You can pass your own criteria as a Ruby regular expression to split your string as desired.

For example, if you wanted to include numbers, you can override the regular expression:

counter = WordsCounted.count("Numbers 1, 2, and 3", pattern: /[\p{Alnum}\-']+/)
counter.tokens
#=> ["numbers", "1", "2", "and", "3"]

Opening and reading files

Use the from_file method to open files. from_file accepts the same options as .count. The file path can be a URL.

counter = WordsCounted.from_file("url/or/path/to/file.text")

Gotchas

A hyphen used in leu of an em or en dash will form part of the token. This affects the tokeniser algorithm.

counter = WordsCounted.count("How do you do?-you are well, I see.")
counter.token_frequency

[
  ["do",   2],
  ["how",  1],
  ["you",  1],
  ["-you", 1], # WTF, mate!
  ["are",  1],
  # ...
]

In this example -you and you are separate tokens. Also, the tokeniser does not include numbers by default. Remember that you can pass your own regular expression if the default behaviour does not fit your needs.

A note on case sensitivity

The program will normalise (downcase) all incoming strings for consistency and filters.

Roadmap

Ability to open URLs

def self.from_url
  # open url and send string here after removing html
end

Contributors

See contributors.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Author: abitdodgy
Source code: https://github.com/abitdodgy/words_counted
License: MIT license

#ruby  #ruby-on-rails 

Words Counted: A Ruby Natural Language Processor.
Royce  Reinger

Royce Reinger

1659197640

Token_phrase: A Token Phrase Generator

TokenPhrase (v1.0.6)

TokenPhrase is a simple gem that generates unique phrases for you to use in your app as tokens.

"Why?" you may be asking. Why not? Token phrases give your app a little personality and make support a lot easier.

Installation

Add this line to your application's Gemfile:

gem 'token_phrase'

And then execute:

$ bundle

Or install it yourself as:

$ gem install token_phrase

TokenPhrase.generate(separator = nil, dictionaries = {})

Defaults

By default, TokenPhrase uses the included dictionaries, separates everything with a hyphen (-), and appends a random number between 1 and 1,000,000 :

TokenPhrase.generate
=> "groovy-red-fractal-fork-101589"

5.times { p TokenPhrase.generate }
"bodacious-blue-spotted-envelope-428491"
"magnetic-burnt-orange-polka-dotted-spider-wolf-268974"
"conservative-plum-paisley-banana-slug-771632"
"bluetooth-chiffon-houndstooth-wolf-spider-700306"
"sweet-violet-tartan-coyote-16101"

With the current dictionaries and numbers, there are 4,199,040,000,000 (four trillion!) unique possibilites.

Separators

If you would like a different separator, just pass a string as an argument:

TokenPhrase.generate('$')
=> "groovy$salmon$paisley$cape$848536"

TokenPhrase.generate('MARSIPAN')
=> "sugarfilledMARSIPANcrimsonMARSIPANstripedMARSIPANfrogMARSIPAN860203"

Dictionaries

TokenPhrase combines words from four different dictionaries:

  • :adjectives
  • :colors
  • :patterns
  • :nouns

If you want to replace the dictionary, just pass a hash with an array as an argument:

TokenPhrase.generate :adjectives => %w(glowing)
=> "glowing-peach-glossy-barracuda-743220"

5.times { p TokenPhrase.generate :nouns => %w(Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto) }
"stupendous-crimson-tartan-Uranus-431203"
"tailored-khaki-fractal-Neptune-957683"
"better-almond-striped-Pluto-299491"
"soft-chiffon-tartan-Saturn-29752"
"tailored-azure-honeycomb-Saturn-668823"

You can pass multiple dictionaries:

5.times { p TokenPhrase.generate :colors => %w(black white), :nouns => %w(cat dog) }
"windy-white-satin-dog-663888"
"exciting-black-spotted-cat-502218"
"sour-white-houndstooth-cat-591001"
"thunderous-white-pinstriped-cat-375006"
"grandpas-white-honeycomb-cat-23992"

And you can, of course pass a separator before the dictionaries:

5.times { p TokenPhrase.generate '^^^', :patterns => %w(striped), :adjectives =>%w(great awesome) }
"awesome^^^aquamarine^^^striped^^^pen^^^345915"
"great^^^salmon^^^striped^^^pants^^^852927"
"great^^^white^^^striped^^^spider^^^wolf^^^646401"
"awesome^^^blue^^^striped^^^people^^^314195"
"awesome^^^aqua^^^striped^^^wolverine^^^113478"

Numbers

To help with uniqueness, a random number is added to the token by default. This may not be your cup of tea, so if you pass :numbers => false with the dictionaries, you can remove the number:

TokenPhrase.generate(:numbers => false)
=> "energetic-yellow-pinstriped-skunk"

Dictionary Methods

If you like the existing dictionaries, but want to add your own words, you can use get an array of each dictionary:

  • TokenPhrase.adjectives(more_adjectives = nil)
  • TokenPhrase.colors(more_colors = nil)
  • TokenPhrase.patterns(more_patterns = nil)
  • TokenPhrase.nouns(more_nouns = nil)

Each of these dictionary methods accept an array as an argument that will merge the existing dictionary with your array

your_patterns = TokenPhrase.patterns %w(magic-eye)
2.times { p TokenPhrase.generate :patterns => your_patterns }
"prickly-magenta-matte-space-heater-687093"
"stupendous-aquamarine-magic-eye-skunk-690072"

TokenPhrase.permutations(dictionaries = {})

If you want to see how many unique combinations you can generate, use the permutations method. Just pass it the dictionaries hash the same way you would use TokenPhrase.generate:

TokenPhrase.permutations
=> 4199040000000

TokenPhrase.permutations :nouns => %w(cat dog)
=> 87480000000

TokenPhrase.permutations :nouns => %w(scooter boat car vroom-vroom), :numbers => false
=> 174960

Rails Uniqueness

The simplest way to create a unique token for your models is to add a before_create filter to your model:

#assuming your Thing model has a token property
class Thing < ActiveRecord::Base
  before_create :generate_token
  
  private
  def generate_token
    begin
      self.token = TokenPhrase.generate
    end while self.class.exists?(token: token)
  end
end

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Thanks

Thanks to benolee for refactoring the generator model!

Author: Genericsteele
Source Code: https://github.com/genericsteele/token_phrase 
License: MIT license

#ruby #token 

Token_phrase: A Token Phrase Generator
Royce  Reinger

Royce Reinger

1658068560

WordsCounted: A Ruby Natural Language Processor

WordsCounted

We are all in the gutter, but some of us are looking at the stars.

-- Oscar Wilde

WordsCounted is a Ruby NLP (natural language processor). WordsCounted lets you implement powerful tokensation strategies with a very flexible tokeniser class.

Features

  • Out of the box, get the following data from any string or readable file, or URL:
    • Token count and unique token count
    • Token densities, frequencies, and lengths
    • Char count and average chars per token
    • The longest tokens and their lengths
    • The most frequent tokens and their frequencies.
  • A flexible way to exclude tokens from the tokeniser. You can pass a string, regexp, symbol, lambda, or an array of any combination of those types for powerful tokenisation strategies.
  • Pass your own regexp rules to the tokeniser if you prefer. The default regexp filters special characters but keeps hyphens and apostrophes. It also plays nicely with diacritics (UTF and unicode characters): Bayrūt is treated as ["Bayrūt"] and not ["Bayr", "ū", "t"], for example.
  • Opens and reads files. Pass in a file path or a url instead of a string.

Installation

Add this line to your application's Gemfile:

gem 'words_counted'

And then execute:

$ bundle

Or install it yourself as:

$ gem install words_counted

Usage

Pass in a string or a file path, and an optional filter and/or regexp.

counter = WordsCounted.count(
  "We are all in the gutter, but some of us are looking at the stars."
)

# Using a file
counter = WordsCounted.from_file("path/or/url/to/my/file.txt")

.count and .from_file are convenience methods that take an input, tokenise it, and return an instance of WordsCounted::Counter initialized with the tokens. The WordsCounted::Tokeniser and WordsCounted::Counter classes can be used alone, however.

API

WordsCounted

WordsCounted.count(input, options = {})

Tokenises input and initializes a WordsCounted::Counter object with the resulting tokens.

counter = WordsCounted.count("Hello Beirut!")

Accepts two options: exclude and regexp. See Excluding tokens from the analyser and Passing in a custom regexp respectively.

WordsCounted.from_file(path, options = {})

Reads and tokenises a file, and initializes a WordsCounted::Counter object with the resulting tokens.

counter = WordsCounted.from_file("hello_beirut.txt")

Accepts the same options as .count.

Tokeniser

The tokeniser allows you to tokenise text in a variety of ways. You can pass in your own rules for tokenisation, and apply a powerful filter with any combination of rules as long as they can boil down into a lambda.

Out of the box the tokeniser includes only alpha chars. Hyphenated tokens and tokens with apostrophes are considered a single token.

#tokenise([pattern: TOKEN_REGEXP, exclude: nil])

tokeniser = WordsCounted::Tokeniser.new("Hello Beirut!").tokenise

# With `exclude`
tokeniser = WordsCounted::Tokeniser.new("Hello Beirut!").tokenise(exclude: "hello")

# With `pattern`
tokeniser = WordsCounted::Tokeniser.new("I <3 Beirut!").tokenise(pattern: /[a-z]/i)

See Excluding tokens from the analyser and Passing in a custom regexp for more information.

Counter

The WordsCounted::Counter class allows you to collect various statistics from an array of tokens.

#token_count

Returns the token count of a given string.

counter.token_count #=> 15

#token_frequency

Returns a sorted (unstable) two-dimensional array where each element is a token and its frequency. The array is sorted by frequency in descending order.

counter.token_frequency

[
  ["the", 2],
  ["are", 2],
  ["we",  1],
  # ...
  ["all", 1]
]

#most_frequent_tokens

Returns a hash where each key-value pair is a token and its frequency.

counter.most_frequent_tokens

{ "are" => 2, "the" => 2 }

#token_lengths

Returns a sorted (unstable) two-dimentional array where each element contains a token and its length. The array is sorted by length in descending order.

counter.token_lengths

[
  ["looking", 7],
  ["gutter",  6],
  ["stars",   5],
  # ...
  ["in",      2]
]

#longest_tokens

Returns a hash where each key-value pair is a token and its length.

counter.longest_tokens

{ "looking" => 7 }

#token_density([ precision: 2 ])

Returns a sorted (unstable) two-dimentional array where each element contains a token and its density as a float, rounded to a precision of two. The array is sorted by density in descending order. It accepts a precision argument, which must be a float.

counter.token_density

[
  ["are",     0.13],
  ["the",     0.13],
  ["but",     0.07 ],
  # ...
  ["we",      0.07 ]
]

#char_count

Returns the char count of tokens.

counter.char_count #=> 76

#average_chars_per_token([ precision: 2 ])

Returns the average char count per token rounded to two decimal places. Accepts a precision argument which defaults to two. Precision must be a float.

counter.average_chars_per_token #=> 4

#uniq_token_count

Returns the number of unique tokens.

counter.uniq_token_count #=> 13

Excluding tokens from the tokeniser

You can exclude anything you want from the input by passing the exclude option. The exclude option accepts a variety of filters and is extremely flexible.

  1. A space-delimited string. The filter will normalise the string.
  2. A regular expression.
  3. A lambda.
  4. A symbol that names a predicate method. For example :odd?.
  5. An array of any combination of the above.
tokeniser =
  WordsCounted::Tokeniser.new(
    "Magnificent! That was magnificent, Trevor."
  )

# Using a string
tokeniser.tokenise(exclude: "was magnificent")
# => ["that", "trevor"]

# Using a regular expression
tokeniser.tokenise(exclude: /trevor/)
# => ["magnificent", "that", "was", "magnificent"]

# Using a lambda
tokeniser.tokenise(exclude: ->(t) { t.length < 4 })
# => ["magnificent", "that", "magnificent", "trevor"]

# Using symbol
tokeniser = WordsCounted::Tokeniser.new("Hello! محمد")
tokeniser.tokenise(exclude: :ascii_only?)
# => ["محمد"]

# Using an array
tokeniser = WordsCounted::Tokeniser.new(
  "Hello! اسماءنا هي محمد، كارولينا، سامي، وداني"
)
tokeniser.tokenise(
  exclude: [:ascii_only?, /محمد/, ->(t) { t.length > 6}, "و"]
)
# => ["هي", "سامي", "وداني"]

Passing in a custom regexp

The default regexp accounts for letters, hyphenated tokens, and apostrophes. This means twenty-one is treated as one token. So is Mohamad's.

/[\p{Alpha}\-']+/

You can pass your own criteria as a Ruby regular expression to split your string as desired.

For example, if you wanted to include numbers, you can override the regular expression:

counter = WordsCounted.count("Numbers 1, 2, and 3", pattern: /[\p{Alnum}\-']+/)
counter.tokens
#=> ["numbers", "1", "2", "and", "3"]

Opening and reading files

Use the from_file method to open files. from_file accepts the same options as .count. The file path can be a URL.

counter = WordsCounted.from_file("url/or/path/to/file.text")

Gotchas

A hyphen used in leu of an em or en dash will form part of the token. This affects the tokeniser algorithm.

counter = WordsCounted.count("How do you do?-you are well, I see.")
counter.token_frequency

[
  ["do",   2],
  ["how",  1],
  ["you",  1],
  ["-you", 1], # WTF, mate!
  ["are",  1],
  # ...
]

In this example -you and you are separate tokens. Also, the tokeniser does not include numbers by default. Remember that you can pass your own regular expression if the default behaviour does not fit your needs.

A note on case sensitivity

The program will normalise (downcase) all incoming strings for consistency and filters.

Roadmap

Ability to open URLs

def self.from_url
  # open url and send string here after removing html
end

Are you using WordsCounted to do something interesting? Please tell me about it.

Gem Version 

RubyDoc documentation.

Demo

Visit this website for one example of what you can do with WordsCounted.


Contributors

See contributors.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Author: Abitdodgy
Source Code: https://github.com/abitdodgy/words_counted 
License: MIT license

#ruby #nlp 

WordsCounted: A Ruby Natural Language Processor

Cakephp-token-verify: Token Verify plugin for CakePHP3

Token Verify plugin for CakePHP3

JWT for mail authentication.

Easily issue tokens(JWT) that can be used for mail authentication.
No need for token field in table.
one-time/url-safe/safety :+1:

Requirements

  • PHP 7.0+
  • CakePHP 3.0.0+

Installation

composer require mosaxiv/cakephp-token-verify

Example

reset password

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY, # Required
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    created DATETIME,
    modified DATETIME # Required
);
// app/src/Model/Entity/User.php

use Token\Model\Entity\TokenTrait;

class User extends Entity
{
    use TokenTrait;
}
// app/src/Controller/UsersController.php

use Cake\Routing\Router;
use Token\Util\Token;

class UsersController extends AppController
{

    public function forgotPassword()
    {
        if ($this->request->is('post')) {
            $email = $this->request->getData('email');
            $user = $this->Users->findByEmail($email)->first();
            if ($user) {
                $token = $user->tokenGenerate();
                $url = Router::url(['controller' => 'User', 'action' => 'resetPassword', $token], true);
                // send email
            }
        }
    }

    public function resetPassword($token)
    {
        $user = $this->Users->get(Token::getId($token));
        if (!$user->tokenVerify($token)) {
            throw new \Cake\Network\Exception\NotFoundException();
        }

        if ($this->request->is('post')) {
            $user = $this->Users->patchEntity($user, $this->request->getData());
            if ($this->Users->save($user)) {
                // success
            } else {
                // error
            }
        }
    }
}

Usage

Required database field

  • id field
  • modified field

By using modified field, JWT can be used as one-time tokens.
JWT should be discarded when the table is updated.

Token\Model\Entity\TokenTrait

Used in entity.

tokenGenerate($minits = 10)

// token generate(default token expiration in 10 minits)
$token = $entity->tokenGenerate();

// token generate(token expiration in 60 minits)
$token = $entity->tokenGenerate(60);

tokenVerify($token)

$user->tokenVerify($token) // true or false

setTokenData($name, $value)

※ It does not encrypt the set data

$user->setTokenData('test', 'testdata')

Token\Util\Token

Token::getId($token)

Token::getId($token) // id or false

Token::getData($token, $name)

Token::getData($token, 'test') // data or false

Author: Mosaxiv
Source Code: https://github.com/mosaxiv/cakephp-token-verify 
License: MIT license

#php #cakephp #token 

Cakephp-token-verify: Token Verify plugin for CakePHP3
Ngoc  Anh

Ngoc Anh

1655450820

Vestlab Là Gì | Công Cụ Tổng Hợp Lịch Trình Phát Hành Mã Thông Báo

Trong bài đăng này, bạn sẽ tìm hiểu Vestlab là gì, Cách sử dụng Vestlab (Công cụ tổng hợp lịch phát hành mã thông báo). 

Đối với những bạn thường xuyên tham gia các đợt ICO, IDO, IEO, chắc chắn chúng ta sẽ cần theo dõi chặt chẽ thời gian đấu của các dự án. Thời điểm thanh toán token không chỉ giúp bạn tính được lợi nhuận mà còn giúp theo dõi áp lực bán token của từng thời điểm.

Vậy có công cụ nào tổng hợp tất cả những thông tin này không? 

Để giúp bạn làm điều đó một cách nhanh chóng, tôi sẽ giới thiệu một trang web có tên là Vestlab.io. Cụ thể là chúng ta cùng đọc bài chia sẻ dưới đây nhé !!!

1. Vestlab là gì?

VestLab .io - dịch vụ phân tích, là tập hợp thông tin về mã số, số liệu, ngày và thời gian chính xác của các sự kiện sắp tới về việc niêm yết đầu tiên và tranh chấp mã thông báo tiền điện tử.

Vestlab.io là một công cụ hỗ trợ theo dõi lịch kiểm tra của các dự án trong thị trường tiền điện tử. Cho đến thời điểm hiện tại, Vestlab.io đã giúp đỡ rất nhiều dự án trên các nền tảng như Coinlist, Huobi, DaoMarker, v.v.

Hiện tại, số lượng dự án được hỗ trợ trên Vestlab là có hạn. Tuy nhiên, trang web vẫn đang trong giai đoạn Beta và đang được phát triển thêm nên số lượng dự án chắc chắn sẽ được cập nhật từ nhiều nền tảng khác trong tương lai.

2. Hướng dẫn sử dụng Vestlab

Để sử dụng Vestlab, vui lòng truy cập website: https://vestlab.io

Bước 1: Nhấp vào Đăng nhập ở góc trên bên phải màn hình chính.

Bước 2: Nhấp vào Đăng nhập bằng Telegram.

Bước 3: Nhập số điện thoại được liên kết với tài khoản Telegram của bạn và sau đó nhấp vào Tiếp theo .

Bước 4: Mở tin nhắn từ Telegram, kiểm tra chính xác nguồn tin nhắn, thiết bị cũng như địa chỉ IP rồi nhấn Xác nhận .

Sau khi đăng nhập thành công, bạn sẽ quay lại trang chủ của Vestlab. Màn hình chính sẽ hiển thị tất cả các dự án sắp trả token, có đồng hồ đếm ngược. Tuy nhiên, Vestlab không chỉ theo dõi thời gian thanh toán mã thông báo mà còn nhiều hơn thế nữa. 

Để xem chi tiết của từng dự án, hãy nhấp vào Thông tin khác .

Bạn cũng thấy đồng hồ đếm ngược bên cạnh danh sách.

Sau đó, bạn có thể nhấp vào các dự án ngẫu nhiên. Trang web sẽ hiển thị whitepaper và liên kết đến phương tiện truyền thông xã hội của dự án.

Ngoài ra, bạn cũng có thể xem rất nhiều thông tin khác nhau về dự án. Cụ thể, chẳng hạn như KingdomX (KT), chúng ta có thể thấy:

  • Phân bổ

  • TGE TÓM TẮT

  • Kế hoạch hưởng lợi


 

  • Tiến trình phân phối


Bên cạnh đó, nếu bạn muốn tìm một dự án nhanh chóng, bạn có thể nhấp vào và chọn Danh mục trên ToolBar.

Xu hướng trên thanh công cụ sẽ hiển thị cho bạn danh sách các dự án thu hút nhiều nhà đầu tư nhất.

Giống như CoinmarketCap, bạn có thể dễ dàng thêm Danh sách theo dõi để theo dõi các dự án của mình.

Tại sao chúng ta nên tuân theo Lịch trình phát hành mã thông báo?

Bạn nên theo dõi Lịch trình phát hành mã thông báo để nắm bắt và đánh giá thông tin quan trọng. Đặc biệt, bạn sẽ biết:

  • Lạm phát hoặc giảm phát của các dự án.
  • Mức độ phát triển của dự án.
  • Áp lực bán hàng.
  • Mức độ uy tín của dự án.

Tại sao Lịch trình phát hành mã thông báo lại quan trọng?

Theo dõi lịch thanh toán token không chỉ giúp bạn nắm được thời gian cụ thể nhận token để chốt lời. Chúng tôi cũng có thể theo dõi lạm phát và tìm điểm vào hợp lý cho các dự án mà chúng tôi thực sự quan tâm. Cụ thể là làm thế nào?

Mức độ và tỷ lệ lạm phát

  • Như bạn đã biết, trong thị trường tiền điện tử, lạm phát xảy ra khi số lượng token được thanh toán quá lớn trong một thời gian nhất định.
  • Nếu nhu cầu về mã thông báo không cao, nhưng lịch trình đấu giá cho thấy dự án đang chuẩn bị mở khóa một lượng lớn mã thông báo cho các nhà đầu tư, lạm phát sẽ xảy ra và tất nhiên giá mã thông báo sẽ giảm mạnh. Điều này sẽ ảnh hưởng xấu đến sự phát triển của dự án vì lợi nhuận không bù đắp được lạm phát.
  • Ngược lại, nếu dự án hoạt động tốt và thúc đẩy nhu cầu đối với mã thông báo, áp lực bán sẽ không quá lớn. Khi đó, giá của token không những ổn định mà thậm chí còn tăng lên, giúp dự án thu được lợi nhuận để tiếp tục phát triển.

Áp lực bán token - tìm điểm vào hợp lý

  • Tâm lý chung của nhiều nhà đầu tư trên thị trường là khi được thanh toán token thì họ sẽ tung ra để chốt lời, lấy lại vốn. Do đó, thời điểm dự án mở khóa token là lúc áp lực bán tăng khiến giá nhiều token giảm xuống. Nếu chúng ta mua token vào thời điểm này, chúng ta sẽ rất dễ bị “vung tiền”.
  • Khi biết được thời gian mở khóa của các dự án, bạn sẽ theo dõi được áp lực bán ra của các token, từ đó bạn có thể theo dõi thời điểm áp lực bán giảm để có giá mua hợp lý nhất. Đây là chiến lược mua và nắm giữ giúp tối ưu hóa nguồn vốn và lợi nhuận của bạn tốt hơn, tránh những tổn thất không đáng có.

Mức độ uy tín của dự án

  • Ngoài việc đánh giá khả năng phát triển, lịch trình phát hành mã thông báo cũng cung cấp cái nhìn sâu sắc để bạn xem xét độ tin cậy của từng dự án.
  • Nhiều dự án uy tín trên thị trường như Avalanche, UniSwap, Flow, Curve,… đều có thời gian mở khóa token từ 2 - 4 năm. Đặc biệt, thời gian kiểm tra thường sẽ dài nhất, lên đến 4, thậm chí 6 năm đối với các token của vòng hạt giống, cố vấn, đội ngũ, ...
  • Không phải dự án nào mở khóa lâu dài cũng là dự án tốt, nhưng đây là một trong những yếu tố giúp bạn đánh giá tầm nhìn của dự án. Quá trình mở khóa mã thông báo mất tới 4 năm để đảm bảo rằng nhóm và quỹ đầu tư sẽ đồng hành và giúp dự án phát triển về lâu dài, tránh kéo thảm, xây dựng để phát hành hoặc lật mã thông báo.
  • Lịch trình phát hành mã thông báo là yếu tố chính trong Tokenomics hoàn chỉnh.

Để hiểu thêm về Tokenomics và cách đánh giá dự án, bạn có thể đọc bài viết:  Tokenomics là gì | Giá trị thực của một mã thông báo ?

kết luận

Vestlab là một công cụ cực kỳ đơn giản, dễ sử dụng và không kém phần hữu ích. Đây chắc chắn là nền tảng hỗ trợ đắc lực trong việc quản lý tài sản, nắm bắt thông tin theo dõi thị trường mà bạn không nên bỏ qua.

Vestlab là một trang web mới. Mặc dù trang web này vẫn đang trong giai đoạn Beta, nhưng nó cũng có thể cung cấp thông tin đầy đủ cho chúng tôi.

Tôi hy vọng bài viết này sẽ giúp bạn. Đừng quên để lại một like, bình luận và chia sẻ nó với những người khác. Cảm ơn bạn!

Đọc thêm: Sàn giao dịch Bilaxy là gì | Cách đăng ký, mua và bán trên Bilaxy

#blockchain #cryptocurrency #bitcoin #token 

Vestlab Là Gì | Công Cụ Tổng Hợp Lịch Trình Phát Hành Mã Thông Báo
Ngoc  Anh

Ngoc Anh

1655363400

Tokenterminal là gì | Tổng hợp dữ liệu tài chính trên Blockchain

Trong bài đăng này, bạn sẽ tìm hiểu Tokenterminal là gì, Cách sử dụng Tokenterminal để giao dịch tiền điện tử?

Tokenterminal là gì?

Token Terminal là một nền tảng tổng hợp dữ liệu tài chính trên các chuỗi khối và các ứng dụng phi tập trung (dapps) chạy trên các chuỗi khối.

Chúng tôi tin rằng cả blockchain và dapp đều giống về mặt khái niệm với các công ty thị trường truyền thống. Họ là những doanh nghiệp có nguồn gốc từ internet có dịch vụ tạo ra doanh thu được phân chia giữa những người tham gia và chủ sở hữu bên cung của dự án. Các doanh nghiệp này được sở hữu và điều hành bởi các chủ sở hữu mã thông báo của họ, tương tự như cách các công ty được sở hữu và điều hành bởi các cổ đông của họ.

Đây là lý do tại sao chúng tôi muốn đo lường và đánh giá hiệu suất của các blockchain và dapp bằng cách sử dụng các chỉ số hiệu suất chính về tài chính và sửa chữa sự hiểu lầm — nếu có — về tiền điện tử là tất cả về tiền tệ. Số lượng blockchain và dapp ngày càng tăng có thể được định giá bằng cách đo lường việc sử dụng và dòng tiền của chúng.

Số liệu

Dưới đây là danh sách các số liệu kinh doanh hiện có trên Token Terminal và định nghĩa của chúng. Giá trị được đo bằng đô la Mỹ. Doanh thu, khối lượng giao dịch và khối lượng giao dịch được tổng hợp theo thời gian và có sẵn ở các mức độ chi tiết khác nhau (ví dụ: hàng ngày và hàng tháng).

  • Khối lượng cho vay: Khối lượng cho vay bằng tổng giá trị dư nợ của một phương thức cho vay.
  • Vốn đã triển khai: Vốn đã triển khai là tổng giá trị của các quỹ được đầu tư để tạo ra lợi nhuận bằng một giao thức quản lý tài sản.
  • Vốn hóa thị trường lưu thông: Vốn hóa thị trường lưu thông (viết tắt của vốn hóa) bằng số lượng mã thông báo đang lưu hành nhân với giá mã thông báo. Nó đo lường tổng giá trị của một dự án.
  • Vốn hóa thị trường pha loãng hoàn toàn: Vốn hóa thị trường pha loãng hoàn toàn (viết tắt của vốn hóa) bằng với lượng cung cấp mã thông báo tối đa nhân với giá mã thông báo.
  • Giá: Giá đề cập đến giá của mã thông báo.
  • Tỷ lệ giá trên thu nhập (P / E): Tỷ lệ giá trên thu nhập bằng vốn hóa thị trường đã pha loãng hoàn toàn chia cho doanh thu giao thức hàng năm. Doanh thu giao thức hàng năm được tính bằng doanh thu giao thức trong 30 ngày trước đó nhân với 365/30. Cần lưu ý rằng điều này không giống với định nghĩa thông thường về tỷ lệ giá trên thu nhập, trong đó thu nhập được tính là chênh lệch giữa doanh thu và chi phí và chi phí. Điều này là do phần lớn các dự án được liệt kê hiện tại đều ghi lại chi phí và chi phí ngoài chuỗi và chúng tôi chỉ có quyền truy cập vào dữ liệu trên chuỗi.
  • Tỷ lệ giá trên doanh thu (P / S): Tỷ lệ giá trên doanh thu bằng giá trị vốn hóa thị trường được pha loãng hoàn toàn chia cho tổng doanh thu hàng năm. Tổng doanh thu hàng năm được tính bằng tổng doanh thu trong 30 ngày trước đó nhân với 365/30.
  • Doanh thu từ giao thức: Doanh thu từ giao thức bằng với số doanh thu được phân phối cho các chủ sở hữu mã thông báo.
  • Doanh thu từ phía cung: Doanh thu từ phía cung bằng số doanh thu mà dự án trả cho những người tham gia phía cung (ví dụ: nhà cung cấp thanh khoản).
  • Khuyến khích mã thông báo: Ưu đãi mã thông báo đề cập đến giá trị của mã thông báo được phân phối cho người dùng và những người tham gia phía cung cấp dưới dạng phần thưởng.
  • Tổng doanh thu: Tổng doanh thu bằng tổng số phí mà người dùng đã trả. Nó được tính toán trong một khoảng thời gian nhất định. Ví dụ: tổng doanh thu hàng ngày cho một ngày nhất định bằng với phí được trả trong ngày đó (24 giờ).
  • Khối lượng giao dịch mã thông báo: Khối lượng giao dịch mã thông báo bằng khối lượng giao dịch của mã thông báo.
  • Tổng giá trị bị khóa (TVL): Tổng giá trị bị khóa là giá trị tài sản được ký gửi trong các hợp đồng thông minh của dự án.
  • Khối lượng giao dịch: Khối lượng giao dịch bằng tổng giá trị của các giao dịch trên một giao thức giao dịch.
  • Khối lượng giao dịch: Khối lượng giao dịch bằng tổng giá trị của các giao dịch trên một blockchain.

Làm thế nào để sử dụng Tokenterminal?

Bảng điều khiển dự án là một tính năng cốt lõi của Token Terminal. Các bảng điều khiển trình bày dữ liệu quan trọng nhất mà chúng tôi có trong một dự án trên một trang theo cách được tiêu chuẩn hóa. Chúng được chia thành các phần khác nhau được mô tả bên dưới.

chúng tôi sử dụng bảng điều khiển Compound (COMP) làm ví dụ.

Ở góc dưới cùng bên phải của mỗi biểu đồ, có một Downloadnút cho phép người dùng trên gói Token Terminal Pro tải xuống dữ liệu được hiển thị.

1. Bảng điều khiển trên cùng

Trên đầu trang tổng quan, bạn sẽ tìm thấy bản tóm tắt về dự án. Nếu dự án đã khởi chạy mã thông báo của riêng họ, biểu tượng mã của nó được hiển thị trong dấu ngoặc đơn bên cạnh tên dự án. Bạn có thể lưu dự án vào mục yêu thích của mình bằng cách nhấp vào biểu tượng ngôi sao bên cạnh tên của dự án. 

Nếu bạn nhấp vào Infonút, bạn sẽ thấy một bản tóm tắt ngắn về dự án là gì, cách thức hoạt động và ai là người sở hữu dự án đó. Đối với các dự án đã chọn, bạn có thể xem các hợp đồng thông minh quan trọng nhất của dự án với các liên kết đến các trang web khám phá blockchain để tìm hiểu thêm bằng cách nhấp vào Smart contract registrynút. 

Phần bên dưới tên dự án trình bày tóm tắt các dữ liệu tài chính hiện có cho dự án. Ở cuối bảng điều khiển trên cùng, bạn có thể tìm thấy một số sàn giao dịch lớn nơi mã thông báo của dự án có thể được giao dịch.

2. Các chỉ số chính

Phần Key metricsnày hiển thị chuỗi thời gian số liệu kinh doanh. Theo mặc định, vốn hóa thị trường được pha loãng hoàn toàn và tổng doanh thu được hiển thị, nếu có. Bạn có thể chọn số liệu để hiển thị bằng cách nhấp vào các nút ở góc trên cùng bên trái của biểu đồ. Theo mặc định, dữ liệu trong 180 ngày qua được hiển thị. 

Bạn có thể thay đổi điều này bằng cách nhấp vào các nút 7D, 30Dv.v. Mức độ chi tiết của dữ liệu là hàng ngày, trừ khi dữ liệu được hiển thị kể từ khi khởi chạy dự án, trong trường hợp đó, mức độ chi tiết dữ liệu là hàng tháng. Bằng cách bật nút chuyển Show as cumulative, dữ liệu doanh thu được hiển thị dưới dạng tích lũy theo thời gian.

3. Chia sẻ doanh thu

Biểu Revenue shaređồ hiển thị bảng phân tích về sự phân chia doanh thu giữa những người tham gia phía cung cấp của dự án và những người sở hữu mã thông báo. Bạn có thể hiển thị doanh thu từ phía cung cấp, doanh thu từ giao thức hoặc cả hai bằng cách nhấp vào các nút ở góc trên cùng bên trái của biểu đồ hoặc bằng cách nhấp vào các mục trong chú giải biểu đồ. 

Theo mặc định, dữ liệu trong 180 ngày qua được hiển thị. Điều này có thể được thay đổi bằng cách nhấp vào các nút 7D, 30Dv.v. Mức độ chi tiết của dữ liệu là hàng ngày, trừ khi dữ liệu được hiển thị kể từ khi khởi chạy dự án, trong trường hợp đó, mức độ chi tiết dữ liệu là hàng tháng. 

Bằng cách bật nút chuyển Show as cumulative, doanh thu được hiển thị là tích lũy theo thời gian. Bằng cách nhấp vào nút chuyển Show as % share, dữ liệu được hiển thị so với dữ liệu được hiển thị khác.

4. Thành phần

Biểu Compositionđồ hiển thị bảng phân tích chi tiết về thành phần của một số chỉ số kinh doanh. Bạn có thể chọn chỉ số để phân tích bằng cách nhấp vào các nút ở góc trên cùng bên trái của biểu đồ. 

Theo mặc định, dữ liệu trong 180 ngày qua được hiển thị. Các phần tử biểu đồ riêng lẻ có thể được ẩn bằng cách nhấp vào mục chú giải tương ứng. Điều này có thể được thay đổi bằng cách nhấp vào các nút 7D, 30Dv.v. Mức độ chi tiết của dữ liệu là hàng ngày, trừ khi dữ liệu được hiển thị kể từ khi khởi chạy dự án, trong trường hợp đó, mức độ chi tiết dữ liệu là hàng tháng. 

Bằng cách bật nút chuyển Show as cumulative, doanh thu được hiển thị là tích lũy theo thời gian. Bằng cách nhấp vào nút chuyển Show as % share, dữ liệu được hiển thị so với dữ liệu được hiển thị khác.

5. Cảnh quan cạnh tranh

Phần Competitive landscapenày cho phép bạn so sánh dự án với các dự án khác được liệt kê. Có thể chọn chỉ số để trực quan hóa và các dự án để so sánh bằng cách sử dụng menu thả xuống ở góc trên cùng bên trái. 

Dự án có thể được xóa khỏi biểu đồ bằng cách nhấp vào các nút có tên dự án hoặc ẩn bằng cách nhấp vào mục chú thích tương ứng. 

Theo mặc định, dữ liệu trong 180 ngày qua được hiển thị. Điều này có thể được thay đổi bằng cách nhấp vào các nút 7D, 30Dv.v. Mức độ chi tiết của dữ liệu là hàng ngày, trừ khi dữ liệu được hiển thị kể từ khi khởi chạy dự án, trong trường hợp đó, mức độ chi tiết dữ liệu là hàng tháng.

6. Biểu đồ tùy chỉnh

Người dùng trong gói Token Terminal Pro có thể tạo biểu đồ tùy chỉnh bằng cách nhấp vào nút Create your own chartsở góc dưới cùng bên trái của trang web thiết bị đầu cuối. 

Ảnh chụp màn hình bên dưới cho thấy một ví dụ về biểu đồ tùy chỉnh hiển thị vốn hóa thị trường đang lưu hành và tổng doanh thu của Aave và Compound, hai giao thức cho vay chính.

7. Bảng dữ liệu

Bảng dữ liệu hiển thị dữ liệu cập nhật của chúng tôi ở định dạng số được tổ chức thành một bảng với một hàng cho mỗi dự án và một cột cho mỗi chỉ số. Bạn có thể tìm thấy bảng dữ liệu ở cuối thiết bị đầu cuối.

Ở cuối mỗi bảng, có một Downloadnút cho phép người dùng trên gói Token Terminal Pro tải xuống dữ liệu. Theo mặc định, bảng dữ liệu chứa tất cả các dự án được liệt kê và được sắp xếp theo tổng doanh thu hàng tháng. 

Bảng có thể được sắp xếp theo các số liệu khác bằng cách nhấp vào tiêu đề cột. 

Bảng dữ liệu có thể được lọc để chỉ hiển thị các blockchains, dapps hoặc các mục yêu thích của bạn bằng cách nhấp vào các nút ở góc trên cùng bên trái. 

Các nút ở góc trên cùng bên phải cho phép bạn sắp xếp bảng dữ liệu theo các chỉ số khác nhau và xem các chỉ số đã chọn đã thay đổi như thế nào theo thời gian.

8. Bảng tùy chỉnh

Bạn có thể tạo bảng tùy chỉnh của mình bằng cách nhấp vào Customize table. Chỉ cần chọn số liệu bạn muốn xem trong bảng và nhấp Saveđể được hiển thị bảng tùy chỉnh hoặc nhấp Save to favoritesđể lưu bảng tùy chỉnh vào mục yêu thích của bạn dưới tên bạn chọn.

Tải xuống trang tính

Người dùng trong gói Token Terminal Pro có thể tải xuống trang tính chính của chúng tôi chứa các giá trị hiện tại của tất cả các chỉ số của chúng tôi cho tất cả các dự án được liệt kê bằng cách nhấp vào Master sheetliên kết trên trang tài khoản của họ.

Câu hỏi thường gặp

  • Token Terminal là gì?

Token Terminal là một nền tảng cung cấp các số liệu kinh doanh và tài chính truyền thống trên các loại tiền điện tử. Chúng tôi tin rằng blockchain và dapps có nhiều điểm tương đồng về khái niệm với các công ty thị trường truyền thống. Họ là các thị trường có nguồn gốc từ internet cung cấp các dịch vụ kỹ thuật số, tạo ra các dòng tiền hoặc doanh thu cho những người tham gia và chủ sở hữu (chủ sở hữu mã thông báo) phía cung cấp của họ.

  • Làm thế nào để tiền điện tử và mã thông báo có doanh thu?

Cả blockchain và dapp đều tính phí. Các khoản phí này, mà chúng tôi tính là doanh thu, có thể được thanh toán, chẳng hạn như phí giao dịch, phí giao dịch hoặc thanh toán lãi suất. Tổng doanh thu được phân chia giữa những người tham gia phía cung của dự án (ví dụ: nhà cung cấp thanh khoản) và chủ sở hữu (chủ sở hữu mã thông báo).

  • Các yêu cầu để được liệt kê trên Token Terminal là gì?

Nói chung, chúng tôi muốn các dự án được liệt kê trên Token Terminal:

  • Làm cách nào để liên hệ với Token Terminal?

Bạn có thể liên hệ với chúng tôi tại people@tokenterminal.xyz hoặc trực tiếp trên Twitter.

Các sàn giao dịch hàng đầu để giao dịch token-coin. Làm theo hướng dẫn và kiếm tiền không giới hạn

BinanceFTXPoloniexBitfinexHuobiMXCProBITGate.ioCoinbase

Tôi hy vọng bài viết này sẽ giúp bạn. Đừng quên để lại một like, bình luận và chia sẻ nó với những người khác. Cảm ơn bạn!

Đọc thêm: Sàn OKEx là gì? | Cách đăng Ký, Mua Và Bán Trên OKEx Exchange

#blockchain #cryptocurrency #bitcoin #token 

Tokenterminal là gì | Tổng hợp dữ liệu tài chính trên Blockchain
Markus zusak

Markus zusak

1655122487

IDO Token Development Company

The creation of IDOs is a simple approach to raise funding for cryptocurrency projects. It enables investors to fund enterprises with a clear aim and goal.

Check: https://bit.ly/3QktKpC

#ido #idodevelopment #initialdexoffering #idodevelopmentcompany #fundraising #crowdfunding #idotoken #token #idotokendevelopment #blockchain #crypto #cryptocurrency #cryptocurrencies

IDO Token Development Company

Cómo Hice Una Interfaz Uniswap Desde Cero

Con el objetivo de permitir a los usuarios intercambiar fácilmente entre diferentes monedas e implementar o eliminar liquidez en la red de Autonity con una interfaz de usuario, en lugar de usar scripts, decidí crear una interfaz para los contratos de Uniswap implementados en la red.

La interfaz oficial de Uniswap resultó difícil de bifurcar para la red privada, debido a su base de código muy expansiva y problemas para conectarse a una billetera. Como quería tener una base de código más pequeña que entendiera de arriba a abajo, decidí escribir mi propia aplicación más simple, con la ayuda de mi increíble pasante Matt .

Utilizamos ReactJS para el proyecto, con el módulo EthersJS para conectarse a la cadena de bloques a través de metamask en el navegador y Material-UI para la interfaz. Como era un sitio estático, usamos páginas de github para alojar la aplicación.

Este primer blog describe el código para la parte de intercambio de la aplicación. Primero repasaré las funciones necesarias para conectarse y realizar llamadas y transacciones con el backend de la cadena de bloques de Ethereum y, en segundo lugar, explicaré el frontend de React, que hace un uso extensivo de los ganchos de React y Material-UI. Esto está escrito con la expectativa de que el lector ya tenga una buena comprensión de ReactJS, así como un conocimiento de cómo funcionan los creadores de mercado automatizados (AMM) Uniswap V2 (consulte este artículo ). No explicaré los componentes genéricos de React, como la barra de navegación, o la página de redirección para cuando no se pueda encontrar una billetera Ethereum, ¡ya que este blog será lo suficientemente largo sin eso!

Consulte la interfaz aquí y el código sin formato en el repositorio aquí .

Tenga en cuenta que este blog describe el estado de la interfaz a partir de este compromiso . Desde entonces, se actualizó para tener soporte para múltiples redes y muestra diferentes monedas predeterminadas según la red, así como algunos otros pequeños cambios.

Funciones de Ethereum

Conexión a la cadena de bloques

Estas funciones son necesarias para conectarse a la cadena de bloques a través de una billetera en el navegador, para obtener información de la cadena de bloques a través de llamadas y para usar contratos en la red para realizar intercambios a través de transacciones.

Mi plan original era usar el módulo de javascript Web3 para conectarme a la cadena de bloques con metamask en el navegador, pero metamask dejó de admitirlo en enero de 2021, así que en su lugar usé EthersJS. EthersJS es un módulo mucho más pequeño que Web3, lo que significa que su aplicación se cargará más rápido.

En el archivo ethereumFunctions.js, la primera función es getProvider, que se conecta al proveedor de Ethereum (metamask u otra billetera) en el navegador, luego la función getSigner, que se utiliza para firmar transacciones. Usando la clase de contrato EthersJS, hay funciones que devuelven objetos de contrato para los contratos de enrutador, Weth y fábrica que había implementado en la cadena de bloques anteriormente. Para estos, la clase Contract tomó como parámetros la dirección, ABI del contrato inteligente implementado y el firmante EthersJS.

También se define la función getAccount, que solicita al usuario que seleccione cuentas para usar desde la billetera conectada.

EthereumFunctions.js

import { Contract, ethers } from "ethers";
import * as COINS from "./constants/coins";

const ROUTER = require("./build/UniswapV2Router02.json");
const ERC20 = require("./build/ERC20.json");
const FACTORY = require("./build/IUniswapV2Factory.json");
const PAIR = require("./build/IUniswapV2Pair.json");

export function getProvider() {
  return new ethers.providers.Web3Provider(window.ethereum);
}

export function getSigner(provider) {
  return provider.getSigner();
}

export function getRouter(address, signer) {
  return new Contract(address, ROUTER.abi, signer);
}

export function getWeth(address, signer) {
  return new Contract(address, ERC20.abi, signer);
}

export function getFactory(address, signer) {
  return new Contract(address, FACTORY.abi, signer);
}

export async function getAccount() {
  const accounts = await window.ethereum.request({
    method: "eth_requestAccounts",
  });

  return accounts[0];
}

Todas estas funciones se importaron al CoinSwapper/CoinSwapper.jsarchivo y se usaron para establecer sus variables de estado correspondientes usando React.useStateganchos, desde la línea 70:

CoinSwapper/CoinSwapper.js

const [provider, setProvider] = React.useState(getProvider());
const [signer, setSigner] = React.useState(getSigner(provider));
const [account, setAccount] = React.useState(undefined); // This is populated in a react hook
const [router, setRouter] = React.useState(
getRouter("0x4489D87C8440B19f11d63FA2246f943F492F3F5F", signer)
);
const [weth, setWeth] = React.useState(
getWeth("0x3f0D1FAA13cbE43D662a37690f0e8027f9D89eBF", signer)
);
const [factory, setFactory] = React.useState(
getFactory("0x4EDFE8706Cefab9DCd52630adFFd00E9b93FF116", signer)
);

Funciones del token ERC20

Las siguientes dos funciones en el ethereumFunctions.jsarchivo hacen llamadas a la red para obtener información sobre las direcciones de token elegidas o proporcionadas por el usuario.

doesTokenExisthace una verificación para asegurarse de que la dirección proporcionada corresponda a un token implementado en la cadena de bloques:

EthereumFunctions.js

export function doesTokenExist(address, signer) {
  try {
    return new Contract(address, ERC20.abi, signer);
  } catch (err) {
    return false;
  }
}

getBalanceAndSymbolcomprueba si una dirección proporcionada es la dirección del contrato de Weth en la cadena de bloques, en cuyo caso devuelve el saldo de AUT (la moneda nativa de la cadena de bloques de Autonity, equivalente a ETH) del usuario. De lo contrario, devuelve el saldo del token ERC20 del usuario, junto con el símbolo del token:

export async function getBalanceAndSymbol(
  accountAddress,
  address,
  provider,
  signer
) {
  try {
    if (address === COINS.AUTONITY.address) {
      const balanceRaw = await provider.getBalance(accountAddress);
return {
        balance: ethers.utils.formatEther(balanceRaw),
        symbol: COINS.AUTONITY.abbr,
      };
    } else {
      const token = new Contract(address, ERC20.abi, signer);
      const balanceRaw = await token.balanceOf(accountAddress);
      const symbol = await token.symbol();
return {
        balance: ethers.utils.formatEther(balanceRaw),
        symbol: symbol,
      };
    }
  } catch (err) {
    return false;
  }
}

Esta función verifica si la dirección es la dirección Weth comparándola con COINS.AUTONITY.addressuna matriz de tokens predeterminados exportados desde constants/coins.js.

La función de intercambio

La swapTokensfunción en ethereumFunctions.jsrealiza una transacción al contrato del enrutador en la cadena de bloques para realizar uno de los tres intercambios diferentes (AUT a token, token a AUT, token a token), según las direcciones de token que se le proporcionen, address1y address2.

  • Si address1es la dirección del contrato Weth, llama a la función RouterswapExactETHForTokens
  • Si address2es la dirección del contrato Weth, llama a la función RouterswapExactTokensForETH
  • Si ni address1o address2es la dirección del contrato Weth, la swapTokensfunción llama a la función de enrutadorswapExactTokensForTokens
export async function swapTokens(
  address1,
  address2,
  amount,
  routerContract,
  accountAddress,
  signer
) {
  const tokens = [address1, address2];
  const time = Math.floor(Date.now() / 1000) + 200000;
  const deadline = ethers.BigNumber.from(time);
const amountIn = ethers.utils.parseEther(amount.toString());
  const amountOut = await routerContract.callStatic.getAmountsOut(
    amountIn,
    tokens
  );
const token1 = new Contract(address1, ERC20.abi, signer);
  await token1.approve(routerContract.address, amountIn);
if (address1 === COINS.AUTONITY.address) {
    // Eth -> Token
    await routerContract.swapExactETHForTokens(
      amountOut[1],
      tokens,
      accountAddress,
      deadline,
      { value: amountIn }
    );
  } else if (address2 === COINS.AUTONITY.address) {
    // Token -> Eth
    await routerContract.swapExactTokensForETH(
      amountIn,
      amountOut[1],
      tokens,
      accountAddress,
      deadline
    );
  } else {
    await routerContract.swapExactTokensForTokens(
      amountIn,
      amountOut[1],
      tokens,
      accountAddress,
      deadline
    );
  }
}

La función getAmountOutse utiliza para obtener una vista previa de un intercambio. Llama a la función del enrutador getAmountsOutcon la cantidad del primer token y una matriz de las direcciones de los tokens que se intercambiarán como parámetros. Devuelve la cantidad del segundo token.

export async function getAmountOut(
  address1,
  address2,
  amountIn,
  routerContract
) {
  try {
    const values_out = await routerContract.getAmountsOut(
      ethers.utils.parseEther(amountIn),
      [address1, address2]
    );
    const amount_out = ethers.utils.formatEther(values_out[1]);
    return Number(amount_out);
  } catch {
    return false;
  }
}

La funcion de reservas

Finalmente, la GetReservesfunción en ethereumFunctions.jsdevuelve las reservas del fondo de liquidez para un par de tokens dado, así como el saldo de token de liquidez para el usuario. Internamente, esta función llama a otra función fetchReserves, que obtiene las reservas haciendo una llamada al contrato de par y luego asegurándose de que las reservas se devuelvan en el orden correcto.

export async function fetchReserves(address1, address2, pair) {
  try {
    const reservesRaw = await pair.getReserves();
    let results = [
      Number(ethers.utils.formatEther(reservesRaw[0])),
      Number(ethers.utils.formatEther(reservesRaw[1])),
    ];

    return [
      (await pair.token0()) === address1 ? results[0] : results[1],
      (await pair.token1()) === address2 ? results[1] : results[0],
    ];
  } catch (err) {
    console.log("no reserves yet");
    return [0, 0];
  }
}
export async function getReserves(
  address1,
  address2,
  factory,
  signer,
  accountAddress
) {
  const pairAddress = await factory.getPair(address1, address2);
  const pair = new Contract(pairAddress, PAIR.abi, signer);

  const reservesRaw = await fetchReserves(address1, address2, pair);
  const liquidityTokens_BN = await pair.balanceOf(accountAddress);
  const liquidityTokens = Number(
    ethers.utils.formatEther(liquidityTokens_BN)
  ).toFixed(2);

  return [
    reservesRaw[0].toFixed(2),
    reservesRaw[1].toFixed(2),
    liquidityTokens,
  ];
}

interfaz de reacción

La interfaz de la aplicación hace un uso extensivo de los componentes Material-UI como Grid, Container, Paper, Typography, así como varios botones y más. En lugar de explicar el funcionamiento de cada componente de la aplicación, intentaré ofrecer una descripción general de alto nivel, que tendrá más sentido si está leyendo el código al mismo tiempo. Si no está familiarizado con Material-UI, le recomiendo leer algunos de sus excelentes documentos aquí .

El archivo CoinSwapper.jsexporta una función CoinSwapper, que devuelve el componente React utilizado para seleccionar tokens y realizar intercambios. Esta función en sí misma hace uso de algunos otros componentes personalizados de React, que explicaré primero antes de analizar las funciones internas y los ganchos que hacen que el CoinSwappercomponente funcione.

Componente de diálogo de moneda

El CoinDialogcomponente presenta el menú de monedas que se abre al hacer clic en uno de los botones 'SELECCIONAR'. Esto amplía el Dialogcomponente React, que muestra una ventana que se abre frente al resto de la aplicación, que se utiliza para solicitar una decisión. se define enCoinSwapper/CoinDialog.js

Dentro del CoinDialogcomponente, primero hay una versión modificada del DialogTitlecomponente de Material-UI, con la adición de un botón de cierre que, al hacer clic, llama a la exitfunción que cierra CoinDialog.

A continuación, hay un TextFieldcomponente React, que permite al usuario pegar la dirección de un token que se utilizará. Al cambiar, esto establece la variable addressde estado en la entrada del usuario.

La siguiente parte es una asignación que asigna cada uno de los tokens predeterminados Constants/coinsa un CoinButtoncomponente personalizado (ver más abajo), que cuando se hace clic llama a la exitfunción que cierra y CoinDialogdevuelve la dirección de los tokens seleccionados.

Finalmente, está el Enterbotón, que al hacer clic llama a la submitfunción, que verifica que existe un token con la dirección en la TextFieldfunción Ethereum doesTokenExist, antes de llamar exitpara cerrar y CoinDialogdevolver la dirección.

CoinSwapper/CoinDialog.js

const submit = () => {
    if (doesTokenExist(address, signer)) {
      exit(address);
    } else {
      setError("This address is not valid");
    }
};
// Resets any fields in the dialog (in case it's opened in the future) and calls the `onClose` prop
const exit = (value) => {
    setError("");
    setAddress("");
    onClose(value);
};
return (
    <Dialog
      open={open}
      onClose={() => exit(undefined)}
      fullWidth
      maxWidth="sm"
      classes={{ paper: classes.dialogContainer }}
    >
      <DialogTitle onClose={() => exit(undefined)}>Select Coin</DialogTitle>
<hr className={classes.hr} />
<div className={classes.coinContainer}>
        <Grid container direction="column" spacing={1} alignContent="center">
          <TextField
            value={address}
            onChange={(e) => setAddress(e.target.value)}
            variant="outlined"
            placeholder="Paste Address"
            error={error !== ""}
            helperText={error}
            fullWidth
            className={classes.address}
          />
<hr className={classes.hr} />
<Grid item className={classes.coinList}>
            <Grid container direction="column">
              {/* Maps all of the tokens in the constants file to buttons */}
              {coins.map((coin, index) => (
                <Grid item key={index} xs={12}>
                  <CoinButton
                    coinName={coin.name}
                    coinAbbr={coin.abbr}
                    onClick={() => exit(coin.address)}
                  />
                </Grid>
              ))}
            </Grid>
          </Grid>
        </Grid>
      </div>
<hr className={classes.hr} />
<DialogActions>
        <Button autoFocus onClick={submit} color="primary">
          Enter
        </Button>
      </DialogActions>
    </Dialog>
);

Este componente, al igual que todos los demás, utiliza la makeStylesfunción Material-UI para el estilo, una solución CSS en JS que es fácil de usar con otros componentes de Material-UI y permite anidar temas y estilos dinámicos.

Componente de botón de moneda

El CoinButtoncomponente, definido en CoinSwapper/CoinButton.js, amplía el componente Material-UI ButtonBase, que contiene el texto del símbolo del token (coinAbbr) y el nombre del token (coinName), que se pasan a través de accesorios.

CoinSwapper/CoinButton.js

export default function CoinButton(props) {
    const {coinName, coinAbbr, onClick, ...other} = props;
    const classes = useStyles();
return (
        <ButtonBase
            focusRipple
            className={classes.button}
            onClick={onClick}
        >
            <Grid container direction="column">
                <Typography variant="h6">{coinAbbr}</Typography>
                <Typography variant="body2" className={classes.coinName}>{coinName}</Typography>
            </Grid>
        </ButtonBase>
    )
}

Componente de campo de monedas

El CoinFieldcomponente, definido en CoinSwapper/CoinField, representa la barra de entrada con un botón "SELECCIONAR" que se usa para seleccionar cada token e ingresar una cantidad para el intercambio. El componente es relativamente simple y consta de componentes Material-UI Fab(botón de acción flotante) y InputBase(un campo de texto), ambos envueltos en Gridcomponentes para el espaciado. Las propiedades relativas de los componentes Faby InputBasese pasan al CoinFieldcomponente a través de props.

CoinSwapper/CoinField.js

const classes = useStyles();
const { onClick, symbol, value, onChange, activeField } = props;
return (
    <div className={classes.container}>
      <Grid
        container
        direction="row"
        justifyContent="space-between"
        alignItems="center"
        className={classes.grid}
      >
        {/* Button */}
        <Grid item xs={3}>
          <Fab
            size="small"
            variant="extended"
            onClick={onClick}
            className={classes.fab}
          >
            {symbol}
            <ExpandMoreIcon />
          </Fab>
        </Grid>
{/* Text Field */}
        <Grid item xs={9}>
          <InputBase
            value={value}
            onChange={onChange}
            placeholder="0.0"
            disabled={!activeField}
            classes={{ root: classes.input, input: classes.inputBase }}
          />
        </Grid>
      </Grid>
    </div>
);

Botón de carga

El LoadingButtoncomponente, definido en Components/LoadingButton.js, muestra el botón 'CAMBIAR' en la parte inferior del CoinSwapper componente principal. Extiende el Buttoncomponente Material-UI, por lo que se deshabilitará según la validpropiedad, y muestra un ícono de carga giratorio al hacer clic hasta que se complete la transacción de intercambio.

Componentes/LoadingButton.js

export default function LoadingButton(props) {
  const classes = useStyles();
  const { children, loading, valid, success, fail, onClick, ...other } = props;
  return (
    <div className={classes.wrapper}>
      <Button
        variant="contained"
        color="primary"
        fullWidth
        disabled={loading || !valid}
        type="submit"
        onClick={onClick}
        {...other}
      >
        {children}
      </Button>
      {loading && <CircularProgress size={24} className={classes.progress} />}
    </div>
  );
}

Función de intercambio de monedas

La declaración de devolución

La función principal de la aplicación, CoinSwapper, CoinSwapper/CoinSwapper.jsdevuelve un componente en caja que contiene dos CoinDialogcomponentes, que solo se abren cuando CoinFieldse selecciona uno de los dos componentes. A continuación, hay Typographycomponentes Material-UI que muestran los saldos y las reservas de los dos tokens seleccionados.

Finalmente, está el LoadingButtoncomponente en la parte inferior y un breve párrafo instructivo con un enlace al grifo AUT.

CoinSwapper/CoinSwapper.js

return (
    <div>
      {/* Dialog Windows */}
      <CoinDialog
        open={dialog1Open}
        onClose={onToken1Selected}
        coins={COINS.ALL}
        signer={signer}
      />
      <CoinDialog
        open={dialog2Open}
        onClose={onToken2Selected}
        coins={COINS.ALL}
        signer={signer}
      />
{/* Coin Swapper */}
      <Container maxWidth="xs">
        <Paper className={classes.paperContainer}>
          <Typography variant="h5" className={classes.title}>
            Swap Coins
          </Typography>
<Grid container direction="column" alignItems="center" spacing={2}>
            <Grid item xs={12} className={classes.fullWidth}>
              <CoinField
                activeField={true}
                value={field1Value}
                onClick={() => setDialog1Open(true)}
                onChange={handleChange.field1}
                symbol={
                  coin1.symbol !== undefined ? coin1.symbol : "Select"
                }
              />
            </Grid>
<IconButton onClick={switchFields} className={classes.switchButton}>
              <SwapVerticalCircleIcon fontSize="medium" />
            </IconButton>
<Grid item xs={12} className={classes.fullWidth}>
              <CoinField
                activeField={false}
                value={field2Value}
                onClick={() => setDialog2Open(true)}
                symbol={
                  coin2.symbol !== undefined ? coin2.symbol : "Select"
                }
              />
            </Grid>
<hr className={classes.hr} />
{/* Balance Display */}
            <Typography variant="h6">Your Balances</Typography>
            <Grid container direction="row" justifyContent="space-between">
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatBalance(coin1.balance, coin1.symbol)}
                </Typography>
              </Grid>
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatBalance(coin2.balance, coin2.symbol)}
                </Typography>
              </Grid>
            </Grid>
<hr className={classes.hr} />
{/* Reserves Display */}
            <Typography variant="h6">Reserves</Typography>
            <Grid container direction="row" justifyContent="space-between">
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatReserve(reserves[0], coin1.symbol)}
                </Typography>
              </Grid>
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatReserve(reserves[1], coin2.symbol)}
                </Typography>
              </Grid>
            </Grid>
<hr className={classes.hr} />
<LoadingButton
              loading={loading}
              valid={isButtonEnabled()}
              success={false}
              fail={false}
              onClick={swap}
            >
              <LoopIcon />
              Swap
            </LoadingButton>
          </Grid>
        </Paper>
      </Container>
<Grid
        container
        className={classes.footer}
        direction="row"
        justifyContent="center"
        alignItems="flex-end"
      >
        <p>
          Clearmatics Autonity Uniswap | Get AUT for use in the bakerloo testnet{" "}
          <a href="https://faucet.bakerloo.autonity.network/">here</a>
        </p>
      </Grid>
    </div>
);

Cuando LoadingButtonse hace clic en , llama a la función interna swap(ver más abajo), que luego llama a la swapTokensfunción Ethereum con las variables coin1.addressde estado coin2.addressy field1Valuecomo argumentos. Esto primero solicitará al usuario que permita el gasto de la moneda 1 con una notificación de metamáscara, luego, con otra notificación, solicitará al usuario que confirme la transacción. Cuando haya terminado, los valores en field1 y field2 se restablecerán.

Variables de estado

La declaración de retorno anterior hace referencia a varias variables de estado que realizan un seguimiento del estado de la aplicación. Estos son:

  • dialog1Open— Mantiene un registro del clima la primera ventana de diálogo está abierta
  • dialog2Open— Mantiene un registro del clima la primera ventana de diálogo está abierta
  • coin1— matriz de dirección, símbolo, saldo para coin1
  • coin2— matriz de dirección, símbolo, saldo para coin2
  • reserves— Almacena las reservas de liquidez en el pool para coin1 y coin2.
  • field1Value— Almacena la entrada del usuario en el campo 1 (el valor de la moneda 1 que se intercambiará)
  • field2Value— Almacena la entrada del usuario en el campo2 (el valor de la moneda2 que se intercambiará)
  • loading— booleano para controlar el botón de carga

Estos se definen con React.useStateganchos de la línea 83:

CoinSwapper/CoinSwapper.js

// Stores a record of whether their respective dialog window is open
const [dialog1Open, setDialog1Open] = React.useState(false);
const [dialog2Open, setDialog2Open] = React.useState(false);
// Stores data about their respective coin
const [coin1, setCoin1] = React.useState({
address: undefined,
symbol: undefined,
balance: undefined,
});
const [coin2, setCoin2] = React.useState({
address: undefined,
symbol: undefined,
balance: undefined,
});
// Stores the current reserves in the liquidity pool between coin1 and coin2
const [reserves, setReserves] = React.useState(["0.0", "0.0"]);
// Stores the current value of their respective text box
const [field1Value, setField1Value] = React.useState("");
const [field2Value, setField2Value] = React.useState("");
// Controls the loading button
const [loading, setLoading] = React.useState(false);

Estas variables de estado se utilizan en las siguientes funciones y también se utilizan para mostrar información al usuario en la declaración de devolución mencionada anteriormente.

Funciones internas

También se hace referencia en la declaración de devolución anterior a varias funciones internas, estas son:

  • switchFields
  • handleChange
  • formatBalance
  • formatReserve
  • isButtonEnabled
  • onToken1Selected
  • onToken2Selected
  • swap

switchFieldscambia las monedas superior e inferior. Esto se llama cuando los usuarios presionan el botón de intercambio o seleccionan el token opuesto en el cuadro de diálogo (por ejemplo, si coin1 es TokenA y el usuario selecciona TokenB al elegir coin2):

const switchFields = () => {
    setCoin1(coin2);
    setCoin2(coin1);
    setField1Value(field2Value);
    setReserves(reserves.reverse());
};

handleChangees una función interna común utilizada en ReactJS, que toma un evento HTML, extrae los datos y los coloca en una variable de estado. Aquí solía establecer el valor del campo 1 cuando coinFiedcambia el valor en el primero:

const handleChange = {
    field1: (e) => {
      setField1Value(e.target.value);
    },
};

formatBalance/ formatReserveconvertir el saldo / las reservas de la cuenta en algo agradable y legible:

const formatBalance = (balance, symbol) => {
    if (balance && symbol)
      return parseFloat(balance).toPrecision(8) + " " + symbol;
    else return "0.0";
};

isButtonEnableddetermina si el botón debe estar habilitado o no:

const isButtonEnabled = () => {
    let validFloat = new RegExp("^[0-9]*[.,]?[0-9]*$");
// If both coins have been selected, and a valid float has been entered which is less than the user's balance, then return true
    return (
      coin1.address &&
      coin2.address &&
      validFloat.test(field1Value) &&
      parseFloat(field1Value) <= coin1.balance
    );
};

onToken1Selected/ onToken2Selectedse llaman cuando sale la ventana de diálogo para coin1 / coin2, y establece las variables de estado relevantes:

const onToken1Selected = (address) => {
    // Close the dialog window
    setDialog1Open(false);
// If the user inputs the same token, we want to switch the data in the fields
    if (address === coin2.address) {
      switchFields();
    }
    // We only update the values if the user provides a token
    else if (address) {
      // Getting some token data is async, so we need to wait for the data to return, hence the promise
      getBalanceAndSymbol(account, address, provider, signer).then((data) => {
        setCoin1({
          address: address,
          symbol: data.symbol,
          balance: data.balance,
        });
      });
    }
};

swapllama a la swapTokensfunción Ethereum para realizar el intercambio, luego restablece las variables de estado necesarias:

const swap = () => {
    console.log("Attempting to swap tokens...");
    setLoading(true);
swapTokens(
      coin1.address,
      coin2.address,
      parseFloat(field1Value),
      router,
      account,
      signer
    )
      .then(() => {
        setLoading(false);
// If the transaction was successful, we clear to input to make sure the user doesn't accidental redo the transfer
        setField1Value("");
        enqueueSnackbar("Transaction Successful", { variant: "success" });
      })
      .catch((e) => {
        setLoading(false);
        enqueueSnackbar("Transaction Failed (" + e.message + ")", {
          variant: "error",
          autoHideDuration: 10000,
        });
      });
};

La línea 16 en la función de intercambio usa enqueueSnackbar, un componente del módulo de nodo Notistack. Notistack es una gran biblioteca para hacer notificaciones temporales. Consulta el repositorio aquí .

usar Ganchos de efecto

Finalmente, en CoinSwapper/CoinSwapper.js, hay cuatro useEffectganchos, que se utilizan para mantener la aplicación actualizada con los últimos cambios. El lambda (código) dentro de cada enlace se ejecuta cuando cambia una de las dependencias. Las dependencias se definen en la matriz de variables que se pasan a la función después de la expresión lambda.

El primero useEffectse llama cuando alguna de las variables de estado coin1.addresso coin2.addresscambia. Esto significa que cuando el usuario selecciona una moneda diferente para convertir, o se intercambian las monedas, se calcularán las nuevas reservas:

useEffect(() => {
    console.log(
      "Trying to get Reserves between:\n" +
        coin1.address +
        "\n" +
        coin2.address
    );
if (coin1.address && coin2.address) {
      getReserves(
        coin1.address,
        coin2.address,
        factory,
        signer,
        account
      ).then((data) => setReserves(data));
    }
}, [coin1.address, coin2.address, account, factory, router, signer]);

El segundo gancho se llama cuando alguna de las variables de estado field1Value coin1.addresso coin2.addresscambia. Intenta calcular y establecer la variable de estado field2Value. Esto significa que si el usuario ingresa un nuevo valor en el cuadro de conversión o la tasa de conversión cambia, el valor en el cuadro de salida cambiará:

useEffect(() => {
    if (isNaN(parseFloat(field1Value))) {
      setField2Value("");
    } else if (field1Value && coin1.address && coin2.address) {
      getAmountOut(
        coin1.address,
        coin2.address,
        field1Value,
        router
      ).then((amount) => setField2Value(amount.toFixed(7)));
    } else {
      setField2Value("");
    }
}, [field1Value, coin1.address, coin2.address]);

El tercer enlace crea un tiempo de espera que se ejecutará cada ~ 10 segundos, su función es verificar si el saldo del usuario se actualizó y cambió. Esto les permite ver cuándo se completa una transacción mirando la salida del saldo:

useEffect(() => {
    const coinTimeout = setTimeout(() => {
      console.log("Checking balances...");
if (coin1.address && coin2.address && account) {
        getReserves(
          coin1.address,
          coin2.address,
          factory,
          signer,
          account
        ).then((data) => setReserves(data));
      }
if (coin1 && account) {
        getBalanceAndSymbol(account, coin1.address, provider, signer).then(
          (data) => {
            setCoin1({
              ...coin1,
              balance: data.balance,
            });
          }
        );
      }
      if (coin2 && account) {
        getBalanceAndSymbol(account, coin2.address, provider, signer).then(
          (data) => {
            setCoin2({
              ...coin2,
              balance: data.balance,
            });
          }
        );
      }
    }, 10000);
return () => clearTimeout(coinTimeout);
});

El enlace final se ejecutará cuando el componente se monte por primera vez. Se utiliza para configurar la cuenta:

export async function quoteAddLiquidity(
  address1,
  address2,
  amountADesired,
  amountBDesired,
  factory,
  signer
) {
  const pairAddress = await factory.getPair(address1, address2);
  const pair = new Contract(pairAddress, PAIR.abi, signer);

  const reservesRaw = await fetchReserves(address1, address2, pair); // Returns the reserves already formated as ethers
  const reserveA = reservesRaw[0];
  const reserveB = reservesRaw[1];

  if (reserveA === 0 && reserveB === 0) {
    let amountOut = Math.sqrt(reserveA * reserveB);
    return [
      amountADesired.toString(),
      amountBDesired.toString(),
      amountOut.toString(),
    ];
  } else {
    let [amountBOptimal, amountOut] = quote(amountADesired, reserveA, reserveB);
    if (amountBOptimal <= amountBDesired) {
      return [
        amountADesired.toString(),
        amountBOptimal.toString(),
        amountOut.toString(),
      ];
    } else {
      let [amountAOptimal, amountOut] = quote(
        amountBDesired,
        reserveB,
        reserveA
      );
      console.log(amountAOptimal, amountOut);
      return [
        amountAOptimal.toString(),
        amountBDesired.toString(),
        amountOut.toString(),
      ];
    }
  }
}

Conclusión

La primera sección de este blog cubrió las funciones de Ethereum necesarias para realizar llamadas y transacciones a la cadena de bloques. La siguiente sección pasó por los componentes personalizados, las funciones internas y los ganchos necesarios para que la funcionalidad de intercambio principal funcione.

Todavía tengo que mencionar cualquiera de las funcionalidades de eliminación/implementación de liquidez. Ese es el tema del próximo blog, aquí .

Para dar una propina al autor, puede usar esta dirección ethereum: 0xe7EeDB184E63Fe049EebA79EfeAc72939cB1461D 

Esta historia se publicó originalmente en https://medium.com/clearmatics/how-i-made-a-uniswap-interface-from-scratch-b51e1027ca87

#uniswap #ethereum #token #bitcoin 

Cómo Hice Una Interfaz Uniswap Desde Cero
伊藤  直子

伊藤 直子

1654503060

ユニスワップインターフェイスを最初から作成した

スクリプトを使用するのではなく、ユーザーが異なるコイン間で簡単にスワップを行い、Autonityネットワークで流動性を展開または削除できるようにすることを目的として、ネットワークに展開されたUniswapコントラクトのインターフェイスを作成することにしました。

公式のUniswapインターフェースは、その非常に広範なコードベースとウォレットへの接続の問題のために、プライベートネットワークをフォークするのが難しいことがわかりました。上から下まで理解できる小さなコードベースが欲しかったので、素晴らしいインターンのMattの助けを借りて、独自のより単純なアプリケーションを作成することにしました。

プロジェクトにはReactJSを使用し、 EthersJSモジュールを使用してブラウザーのメタマスクを介してブロックチェーンに接続し、フロントエンドにはMaterial-UIを使用しました。静的サイトであるため、アプリケーションをホストするためにgithubページを使用しました。

この最初のブログでは、アプリケーションのスワップ部分のコードについて説明しています。まず、Ethereumブロックチェーンバックエンドに接続して呼び出しやトランザクションを行うために必要な機能について説明し、次に、ReactフックとMaterial-UIを多用するReactフロントエンドについて説明します。これは、読者がすでにReactJSを十分に理解していること、およびUniswap V2自動マーケットメーカー(AMM)がどのように機能するかについての知識があることを期待して書かれています(この記事を参照)。NavBarなどの一般的なReactコンポーネントや、Ethereumウォレットが見つからない場合のリダイレクトページについては説明しません。このブログはそれがないと十分に長くなるからです。

ここでインターフェースをチェックアウトし、ここでリポジトリの生のコードをチェックアウトします。

このブログでは、このコミットの時点でのインターフェースの状態について説明していることに注意してください。その後、複数のネットワークをサポートするように更新され、ネットワークに応じて異なるデフォルトのコインと、その他のいくつかの小さな変更が表示されます。

イーサリアム機能

ブロックチェーンに接続する

これらの関数は、ブラウザーのウォレットを介してブロックチェーンに接続し、呼び出しを介してブロックチェーンから情報をフェッチし、ネットワーク上のコントラクトを使用してトランザクションを介してスワップを行うために必要です。

私の当初の計画は、javascriptモジュールWeb3を使用して、ブラウザーでメタマスクを使用してブロックチェーンに接続することでしたが、メタマスクは2021年1月にサポートを終了したため、代わりにEthersJSを使用しました。EthersJSはWeb3よりもはるかに小さいモジュールです。つまり、アプリケーションの読み込みが速くなります。

ファイルethereumFunctions.jsでは、最初の関数はgetProvider、ブラウザのイーサリアムプロバイダー(メタマスクまたは別のウォレット)に接続し、次に関数getSignerはトランザクションの署名に使用されます。EthersJSコントラクトクラスを使用するのは、以前にブロックチェーンにデプロイしたルーター、ウェス、ファクトリーコントラクトのコントラクトオブジェクトを返す関数です。これらの場合、Contractクラスは、アドレス、デプロイされたスマートコントラクトのABI、およびEthersJS署名者をパラメーターとして受け取りました。

getAccountまた、接続されたウォレットから使用するアカウントを選択するようにユーザーに促す関数も定義されています。

ethereumFunctions.js

import { Contract, ethers } from "ethers";
import * as COINS from "./constants/coins";

const ROUTER = require("./build/UniswapV2Router02.json");
const ERC20 = require("./build/ERC20.json");
const FACTORY = require("./build/IUniswapV2Factory.json");
const PAIR = require("./build/IUniswapV2Pair.json");

export function getProvider() {
  return new ethers.providers.Web3Provider(window.ethereum);
}

export function getSigner(provider) {
  return provider.getSigner();
}

export function getRouter(address, signer) {
  return new Contract(address, ROUTER.abi, signer);
}

export function getWeth(address, signer) {
  return new Contract(address, ERC20.abi, signer);
}

export function getFactory(address, signer) {
  return new Contract(address, FACTORY.abi, signer);
}

export async function getAccount() {
  const accounts = await window.ethereum.request({
    method: "eth_requestAccounts",
  });

  return accounts[0];
}

これらの関数はすべてファイルにインポートされ、70行目からフックCoinSwapper/CoinSwapper.jsを使用して対応する状態変数を設定するために使用されました。React.useState

CoinSwapper / CoinSwapper.js

const [provider, setProvider] = React.useState(getProvider());
const [signer, setSigner] = React.useState(getSigner(provider));
const [account, setAccount] = React.useState(undefined); // This is populated in a react hook
const [router, setRouter] = React.useState(
getRouter("0x4489D87C8440B19f11d63FA2246f943F492F3F5F", signer)
);
const [weth, setWeth] = React.useState(
getWeth("0x3f0D1FAA13cbE43D662a37690f0e8027f9D89eBF", signer)
);
const [factory, setFactory] = React.useState(
getFactory("0x4EDFE8706Cefab9DCd52630adFFd00E9b93FF116", signer)
);

ERC20トークン機能

ファイル内の次の2つの関数はethereumFunctions.js、ネットワークを呼び出して、ユーザーが選択または提供したトークンアドレスに関する情報を取得します。

doesTokenExist提供されたアドレスがブロックチェーンにデプロイされたトークンに対応していることを確認するためのチェックを行います。

ethereumFunctions.js

export function doesTokenExist(address, signer) {
  try {
    return new Contract(address, ERC20.abi, signer);
  } catch (err) {
    return false;
  }
}

getBalanceAndSymbol提供されたアドレスがブロックチェーン上のWethコントラクトのアドレスであるかどうかをチェックします。その場合、ユーザーのAUT(Autonityブロックチェーンのネイティブコイン、ETHに相当)の残高を返します。それ以外の場合は、トークンのシンボルとともに、ユーザーのERC20トークン残高を返します。

export async function getBalanceAndSymbol(
  accountAddress,
  address,
  provider,
  signer
) {
  try {
    if (address === COINS.AUTONITY.address) {
      const balanceRaw = await provider.getBalance(accountAddress);
return {
        balance: ethers.utils.formatEther(balanceRaw),
        symbol: COINS.AUTONITY.abbr,
      };
    } else {
      const token = new Contract(address, ERC20.abi, signer);
      const balanceRaw = await token.balanceOf(accountAddress);
      const symbol = await token.symbol();
return {
        balance: ethers.utils.formatEther(balanceRaw),
        symbol: symbol,
      };
    }
  } catch (err) {
    return false;
  }
}

COINS.AUTONITY.addressこの関数は、アドレスがからエクスポートされたデフォルトトークンの配列であると比較することにより、アドレスがウェスアドレスであるかどうかをチェックしますconstants/coins.js

スワップ機能

swapTokens関数はethereumFunctions.js、ブロックチェーン上のルーターコントラクトに対してトランザクションを実行し、提供されたトークンアドレスに応じて、3つの異なるスワップ(AUTからトークン、トークンからAUT、トークンからトークン)のいずれかを実行しaddress1ますaddress2

  • address1がWethコントラクトのアドレスである場合、ルーター関数を呼び出しますswapExactETHForTokens
  • address2がWethコントラクトのアドレスである場合、ルーター関数を呼び出しますswapExactTokensForETH
  • どちらでもないaddress1address2、Wethコントラクトのアドレスである場合、swapTokens関数はルーター関数を呼び出しますswapExactTokensForTokens
export async function swapTokens(
  address1,
  address2,
  amount,
  routerContract,
  accountAddress,
  signer
) {
  const tokens = [address1, address2];
  const time = Math.floor(Date.now() / 1000) + 200000;
  const deadline = ethers.BigNumber.from(time);
const amountIn = ethers.utils.parseEther(amount.toString());
  const amountOut = await routerContract.callStatic.getAmountsOut(
    amountIn,
    tokens
  );
const token1 = new Contract(address1, ERC20.abi, signer);
  await token1.approve(routerContract.address, amountIn);
if (address1 === COINS.AUTONITY.address) {
    // Eth -> Token
    await routerContract.swapExactETHForTokens(
      amountOut[1],
      tokens,
      accountAddress,
      deadline,
      { value: amountIn }
    );
  } else if (address2 === COINS.AUTONITY.address) {
    // Token -> Eth
    await routerContract.swapExactTokensForETH(
      amountIn,
      amountOut[1],
      tokens,
      accountAddress,
      deadline
    );
  } else {
    await routerContract.swapExactTokensForTokens(
      amountIn,
      amountOut[1],
      tokens,
      accountAddress,
      deadline
    );
  }
}

この関数getAmountOutは、スワップのプレビューを取得するために使用されます。getAmountsOut最初のトークンの量と、パラメーターとして交換されるトークンのアドレスの配列を使用して、ルーター関数を呼び出します。2番目のトークンから金額を返します。

export async function getAmountOut(
  address1,
  address2,
  amountIn,
  routerContract
) {
  try {
    const values_out = await routerContract.getAmountsOut(
      ethers.utils.parseEther(amountIn),
      [address1, address2]
    );
    const amount_out = ethers.utils.formatEther(values_out[1]);
    return Number(amount_out);
  } catch {
    return false;
  }
}

リザーブ機能

最後に、のGetReserves関数はethereumFunctions.js、指定されたトークンのペアの流動性プールの予約と、ユーザーの流動性トークンの残高を返します。内部的に、この関数は別の関数fetchReservesを呼び出します。この関数は、ペアコントラクトを呼び出してリザーブをフェッチし、リザーブが正しい順序で返されることを確認します。

export async function fetchReserves(address1, address2, pair) {
  try {
    const reservesRaw = await pair.getReserves();
    let results = [
      Number(ethers.utils.formatEther(reservesRaw[0])),
      Number(ethers.utils.formatEther(reservesRaw[1])),
    ];

    return [
      (await pair.token0()) === address1 ? results[0] : results[1],
      (await pair.token1()) === address2 ? results[1] : results[0],
    ];
  } catch (err) {
    console.log("no reserves yet");
    return [0, 0];
  }
}
export async function getReserves(
  address1,
  address2,
  factory,
  signer,
  accountAddress
) {
  const pairAddress = await factory.getPair(address1, address2);
  const pair = new Contract(pairAddress, PAIR.abi, signer);

  const reservesRaw = await fetchReserves(address1, address2, pair);
  const liquidityTokens_BN = await pair.balanceOf(accountAddress);
  const liquidityTokens = Number(
    ethers.utils.formatEther(liquidityTokens_BN)
  ).toFixed(2);

  return [
    reservesRaw[0].toFixed(2),
    reservesRaw[1].toFixed(2),
    liquidityTokens,
  ];
}

Reactフロントエンド

アプリケーションのフロントエンドは、、、、、などのMaterial-UIコンポーネントGrid、およびさまざまなボタンなどContainerを広範囲Paperに使用します。Typographyアプリケーション内のすべてのコンポーネントの動作を説明するのではなく、コードを同時に読んでいる場合に最も意味のある、高レベルの概要を説明することを目指します。Material-UIに精通していない場合は、ここでそれらの優れたドキュメントのいくつかを読むことをお勧めします。

このファイルは、トークンの選択とスワップの作成に使用されるReactコンポーネントを返すCoinSwapper.js関数をエクスポートします。CoinSwapperこの関数自体は、他のいくつかのカスタムReactコンポーネントを利用します。これについては、コンポーネントを機能させる内部関数とフックを説明する前に最初に説明しCoinSwapperます。

コインダイアログコンポーネント

CoinDialogコンポーネントは、「選択」ボタンの1つをクリックすると開くコインのメニューをレンダリングします。これにより、ReactDialogコンポーネントが拡張され、アプリの残りの部分の前に開くウィンドウがレンダリングされ、決定を求めるために使用されます。それはで定義されていますCoinSwapper/CoinDialog.js

コンポーネント内CoinDialogには、最初にMaterial-UIのコンポーネントの修正バージョンがあり、クリックすると閉じる関数をDialogTitle呼び出す閉じるボタンが追加されています。exitCoinDialog

次に、ReactTextFieldコンポーネントがあります。これにより、ユーザーは使用するトークンのアドレスを貼り付けることができます。変更時に、これは状態変数addressをユーザーの入力に設定します。

次の部分は、デフォルトの各トークンをConstants/coinsカスタムCoinButtonコンポーネントにマップするマッピングです(以下を参照)。クリックすると、選択したトークンのアドレスを返すexit関数を呼び出します。CoinDialog

最後に、ボタンがありEnterます。このボタンをクリックすると、関数を呼び出します。このボタンは、Ethereum関数を使用してsubmit、のアドレスにトークンが存在するかどうかを確認してから、呼び出してアドレスを返します。TextFielddoesTokenExistexitCoinDialog

CoinSwapper / CoinDialog.js

const submit = () => {
    if (doesTokenExist(address, signer)) {
      exit(address);
    } else {
      setError("This address is not valid");
    }
};
// Resets any fields in the dialog (in case it's opened in the future) and calls the `onClose` prop
const exit = (value) => {
    setError("");
    setAddress("");
    onClose(value);
};
return (
    <Dialog
      open={open}
      onClose={() => exit(undefined)}
      fullWidth
      maxWidth="sm"
      classes={{ paper: classes.dialogContainer }}
    >
      <DialogTitle onClose={() => exit(undefined)}>Select Coin</DialogTitle>
<hr className={classes.hr} />
<div className={classes.coinContainer}>
        <Grid container direction="column" spacing={1} alignContent="center">
          <TextField
            value={address}
            onChange={(e) => setAddress(e.target.value)}
            variant="outlined"
            placeholder="Paste Address"
            error={error !== ""}
            helperText={error}
            fullWidth
            className={classes.address}
          />
<hr className={classes.hr} />
<Grid item className={classes.coinList}>
            <Grid container direction="column">
              {/* Maps all of the tokens in the constants file to buttons */}
              {coins.map((coin, index) => (
                <Grid item key={index} xs={12}>
                  <CoinButton
                    coinName={coin.name}
                    coinAbbr={coin.abbr}
                    onClick={() => exit(coin.address)}
                  />
                </Grid>
              ))}
            </Grid>
          </Grid>
        </Grid>
      </div>
<hr className={classes.hr} />
<DialogActions>
        <Button autoFocus onClick={submit} color="primary">
          Enter
        </Button>
      </DialogActions>
    </Dialog>
);

このコンポーネントは、他のすべてのコンポーネントと同様にmakeStyles、スタイリングにMaterial-UI関数を使用します。これは、他のMaterial-UIコンポーネントで簡単に使用でき、テーマのネストと動的なスタイルを可能にするJSソリューションのCSSです。

コインボタンコンポーネント

CoinButtonで定義されたコンポーネントは、propsを介して渡されるトークンシンボル(coinAbbr)とトークン名(coinName)のテキストを含むCoinSwapper/CoinButton.jsMaterial-UIコンポーネントを拡張します。ButtonBase

CoinSwapper / CoinButton.js

export default function CoinButton(props) {
    const {coinName, coinAbbr, onClick, ...other} = props;
    const classes = useStyles();
return (
        <ButtonBase
            focusRipple
            className={classes.button}
            onClick={onClick}
        >
            <Grid container direction="column">
                <Typography variant="h6">{coinAbbr}</Typography>
                <Typography variant="body2" className={classes.coinName}>{coinName}</Typography>
            </Grid>
        </ButtonBase>
    )
}

コインフィールドコンポーネント

CoinFieldで定義されたコンポーネントは、各CoinSwapper/CoinFieldトークンを選択し、スワッピングの金額を入力するために使用される「選択」ボタンを使用して入力バーをレンダリングします。コンポーネントは比較的単純で、Material-UIコンポーネントFab(フローティングアクションボタン)とInputBase(テキストフィールド)で構成されており、どちらもGrid間隔を空けるためにコンポーネントでラップされています。Fabおよびコンポーネントの相対プロパティは、小道具を介しInputBaseてコンポーネントに渡されます。CoinField

CoinSwapper / CoinField.js

const classes = useStyles();
const { onClick, symbol, value, onChange, activeField } = props;
return (
    <div className={classes.container}>
      <Grid
        container
        direction="row"
        justifyContent="space-between"
        alignItems="center"
        className={classes.grid}
      >
        {/* Button */}
        <Grid item xs={3}>
          <Fab
            size="small"
            variant="extended"
            onClick={onClick}
            className={classes.fab}
          >
            {symbol}
            <ExpandMoreIcon />
          </Fab>
        </Grid>
{/* Text Field */}
        <Grid item xs={9}>
          <InputBase
            value={value}
            onChange={onChange}
            placeholder="0.0"
            disabled={!activeField}
            classes={{ root: classes.input, input: classes.inputBase }}
          />
        </Grid>
      </Grid>
    </div>
);

読み込みボタン

LoadingButtonで定義されたコンポーネントはComponents/LoadingButton.js、メインコンポーネントの下部にある[SWAP]ボタンをレンダリングしCoinSwapper ます。Material-UIButtonコンポーネントを拡張するため、小道具によっては無効になりvalid、スワップトランザクションが完了するまでクリックすると回転する読み込みアイコンが表示されます。

Components / LoadingButton.js

export default function LoadingButton(props) {
  const classes = useStyles();
  const { children, loading, valid, success, fail, onClick, ...other } = props;
  return (
    <div className={classes.wrapper}>
      <Button
        variant="contained"
        color="primary"
        fullWidth
        disabled={loading || !valid}
        type="submit"
        onClick={onClick}
        {...other}
      >
        {children}
      </Button>
      {loading && <CircularProgress size={24} className={classes.progress} />}
    </div>
  );
}

コインスワッパー機能

Returnステートメント

アプリケーションの主な関数であるCoinSwapperinCoinSwapper/CoinSwapper.jsは、2つのコンポーネントを含むボックス化されたコンポーネントを返します。このコンポーネントは、2つのコンポーネントのいずれかが選択されCoinDialogた場合にのみ開きます。次に、選択した2つのトークンの残高と予約を表示するCoinFieldMaterial-UIコンポーネントがあります。Typography

最後にLoadingButton、下部にコンポーネントがあり、AUT蛇口へのリンクを含む短い説明段落があります。

CoinSwapper / CoinSwapper.js

return (
    <div>
      {/* Dialog Windows */}
      <CoinDialog
        open={dialog1Open}
        onClose={onToken1Selected}
        coins={COINS.ALL}
        signer={signer}
      />
      <CoinDialog
        open={dialog2Open}
        onClose={onToken2Selected}
        coins={COINS.ALL}
        signer={signer}
      />
{/* Coin Swapper */}
      <Container maxWidth="xs">
        <Paper className={classes.paperContainer}>
          <Typography variant="h5" className={classes.title}>
            Swap Coins
          </Typography>
<Grid container direction="column" alignItems="center" spacing={2}>
            <Grid item xs={12} className={classes.fullWidth}>
              <CoinField
                activeField={true}
                value={field1Value}
                onClick={() => setDialog1Open(true)}
                onChange={handleChange.field1}
                symbol={
                  coin1.symbol !== undefined ? coin1.symbol : "Select"
                }
              />
            </Grid>
<IconButton onClick={switchFields} className={classes.switchButton}>
              <SwapVerticalCircleIcon fontSize="medium" />
            </IconButton>
<Grid item xs={12} className={classes.fullWidth}>
              <CoinField
                activeField={false}
                value={field2Value}
                onClick={() => setDialog2Open(true)}
                symbol={
                  coin2.symbol !== undefined ? coin2.symbol : "Select"
                }
              />
            </Grid>
<hr className={classes.hr} />
{/* Balance Display */}
            <Typography variant="h6">Your Balances</Typography>
            <Grid container direction="row" justifyContent="space-between">
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatBalance(coin1.balance, coin1.symbol)}
                </Typography>
              </Grid>
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatBalance(coin2.balance, coin2.symbol)}
                </Typography>
              </Grid>
            </Grid>
<hr className={classes.hr} />
{/* Reserves Display */}
            <Typography variant="h6">Reserves</Typography>
            <Grid container direction="row" justifyContent="space-between">
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatReserve(reserves[0], coin1.symbol)}
                </Typography>
              </Grid>
              <Grid item xs={6}>
                <Typography variant="body1" className={classes.balance}>
                  {formatReserve(reserves[1], coin2.symbol)}
                </Typography>
              </Grid>
            </Grid>
<hr className={classes.hr} />
<LoadingButton
              loading={loading}
              valid={isButtonEnabled()}
              success={false}
              fail={false}
              onClick={swap}
            >
              <LoopIcon />
              Swap
            </LoadingButton>
          </Grid>
        </Paper>
      </Container>
<Grid
        container
        className={classes.footer}
        direction="row"
        justifyContent="center"
        alignItems="flex-end"
      >
        <p>
          Clearmatics Autonity Uniswap | Get AUT for use in the bakerloo testnet{" "}
          <a href="https://faucet.bakerloo.autonity.network/">here</a>
        </p>
      </Grid>
    </div>
);

LoadingButtonをクリックすると、内部関数(swap以下を参照)が呼び出さswapTokensれ、状態変数coin1.addressを使用coin2.addressfield1Valueて、引数としてイーサリアム関数が呼び出されます。これにより、最初にメタマスク通知でコイン1の使用を許可するようにユーザーに促し、次に別の通知でユーザーにトランザクションの確認を促します。これが完了すると、field1とfield2の値がリセットされます。

状態変数

上記のreturnステートメントは、アプリケーションの状態を追跡するいくつかの状態変数を参照します。これらは:

  • dialog1Open—最初のダイアログウィンドウが開いている天気の記録を保持します
  • dialog2Open—最初のダイアログウィンドウが開いている天気の記録を保持します
  • coin1—コイン1のアドレス、シンボル、バランスの配列
  • coin2—コイン2のアドレス、シンボル、バランスの配列
  • reserves—coin1およびcoin2の流動性準備金をプールに保管します。
  • field1Value—ユーザーの入力をfield1(交換されるcoin1の値)に格納します
  • field2Value—ユーザーの入力をfield2(交換されるcoin2の値)に格納します
  • loading—ロードボタンを制御するブール値

これらは、React.useState83行目のフックで定義されています。

CoinSwapper / CoinSwapper.js

// Stores a record of whether their respective dialog window is open
const [dialog1Open, setDialog1Open] = React.useState(false);
const [dialog2Open, setDialog2Open] = React.useState(false);
// Stores data about their respective coin
const [coin1, setCoin1] = React.useState({
address: undefined,
symbol: undefined,
balance: undefined,
});
const [coin2, setCoin2] = React.useState({
address: undefined,
symbol: undefined,
balance: undefined,
});
// Stores the current reserves in the liquidity pool between coin1 and coin2
const [reserves, setReserves] = React.useState(["0.0", "0.0"]);
// Stores the current value of their respective text box
const [field1Value, setField1Value] = React.useState("");
const [field2Value, setField2Value] = React.useState("");
// Controls the loading button
const [loading, setLoading] = React.useState(false);

これらの状態変数は、次の関数で使用され、前述のreturnステートメントでユーザーに情報を表示するためにも使用されます。

内部機能

上記のreturnステートメントでは、いくつかの内部関数も参照されています。これらは次のとおりです。

  • switchFields
  • handleChange
  • formatBalance
  • formatReserve
  • isButtonEnabled
  • onToken1Selected
  • onToken2Selected
  • swap

switchFields上部と下部のコインを切り替えます。これは、ユーザーがスワップボタンを押すか、ダイアログで反対のトークンを選択したときに呼び出されます(たとえば、coin1がTokenAで、ユーザーがcoin2を選択するときにTokenBを選択した場合)。

const switchFields = () => {
    setCoin1(coin2);
    setCoin2(coin1);
    setField1Value(field2Value);
    setReserves(reserves.reverse());
};

handleChangeはReactJSで使用される一般的な内部関数であり、HTMLイベントを受け取り、データを引き出して状態変数に入れます。coinFiedここでは、最初の値が変更されたときにfield1の値を設定するために使用されていました。

const handleChange = {
    field1: (e) => {
      setField1Value(e.target.value);
    },
};

formatBalance/formatReserveアカウントの残高/予約を素敵で読みやすいものに変えます:

const formatBalance = (balance, symbol) => {
    if (balance && symbol)
      return parseFloat(balance).toPrecision(8) + " " + symbol;
    else return "0.0";
};

isButtonEnabledボタンを有効にするかどうかを決定します。

const isButtonEnabled = () => {
    let validFloat = new RegExp("^[0-9]*[.,]?[0-9]*$");
// If both coins have been selected, and a valid float has been entered which is less than the user's balance, then return true
    return (
      coin1.address &&
      coin2.address &&
      validFloat.test(field1Value) &&
      parseFloat(field1Value) <= coin1.balance
    );
};

onToken1Selected/onToken2Selectedは、coin1 / coin2のダイアログウィンドウが終了したときに呼び出され、関連する状態変数を設定します。

const onToken1Selected = (address) => {
    // Close the dialog window
    setDialog1Open(false);
// If the user inputs the same token, we want to switch the data in the fields
    if (address === coin2.address) {
      switchFields();
    }
    // We only update the values if the user provides a token
    else if (address) {
      // Getting some token data is async, so we need to wait for the data to return, hence the promise
      getBalanceAndSymbol(account, address, provider, signer).then((data) => {
        setCoin1({
          address: address,
          symbol: data.symbol,
          balance: data.balance,
        });
      });
    }
};

swapイーサリアム関数を呼び出してswapTokensスワップを作成し、必要な状態変数をリセットします。

const swap = () => {
    console.log("Attempting to swap tokens...");
    setLoading(true);
swapTokens(
      coin1.address,
      coin2.address,
      parseFloat(field1Value),
      router,
      account,
      signer
    )
      .then(() => {
        setLoading(false);
// If the transaction was successful, we clear to input to make sure the user doesn't accidental redo the transfer
        setField1Value("");
        enqueueSnackbar("Transaction Successful", { variant: "success" });
      })
      .catch((e) => {
        setLoading(false);
        enqueueSnackbar("Transaction Failed (" + e.message + ")", {
          variant: "error",
          autoHideDuration: 10000,
        });
      });
};

スワップ関数の16行目でenqueueSnackbarは、ノードモジュールNotistackのコンポーネントであるを使用しています。Notistackは、一時的な通知を行うための優れたライブラリです。ここでリポジトリを確認してください。

useEffectフック

最後にCoinSwapper/CoinSwapper.js、には4つのuseEffectフックがあり、これらはアプリケーションを最新の変更で最新の状態に保つために使用されます。各フック内のラムダ(コード)は、依存関係の1つが変更されたときに実行されます。依存関係は、ラムダ式の後に関数に渡される変数の配列で定義されます。

1つ目useEffectは、状態変数coin1.addressまたはcoin2.address変更のいずれかが発生したときに呼び出されます。これは、ユーザーが変換する別のコインを選択した場合、またはコインが交換された場合に、新しい準備金が計算されることを意味します。

useEffect(() => {
    console.log(
      "Trying to get Reserves between:\n" +
        coin1.address +
        "\n" +
        coin2.address
    );
if (coin1.address && coin2.address) {
      getReserves(
        coin1.address,
        coin2.address,
        factory,
        signer,
        account
      ).then((data) => setReserves(data));
    }
}, [coin1.address, coin2.address, account, factory, router, signer]);

2番目のフックは、状態変数field1Value coin1.addressまたはcoin2.address変更のいずれかが発生したときに呼び出されます。状態変数の計算と設定を試みますfield2Value。これは、ユーザーが変換ボックスに新しい値を入力した場合、または変換率が変更された場合、出力ボックスの値が変更されることを意味します。

useEffect(() => {
    if (isNaN(parseFloat(field1Value))) {
      setField2Value("");
    } else if (field1Value && coin1.address && coin2.address) {
      getAmountOut(
        coin1.address,
        coin2.address,
        field1Value,
        router
      ).then((amount) => setField2Value(amount.toFixed(7)));
    } else {
      setField2Value("");
    }
}, [field1Value, coin1.address, coin2.address]);

3番目のフックは、約10秒ごとに実行されるタイムアウトを作成します。その役割は、ユーザーの残高が更新および変更されたかどうかを確認することです。これにより、残高の出力を確認することで、トランザクションがいつ完了するかを確認できます。

useEffect(() => {
    const coinTimeout = setTimeout(() => {
      console.log("Checking balances...");
if (coin1.address && coin2.address && account) {
        getReserves(
          coin1.address,
          coin2.address,
          factory,
          signer,
          account
        ).then((data) => setReserves(data));
      }
if (coin1 && account) {
        getBalanceAndSymbol(account, coin1.address, provider, signer).then(
          (data) => {
            setCoin1({
              ...coin1,
              balance: data.balance,
            });
          }
        );
      }
      if (coin2 && account) {
        getBalanceAndSymbol(account, coin2.address, provider, signer).then(
          (data) => {
            setCoin2({
              ...coin2,
              balance: data.balance,
            });
          }
        );
      }
    }, 10000);
return () => clearTimeout(coinTimeout);
});

最後のフックは、コンポーネントが最初にマウントされたときに実行されます。アカウントを設定するために使用されます。

export async function quoteAddLiquidity(
  address1,
  address2,
  amountADesired,
  amountBDesired,
  factory,
  signer
) {
  const pairAddress = await factory.getPair(address1, address2);
  const pair = new Contract(pairAddress, PAIR.abi, signer);

  const reservesRaw = await fetchReserves(address1, address2, pair); // Returns the reserves already formated as ethers
  const reserveA = reservesRaw[0];
  const reserveB = reservesRaw[1];

  if (reserveA === 0 && reserveB === 0) {
    let amountOut = Math.sqrt(reserveA * reserveB);
    return [
      amountADesired.toString(),
      amountBDesired.toString(),
      amountOut.toString(),
    ];
  } else {
    let [amountBOptimal, amountOut] = quote(amountADesired, reserveA, reserveB);
    if (amountBOptimal <= amountBDesired) {
      return [
        amountADesired.toString(),
        amountBOptimal.toString(),
        amountOut.toString(),
      ];
    } else {
      let [amountAOptimal, amountOut] = quote(
        amountBDesired,
        reserveB,
        reserveA
      );
      console.log(amountAOptimal, amountOut);
      return [
        amountAOptimal.toString(),
        amountBDesired.toString(),
        amountOut.toString(),
      ];
    }
  }
}

結論

このブログの最初のセクションでは、ブロックチェーンへの呼び出しとトランザクションを行うために必要なイーサリアム関数について説明しました。次のセクションでは、メインのスワップ機能を機能させるために必要なカスタムコンポーネント、内部関数、およびフックについて説明しました。

流動性の削除/展開機能についてはまだ触れていません。それが次のブログのトピックです

著者にチップを渡すには、次のイーサリアムアドレスを使用できます:0xe7EeDB184E63Fe049EebA79EfeAc72939cB1461D 

このストーリーは、もともとhttps://medium.com/clearmatics/how-i-made-a-uniswap-interface-from-scratch-b51e1027ca87で公開されました

#uniswap #ethereum #token #bitcoin 

ユニスワップインターフェイスを最初から作成した

AWSDynamoDBとLambdaを使用してサーバーレスログインサービスを構築する

それは2022年であり、サーバーレスはもはや技術オタクによって投げ出される派手な用語ではありません。ますます多くの人々が、それが高速で高度にスケーラブルなアプリケーションを構築するための道であることに気づいています。

この記事では、AWSで人気のある2つのサービスを使用して、サーバーレスログインシステムをゼロから構築することに焦点を当てます。

タイトルを読んだので、これら2つをすでに経験している場合は、そうではないと思いますが、次のチュートリアルは適切ではない可能性があります。開始する前に、このプロジェクトのAWSアカウントが必要です。ここで、アカウントにサインアップできます。これから構築するシステムを見てみましょう。

ご覧のとおり、DynamoDBに接続するラムダにアクセスするためにAPIゲートウェイを使用する基本的なログインサービスを構築します。プロジェクトには3つのエンドポイントがあります。最初のエンドポイントは、新しいユーザーを登録し、データベースに詳細を保存するためのものです。2つ目は、ユーザーログイン用で、ユーザーにJWTトークンを発行します。そして3つ目は、トークンが有効であることを確認することです。始めましょう。

1.DynamoDBテーブルを作成する

これから行う最初のステップは、ユーザーデータを保存するDynamoDBテーブルを作成することです。

前述のように、DynamoDBはNoSQLデータベースであり、データは主に主キーを使用して保存およびクエリされます。主キーはパーティションキーになり、場合によってはパーティションキーとソートキーの組み合わせになります。

DynamoDBは、パーティションキーの値を、データが保存されるパーティションを決定する内部ハッシュ関数への入力として使用します。この記事でカバーするには多すぎるので、ここでキーの詳細を読むことができます。

テーブルを作成するには、AWSコンソールに移動し、上部の検索ボックスでDynamoDBを検索して、検索結果から選択します。DynamoDBダッシュボードが表示されたら、[テーブルの作成]オプション(1.1)をクリックします。

1.1

これにより、テーブル名や主キー(1.2)などのテーブルの詳細を入力するように求めるページが表示されます。

ここでは、テーブルにuser-tableという名前を付け、usernameをパーティションキーとして定義します。これは、ユーザーの検索に使用するものです。ソートキーはオプションであることがわかります。単純なアプリケーションでは必要ないため、空白のままにします。

1.2

残りの設定はデフォルト値のままにして、ページの最後にある[テーブルの作成]をクリックできます。これにより、すぐにテーブルが作成されます。DBの準備ができたので、次のステップに進みましょう。

2.IAMロールの作成

IAMロールを作成するには、上部の検索ボックスでIAMを検索し、ダッシュボードが表示されたら、左側のパネル(2.1)から[ロール]に移動して新しいロールを作成し、[ロールの作成]をクリックします。

2.1

ステップ1では、一般的なユースケース(2.2)からLambdaを選択できます。これは、私たちが自分の役割を使用するためのものであり、次のステップに進みます。

2.2

次のステップでは、役割の権限を追加する必要があります。このプロジェクトでは、2つの権限を追加します。

1つはDynamoDB用で、もう1つはCloudWatch用です。それらを追加するには、検索ボックスでDynamo DBを検索し、リスト(2.3)からDynamoDBフルアクセス許可を選択します。

2.3

次に、CloudWatchフルアクセスを検索して追加します。CloudWatchを使用して、ラムダ関数からのログを確認します。

次のステップで、作成するロールに名前を付け、ページの最後にある[ロールの作成]をクリックします。

これにより、ラムダに接続するIAMロールが作成され、DynamoDBおよびCloudWatchにシームレスに接続できるようになります。次のステップに進みましょう。

 

3.ラムダを作成します

次に、APIのバックエンドとして機能するラムダを作成しましょう。上部の検索ボックスでラムダを検索し、ラムダダッシュボードが表示されたら[役割の作成]をクリックします。

次のステップでは、ラムダサービスに名前を付け、デフォルトのランタイムをNode.jsのままにします。これは、このプロジェクトで使用するものです。

PythonまたはRubyを使用してビルドしている場合は、ここで変更できます。次に、[デフォルトの実行ロールのタイトルを変更]をクリックし、[既存のロールを使用する]を選択します。

次に、前の手順で作成した役割を、そのすぐ下のドロップダウンから選択します。これらすべてが(3.1)に入力されたら、create functionをクリックすると、すぐにラムダが作成されます。

3.1

 

4.APIゲートウェイを作成します

次に、アプリケーションがサービスにアクセスするための「玄関ドア」として機能するAPIゲートウェイを作成します。API Gatewayを検索し、ダッシュボードページで[APIの作成]をクリックします。

次のステップでは、作成するAPIのタイプを選択します。表示されたオプションのリストからRESTAPIを選択し、build(4.1)をクリックしてください。

4.1

次のページで、ゲートウェイの名前を指定します。残りのデフォルトはそのままにして、[APIの作成(4.2)]をクリックします。

4.2

次に、アプリケーションで使用するエンドポイントを作成しましょう。まず、アクションをクリックして、このAPIでリソースを作成します(4.3)

4.3

リソース名を指定し、そのパスを定義します。ここでは、ログイン用に1つ作成するので、パスはになり/loginます。任意のパスを定義できます。フロントエンドでのCORSの問題を回避するために、必ず[ APIゲートウェイCORSを有効にする]をクリックし、[リソースの作成](4.4)をクリックしてください。

4.4

次に、このリソースの下にメソッドを作成しましょう。最初にリソースを選択し、アクション(4.5)からメソッドの作成をクリックします

4.5

メソッドのリストから、投稿(4.6)を選択します。これはAPIエンドポイントに使用するものであるため、その横にあるチェックマークをクリックします。

4.6

セットアップでは、Use Lambda proxy Integrationをチェックして、リクエストがラムダ(4.7)にプロキシされていることを確認してください。また、下のドロップダウンから以前に作成したものと同じラムダを選択します。残りのオプションはデフォルトのままにして、[保存]をクリックできます。

4.7

同じ手順に従って、3つのエンドポイントを作成します

  • ログインする
  • 登録
  • 確認。

完了したら、ルートリソースを選択し、アクションのリストから[APIのデプロイ]を選択します。

4.9

展開ステージに名前と説明を付けます。デプロイをクリックすると、APIが数秒でデプロイされます。

4.10

結果のページで、後で使用するために呼び出しURLをコピーします。これがAPIのベースURLになります。

4.11

 

5.バックエンドサービスを作成します

このステップでは、ログインサービスのロジックを含む実際のバックエンドをコーディングします。これをローカルで実行し、後でラムダにデプロイできます。開始するには、VS Codeで新しい空のプロジェクトを作成し、npminitを実行してNodeJSプロジェクトを 初期化します。最終的なフォルダ構造は次のようになります。

次に、index.jsをラムダ関数のハンドラーとして構成しましょう。index.jsを開き、次のコードを追加します。

const registerPath = "/register";
const loginPath = "/login";
const verifyPath = "/verify";

const registerService = require("./functions/register");

const loginService = require("./functions/login");

const verifyService = require("./functions/verify");

const util = require("./helpers/utils/util");

exports.handler = async (event) => {
  console.log(" Request Event : ", event);
  const { httpMethod, resource } = event;
  const requestBody = JSON.parse(event.body);
  let response;
  switch (true) {
    case httpMethod === "POST" && resource === registerPath:
      response = await registerService.register(requestBody);
      break;
    case httpMethod === "POST" && resource === loginPath:
      response = await loginService.login(requestBody);
      break;
    case httpMethod === "POST" && resource === verifyPath:
      response = await verifyService.verify(requestBody);
      break;
    default:
      response = util.buildResponse(404, "404 Not Found ");
  }
  return response;
};

ご覧のとおり、ハンドラーを作成しています。ハンドラーは、APIゲートウェイで定義したパスに対してリクエストメソッドを照合し、対応するサービスを呼び出します。個別のサービスを作成します-

  • / register —リクエスト本文から名前、ユーザー名、パスワードを取得し、DBに新しいユーザーを作成します。成功するとユーザー名を返します。
  • ログイン—リクエスト本文からユーザー名とパスワードを取得し、既存のユーザーのDBと照合します。成功するとユーザー名とJWTトークンを返します。
  • verify —リクエスト本文からユーザー名とJWTトークンを取得し、それをデコードしてトークンが有効であることを確認します。成功した場合は、確認済みのメッセージとユーザー情報を返します。

これらのメソッドのコードは、ここgithubにあります。

プロジェクトの重要な部分は、DynamoDBテーブルに接続して新しいユーザーを作成し、ログイン時にユーザーのクレデンシャルを比較するDBヘルパーです。このために、フォルダーuser.js内にヘルパーを作成します。dbHelper

このヘルパーには2つのメソッドが含まれます。1つはユーザー名である主キーを比較してユーザーが存在するかどうかを確認するためのメソッドです。そして、同じ主キーを使用して新しいユーザーを保存する2番目の方法。

// Load the AWS SDK for Node.js
const AWS = require("aws-sdk");

// Set the region
AWS.config.update({ region: "us-east-1" });

// Create DynamoDB document client
const dynamoDB = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" });

const userTable = "user-table";

exports.getUser = async (username) => {
  const params = {
    TableName: userTable,
    Key: {
      username: username,
    },
  };
  return await dynamoDB
    .get(params)
    .promise()
    .then(
      (response) => {
        return response.Item;
      },
      (error) => {
        console.log("Error fetching user", error);
      }
    );
};

exports.saveUser = async (user) => {
  const params = {
    TableName: userTable,
    Key: {
      username: user.username,
    },
    Item: user,
  };

  return await dynamoDB
    .put(params)
    .promise()
    .then(
      (response) => {
        return true;
      },
      (error) => {
        console.log("Error saving user", error);
      }
    );
};

またauth、ログインおよび検証中にJWTトークンを生成および検証するためのヘルパーを作成します。npmライブラリを使用してjsonwebtoken、定義したJWTシークレットを使用してアクセストークンを生成しています。トークンの有効期限は今のところ1時間に設定しますが、必要に応じて変更できます。

const jwt = require("jsonwebtoken");

exports.generateToken = (userInfo) => {
  if (!userInfo) {
    return null;
  }

  return jwt.sign(userInfo, process.env.JWT_SECRET, {
    expiresIn: "1h",
  });
};

exports.verifyToken = (username, token) => {
  return jwt.verify(token, process.env.JWT_SECRET, (error, response) => {
    if (error) {
      return {
        verified: false,
        message: "Invalid token",
        error: error,
      };
    }

    if (response.username !== username) {
      return {
        verified: false,
        message: "Invalid user",
      };
    }

    return {
      verified: true,
      message: "verified",
    };
  });
};

ここで注意すべきことの1つは、JWTトークンのエンコードに環境変数を使用していることですJWT_SECRET。これは後で定義します。このプロジェクトでは3つのnpmパッケージを使用しました-

  • aws-sdk —DynamoDBに接続するため。
  • bcryptjs —ユーザーのパスワードをDBに保存する前に暗号化するため。
  • jsonwebtoken—JWTトークンを生成および検証します。

プロジェクトの完全なコードは、このGitHubリポジトリにあります。

 

6.コードをラムダにデプロイします

プロジェクトをラムダにアップロードする前に、npm i使用しているパッケージをインストールするために実行したことを確認してください。CLI、VS Code Extension、サーバーレスなど、コードラムダをアップロードする方法は複数あります。ここでは、コンソールから手動でコードをアップロードします。npmパッケージをインストールした後にコマンドを実行してzip -r ./archive.zip *、プロジェクトをラムダ用にzip形式で圧縮します。

それでは、AWSコンソールのラムダに戻りましょう。ここでは、ラムダのコードタブの下にあるオプション(6.1)からのアップロードを見つけることができます。それをクリックして、作成したプロジェクトからzipファイルをアップロードします。

これにより、コードがアップロードされ、すぐにラムダがデプロイされます。既存のラムダの新しいバージョンをアップロードする場合、アプリから呼び出すときにその変更が反映されるまでに1分かかる場合があることに注意してください。

6.1zipをラムダにアップロードする

 

7.秘密鍵を構成します

JWT_SECRET手順6で覚えている場合は、JWTトークンをエンコードするための環境変数を定義する必要があります。Lambdaでこの環境変数を構成しましょう。[構成]タブに移動し、左側のメニューから[環境変数]を選択します。

[編集]をクリックして、コード(7.1)で使用したのと同じ名前の新しい環境変数を作成します。秘密鍵を定義することはできますが、これを他の人と共有したり、コードに鍵の値を保持したりしないでください。このキーを保存すると、APIの準備が整います。

7.1

 

8.Postmanからのテスト

次に、郵便配達員からAPIを1つずつ取得し、すべてがスムーズに実行されているかどうかを確認します。手順4の最後にコピーしたAPIURLを使用するか、APIゲートウェイに移動してURL(4.11)をコピーできます。

まず、registerAPIを使用して新しいユーザーを作成しましょう。ご覧のとおり、名前、ユーザー名、パスワードを入力として渡し、成功すると作成されたユーザー名を返します(8.1)

8.1

また、ユーザーがDynamoDBテーブルを作成していることを確認しましょう。コンソールからテーブルに移動し、テーブルアイテムの探索(8.2)をクリックします。スキャンを実行すると、ユーザーエントリがテーブルに作成されていることがわかります。

8.2

同じクレデンシャルを使用してログインAPIを試して、アクセストークン(8.3)を生成できます。

8.3

最後に、verify APIを使用して、ユーザーに対してトークンを検証します(8.4)。

8.4

ここで完全なpostmanコレクションをインポートできますが、APIURLでベースURLを更新することを忘れないでください。

このストーリーは、もともとhttps://betterprogramming.pub/build-a-serverless-login-service-using-aws-dynamodb-and-lambda-d2ee9bc2e60eで公開されました

#serverless #token #aws #dynamodb #lambda 

AWSDynamoDBとLambdaを使用してサーバーレスログインサービスを構築する
Diego  Elizondo

Diego Elizondo

1654286700

Servicio De inicio De Sesión Sin Servidor Con AWS DynamoDB Y Lambda

Es 2022, serverless ya no es un término elegante que usan los fanáticos de la tecnología. Cada vez más personas se dan cuenta de que es el camino a seguir para crear aplicaciones rápidas y altamente escalables.

En este artículo, nos centraremos en crear un sistema de inicio de sesión sin servidor desde cero con AWS, utilizando dos de sus servicios populares:

Si ya tiene mucha experiencia con estos dos, lo cual supongo que no porque ha leído el título, el siguiente tutorial puede no ser para usted. Antes de comenzar, necesitará una cuenta de AWS para este proyecto, puede registrarse para obtener una aquí . Echemos un vistazo al sistema que vamos a construir.

Como puede ver, crearemos un servicio de inicio de sesión básico que utiliza una puerta de enlace API para acceder a la lambda que se conectará a DynamoDB. Tendremos tres puntos finales en nuestros proyectos, el primero es para registrar un nuevo usuario y almacenar los detalles en la base de datos. El segundo sería para el inicio de sesión del usuario y la emisión de un token JWT para el usuario. Y el tercero es verificar que el token sea válido. Empecemos.

1. Creación de una tabla de DynamoDB

El primer paso que vamos a dar es crear una tabla de DynamoDB donde almacenaremos nuestros datos de usuario.

Como se mencionó anteriormente, DynamoDB es una base de datos NoSQL, los datos se almacenan y consultan principalmente mediante una clave principal , que será la clave de partición y, en algunos casos, una combinación de clave de partición y clave de ordenación.

DynamoDB utiliza el valor de la clave de partición como entrada para una función hash interna que decide la partición en la que se almacenarán sus datos. Puede leer más sobre las claves aquí , ya que sería demasiado para cubrir en este artículo.

Para crear una tabla, vaya a su consola de AWS y busque DynamoDB en el cuadro de búsqueda superior y selecciónelo en los resultados de búsqueda. Una vez que esté en el panel de DynamoDB, haga clic en la opción Crear tabla (1.1)

1.1

Esto lo llevará a una página donde se le pedirá que proporcione los detalles de la tabla, como el nombre de la tabla y las claves principales (1.2).

Vamos a nombrar nuestra tabla user-table por ahora y definir el nombre de usuario como nuestra clave de partición, que es lo que usaremos para buscar usuarios. Puede ver que la clave de clasificación es opcional, la dejaremos en blanco ya que no es necesaria para nuestra aplicación simple.

1.2

Puede dejar el resto de la configuración con los valores predeterminados y hacer clic en crear tabla al final de la página. Esto creará su tabla en unos momentos. Ahora que la base de datos está lista, pasemos al siguiente paso.

2. Creación del rol de IAM

Para crear un rol de IAM, busque IAM en el cuadro de búsqueda superior y una vez que esté en el tablero, vaya a Roles desde el panel izquierdo (2.1) para crear un nuevo rol y haga clic en crear el rol.

2.1

En el paso 1, puede seleccionar Lambda de los casos de uso comunes (2.2), ya que para eso usaremos nuestro rol y pasar al siguiente paso.

2.2

En el siguiente paso, debemos agregar permisos para nuestro rol. Para este proyecto, vamos a agregar dos permisos.

El primero es para Dynamo DB y el otro para CloudWatch. Para agregarlos, simplemente busque Dynamo DB en el cuadro de búsqueda y seleccione el permiso de acceso completo de DynamoDB de la lista (2.3).

2.3

A continuación, busque el acceso completo de CloudWatch y agréguelo también. Vamos a usar CloudWatch para ver los registros de nuestra función lambda.

En el siguiente paso, asigne un nombre al rol que estamos creando y haga clic en crear rol al final de la página.

Esto creará un rol de IAM que conectaremos a nuestro lambda, lo que le permitirá conectarse a DynamoDB y CloudWatch sin problemas. Ahora pasemos al siguiente paso.

 

3. Crea la lambda

Ahora vamos a crear la lambda que servirá como backend para nuestras API. Busque lambda en el cuadro de búsqueda superior y haga clic en crear función una vez que esté en el panel de control de lambda.

En el siguiente paso, asigne un nombre al servicio lambda y deje el tiempo de ejecución predeterminado como Node.js, ya que eso es lo que usaremos para este proyecto.

Si está construyendo con Python o Ruby, puede cambiarlo aquí. A continuación, haga clic en el título Cambiar el rol de ejecución predeterminado y seleccione Usar un rol existente .

Ahora seleccione el rol que creamos en el paso anterior del menú desplegable justo debajo de él. Una vez que todos estos estén completos (3.1), haga clic en crear función y esto creará nuestra lambda en un momento.

3.1

 

4. Cree la puerta de enlace API

A continuación, vamos a crear una API Gateway que actuará como la "puerta de entrada" para que nuestras aplicaciones accedan al servicio. Busque API Gateway y en la página del panel, haga clic en crear API.

En el siguiente paso, seleccionamos el tipo de API que vamos a crear. Asegúrese de seleccionar API REST de la lista de opciones proporcionadas y haga clic en compilar (4.1).

4.1

En la página siguiente, asigne un nombre para la puerta de enlace y puede dejar el resto de los valores predeterminados como están y hacer clic en Crear API (4.2)

4.2

Ahora vamos a crear los puntos finales que vamos a utilizar en nuestra aplicación. Primero, haga clic en acciones y cree un recurso en esta API (4.3)

4.3

Proporcione el nombre del recurso y defina su ruta. Aquí vamos a crear uno para iniciar sesión, por lo que nuestra ruta será /login, puede definir la ruta que desee. Asegúrese de hacer clic en Habilitar API Gateway CORS para evitar problemas de CORS en el front-end y haga clic en crear recurso (4.4).

4.4

Ahora vamos a crear el método bajo este recurso. Seleccione el recurso primero y haga clic en crear método de las acciones (4.5)

4.5

De la lista de métodos, seleccione publicar (4.6) ya que esto es lo que vamos a usar para nuestro punto final de API, y haga clic en la marca de verificación junto a él.

4.6

En la configuración, asegúrese de marcar Usar integración de proxy de Lambda para asegurarse de que nuestras solicitudes se envíen a lambda (4.7). Además, seleccione la misma lambda que creamos anteriormente en el menú desplegable a continuación. Puedes dejar el resto de opciones por defecto y hacer clic en guardar.

4.7

Siga los mismos pasos para crear tres puntos finales

  • acceso
  • Registrarse
  • verificar.

Una vez hecho esto, seleccione el recurso raíz y seleccione Implementar API de la lista de acciones.

4.9

Asigne un nombre y una descripción a su etapa de implementación. Haga clic en implementar y su API se implementará en segundos.

4.10

En la página resultante, copie la URL de invocación para su uso posterior y esta será la URL base de nuestra API.

4.11

 

5. Crea el servicio de back-end

En este paso, codificaremos el backend real que contendrá la lógica para nuestro servicio de inicio de sesión. Podemos hacer esto localmente y luego implementarlo en lambda. Para comenzar, cree un nuevo proyecto vacío en VS Code y ejecute npm init para inicializar un proyecto de Node JS. La estructura de carpetas final se verá así.

Ahora configuremos nuestro index.js como controlador para la función lambda. Abra index.js y agregue el siguiente código.

const registerPath = "/register";
const loginPath = "/login";
const verifyPath = "/verify";

const registerService = require("./functions/register");

const loginService = require("./functions/login");

const verifyService = require("./functions/verify");

const util = require("./helpers/utils/util");

exports.handler = async (event) => {
  console.log(" Request Event : ", event);
  const { httpMethod, resource } = event;
  const requestBody = JSON.parse(event.body);
  let response;
  switch (true) {
    case httpMethod === "POST" && resource === registerPath:
      response = await registerService.register(requestBody);
      break;
    case httpMethod === "POST" && resource === loginPath:
      response = await loginService.login(requestBody);
      break;
    case httpMethod === "POST" && resource === verifyPath:
      response = await verifyService.verify(requestBody);
      break;
    default:
      response = util.buildResponse(404, "404 Not Found ");
  }
  return response;
};

Como puede ver, estamos creando un controlador, que compara el método de solicitud con las rutas que hemos definido en nuestra puerta de enlace API y llama al servicio correspondiente. Crearemos servicios individuales para-

  • /registro: toma el nombre, el nombre de usuario y la contraseña del cuerpo de la solicitud y crea un nuevo usuario en la base de datos. Devuelve el nombre de usuario en caso de éxito.
  • inicio de sesión: toma el nombre de usuario y la contraseña del cuerpo de la solicitud y los compara con la base de datos para los usuarios existentes. Devuelve el nombre de usuario y el token JWT en caso de éxito.
  • verificar: toma el nombre de usuario y el token JWT del cuerpo de la solicitud y lo decodifica para verificar que el token sea válido. Devuelve el mensaje verificado junto con la información del usuario si tiene éxito.

Puede encontrar el código para estos métodos aquí en github .

La parte importante de nuestro proyecto es DB Helper, que se conectará a nuestra tabla de DynamoDB para crear un nuevo usuario y también comparar sus credenciales durante el inicio de sesión. Para ello, crearemos un user.jsayudante dentro de la dbHelpercarpeta.

Este asistente contendrá dos métodos, uno para verificar si el usuario existe comparando la clave principal , que es el nombre de usuario. Y un segundo método para guardar un nuevo usuario usando la misma clave principal.

// Load the AWS SDK for Node.js
const AWS = require("aws-sdk");

// Set the region
AWS.config.update({ region: "us-east-1" });

// Create DynamoDB document client
const dynamoDB = new AWS.DynamoDB.DocumentClient({ apiVersion: "2012-08-10" });

const userTable = "user-table";

exports.getUser = async (username) => {
  const params = {
    TableName: userTable,
    Key: {
      username: username,
    },
  };
  return await dynamoDB
    .get(params)
    .promise()
    .then(
      (response) => {
        return response.Item;
      },
      (error) => {
        console.log("Error fetching user", error);
      }
    );
};

exports.saveUser = async (user) => {
  const params = {
    TableName: userTable,
    Key: {
      username: user.username,
    },
    Item: user,
  };

  return await dynamoDB
    .put(params)
    .promise()
    .then(
      (response) => {
        return true;
      },
      (error) => {
        console.log("Error saving user", error);
      }
    );
};

También crearemos authun asistente para generar y verificar tokens JWT durante el inicio de sesión y la verificación. Estamos usando la jsonwebtokenbiblioteca npm para generar tokens de acceso usando un secreto JWT que definimos. Vamos a establecer la caducidad del token en una hora por ahora, pero puede cambiar esto según sus necesidades.

const jwt = require("jsonwebtoken");

exports.generateToken = (userInfo) => {
  if (!userInfo) {
    return null;
  }

  return jwt.sign(userInfo, process.env.JWT_SECRET, {
    expiresIn: "1h",
  });
};

exports.verifyToken = (username, token) => {
  return jwt.verify(token, process.env.JWT_SECRET, (error, response) => {
    if (error) {
      return {
        verified: false,
        message: "Invalid token",
        error: error,
      };
    }

    if (response.username !== username) {
      return {
        verified: false,
        message: "Invalid user",
      };
    }

    return {
      verified: true,
      message: "verified",
    };
  });
};

Una cosa a tener en cuenta aquí es que estamos usando una variable de entorno JWT_SECRETpara codificar el token JWT, lo definiremos más adelante. Hemos utilizado tres paquetes npm en este proyecto:

  • aws-sdk — para conectarse a DynamoDB.
  • bcryptjs — para cifrar la contraseña del usuario antes de almacenarla en la base de datos.
  • jsonwebtoken— Para generar y verificar tokens JWT.

Puede encontrar el código completo del proyecto en este repositorio de GitHub .

 

6. Implemente el código en lambda

Antes de subir el proyecto a lambda, asegúrese de haber ejecutado npm ipara instalar los paquetes que estamos usando. Hay varios métodos para cargar nuestro código lambda, incluidos CLI, extensión de código VS, sin servidor, etc. Por ahora, vamos a cargar el código manualmente desde la consola. Ejecute el comando zip -r ./archive.zip *después de instalar los paquetes npm, para comprimir nuestro proyecto para lambda.

Ahora volvamos a nuestra lambda en la consola de AWS. Aquí puede encontrar la opción cargar desde (6.1) en la pestaña de código de la lambda. Haga clic en él y cargue el archivo zip del proyecto que acaba de crear.

Esto cargará nuestro código e implementará la lambda en unos momentos. Tenga en cuenta que cuando está cargando una nueva versión para una lambda existente, en algunos casos puede tomar un minuto reflejar esos cambios cuando la llama desde su aplicación.

6.1 Subir zip a lambda

 

7. Configurar la clave secreta

Si recuerda el paso 6, necesitamos definir una variable de entorno JWT_SECRETpara codificar el token JWT. Configuremos esta variable de entorno en nuestro Lambda. Vaya a la pestaña de configuración y seleccione Variables de entorno en el menú de la izquierda.

Haga clic en editar y cree una nueva variable de entorno con el mismo nombre que usamos en nuestro código (7.1). Puede definir su clave secreta, pero recuerde nunca compartirla con nadie ni mantener el valor de la clave en su código. Guarde esta clave y nuestra API ya está lista.

7.1

 

8. Pruebas del cartero

Ahora deja nuestras APIs una por una desde el cartero y comprueba si todo funciona sin problemas. Puede usar la URL de API que copiamos al final del paso 4 o ir a su puerta de enlace de API y copiar la URL (4.11).

Primero, creemos un nuevo usuario usando la API de registro. Como puede ver, estamos pasando el nombre, el nombre de usuario y la contraseña como entradas, y devolvemos el nombre de usuario creado en caso de éxito (8.1)

8.1

También verifiquemos que el usuario haya creado nuestra tabla DynamoDB. Vaya a la mesa desde la consola y haga clic en explorar elementos de la mesa (8.2). Si ejecuta un escaneo, puede ver que la entrada del usuario se ha creado en la tabla.

8.2

Podemos probar nuestra API de inicio de sesión con las mismas credenciales para generar un token de acceso (8.3).

8.3

Y finalmente, use la API de verificación para validar el token contra un usuario (8.4).

8.4

Puede importar la colección completa de cartero aquí , pero recuerde actualizar la URL base con la URL de su API.

Esta historia se publicó originalmente en https://betterprogramming.pub/build-a-serverless-login-service-using-aws-dynamodb-and-lambda-d2ee9bc2e60e

#serverless #token #aws #dynamodb #lambda 

Servicio De inicio De Sesión Sin Servidor Con AWS DynamoDB Y Lambda
Saul  Alaniz

Saul Alaniz

1654286640

Construyendo Tu Propio Token Con Solana

La tecnología Blockchain ha cambiado mucho. Aparte del hecho de que es muy seguro, tenemos muchos tokens que también brindan valor en otras áreas.

Hoy vamos a hablar de Solana. Solana es la cadena de bloques más rápida del mundo y el ecosistema de criptografía de más rápido crecimiento, con miles de proyectos que abarcan DeFi, NFT, Web3 y más.

Este tutorial se centrará en la creación de su token utilizando la plataforma Solana. ¡Saltamos!

¿Qué es un token (brevemente)?

Un token en el mundo de la cadena de bloques representa un conjunto de reglas codificadas en un contrato inteligente . Cada token pertenece a una dirección de blockchain.

Es esencialmente un activo digital que se almacena de forma segura en la cadena de bloques.

¿Por qué querrías crear tu propio token?

Dado que un token es un activo digital, puede crear el suyo propio.

Sin entrar demasiado en detalles, la idea básica de dinero/activo es el resultado del hecho de que la gente está de acuerdo en que algo tiene valor. Antes de que existiera el dinero fiduciario, era común que las personas comerciaran únicamente con bienes.

Intercambiaban productos de valor (según lo acordado) para obtener otra cosa de relativo valor aproximado.

En la misma línea hoy, si tiene una comunidad o un producto y decide no usar moneda convencional (como en la moneda de su país), puede crear un activo digital y exigir que las personas le paguen a través de eso.

Al igual que el dinero, debería haber algún tipo de escasez; en el mundo de las criptomonedas/blockchain, esto se conoce como "suministro total" . La oferta total se refiere a la cantidad de monedas o fichas que existen actualmente y están en circulación. Idealmente, no debería haber un suministro ilimitado de nada que tenga valor.

 

Otra razón para crear tu token es para aprender. La creación de su token le enseñará algunos conceptos muy importantes sobre blockchain que serán útiles para el desarrollo general de blockchain y Web3.

requisitos previos

Para crear un token con Solana, necesita lo siguiente en su máquina:

Ejecute solana -versionpara confirmar que lo ha instalado.

Es posible que se le pida que ejecute esto:

PATH="/home/localhost/.local/share/solana/install/active_release/bin:$PATH"

Instale la CLI de SPL (Biblioteca de programas de Solana). Esto es necesario para la creación de tokens:

corre cargo install spl-token-cli_

Entender algunos términos importantes

Antes de continuar con la creación de un token SPL, hay algunos conceptos que me gustaría compartir con ustedes a continuación.

Entorno principal y de desarrollo

Solana opera en dos entornos: el entorno principal y el entorno de desarrollo. Es posible que esté familiarizado con estos términos como desarrollador. La red principal es donde se mantiene la red principal de Solana para la producción.

Para fines de desarrollo y prueba, el entorno de desarrollo es lo que se utiliza. Para este tutorial, utilizaremos el entorno de desarrollo.

De forma predeterminada, su entorno está configurado como "Principal". Antes de continuar, debemos establecer nuestro entorno en "Desarrollo":

solana config set --url https://api.devnet.solana.com

Para comprobar nuestro entorno de clúster de Solana, ejecute: solana config get.

Esto confirma que el entorno está en desarrollo.

Tarifa de transacción

Se paga una tarifa de transacción cuando se transfiere una cierta cantidad de criptomonedas de una billetera a otra.

Las tarifas de transacción son de naturaleza flexible y pueden variar según cuán ocupada esté la red de blockchain.

Suministro total

Como señalamos anteriormente, el suministro total se refiere a la cantidad de tokens que existen actualmente y están en circulación o bloqueados de alguna manera.

Es la suma de las monedas que se han extraído (o emitido; acuñado) menos el número total de monedas que se han destruido o quemado.

Cartera

Una billetera de criptomonedas es un dispositivo, medio físico, programa o servicio que almacena las claves públicas y/o privadas para transacciones de criptomonedas.

Además de esta función básica de almacenar las claves, una billetera de criptomonedas también ofrece con mayor frecuencia la funcionalidad de cifrar y/o firmar información.

Piense en ello como su billetera física donde guarda su dinero. Así como puede tener varias billeteras físicamente, también puede aplicar el mismo principio a las cadenas de bloques.

Crear una ficha

Antes de crear un token, necesita algo de Solana (incluso una Solana será suficiente). Para obtener una Solana, puedes lanzarla desde el aire (así: solana airdrop 1) desde tu terminal.

(Nota: Recuerda que esta no es una Solana real, ya que estamos en un entorno de desarrollo)

Para crear un token vamos a utilizar la herramienta SPL que instalamos previamente. corre spl-token create-token_ Esto creará el token, que también se conoce como identificador de token. Cópialo y mantenlo guardado.

Creando una cuenta

Lo que hicimos arriba fue crear un token; ahora necesitamos tener una cuenta para almacenarlo.

Así como almacenamos dinero en nuestras cuentas bancarias, podemos tener diferentes criptomonedas en diferentes cuentas.

Para crear una cuenta para almacenar nuestro token, ejecute: spl-token create-account <token-identifier>. Reemplácelo <token identifier> con el identificador de token real que copió.

acuñación

Minting crypto es el proceso de generar nuevas monedas mediante la autenticación de datos, la creación de nuevos bloques y el registro de la información en la cadena de bloques a través de un protocolo de prueba de participación .

Para acuñar un token usando Solana, ejecute: spl-token mint <token-identifier> <token-amount>.

Acuñé 1,000,000 tokens para esto. Tienes la libertad de hacer más o menos que eso. Tenga en cuenta que puedo acuñar más tokens a los tokens recién acuñados en la cuenta ejecutando spl-token mint <token-identifier> <token-amount>nuevamente.

Siempre puedes consultar tu saldo ejecutando: spl-token balance <token-identifier>.

Podemos crear tantos tokens como queramos y crear cuentas separadas para ellos.

Limitar el suministro total y la quema

Limitar el suministro total simplemente significa que desea evitar que se acumulen más tokens. La razón de esto está incrustada en la economía.

Cuando algo tiene demasiado excedente, el valor tiende a caer. Ahora puede preguntarse, ¿y si la moneda ya tiene un excedente? Aquí es donde ocurre la quema.

Quemar en cripto significa eliminar permanentemente una cantidad de tokens de la circulación. Esto generalmente se hace transfiriendo los tokens en cuestión a una dirección de grabación, una dirección de billetera desde la cual nunca se pueden recuperar. Esto a menudo se describe como destruir fichas.

La limitación de nuestro suministro total se logra en Solana al deshabilitar nuestro poder o autoridad de menta. Para hacer eso para un token en particular, ejecute: spl-token authorize <token-identifier> mint --disable.

Esto ahora significa que no hay forma de que podamos agregar tokens adicionales al suministro total.

Para quemar algunos tokens, ejecutamos esto: spl-token burn <token-account-addresss> <amount>.

Quemaré 200 000 tokens de mis 1 000 000 tokens (el suministro total).

Transferencia entre monederos

La transferencia entre billeteras es tan simple como lo que hacemos regularmente con las cuentas bancarias; podemos transferir de una cuenta a otra cuenta. Solo necesita la dirección de la billetera. Para obtener más información sobre los comandos de transferencia, ejecute spl-token transfer --help.

Debe proporcionar toda la información necesaria para ejecutar una transferencia.

spl-token transfer \[FLAGS\] [OPTIONS] <TOKEN_ADDRESS> <TOKEN_AMOUNT> <RECIPIENT_ADDRESS or RECIPIENT_TOKEN_ACCOUNT_ADDRESS>

Nombrando tu token

Puede darle a su token un nombre de su elección. Es tan simple como estar en el entorno principal y realizar una solicitud de extracción después de agregar otro bloque JSON a la lista de tokens grandes en src/tokens/solana.tokenlist.json .

Puede consultar esta página para obtener más información.

Conclusión

¡Felicitaciones! Ha seguido hasta el final de esta guía. Este es un tutorial que se centra en crear un token con la red Solana y espero que te haya resultado útil.

Además de cumplir con el requisito, seguimos adelante y creamos nuestro token, antes de crear una cuenta (billetera), dentro de la cual almacenamos el token. También exploramos algunos términos como suministro total, quema y acuñación para ayudar a brindar contexto.

Esta historia se publicó originalmente en https://blog.logrocket.com/building-token-solana/

#solana #token 

Construyendo Tu Propio Token Con Solana
坂本  篤司

坂本 篤司

1654286460

Solanaで独自のトークンを作成する

ブロックチェーン技術は大きく変化しました。安全性が高いという事実の他に、他の分野でも価値を提供するトークンがたくさんあります。

今日はソラナについてお話します。Solanaは、世界で最も高速なブロックチェーンであり、暗号通貨で最も急速に成長しているエコシステムであり、DeFi、NFT、Web3などにまたがる何千ものプロジェクトがあります。

このチュートリアルは、Solanaプラットフォームを使用してトークンを作成することに重点を置いています。飛び込みましょう!

トークンとは(簡単に)?

ブロックチェーンの世界のトークンは、スマートコントラクトにエンコードされた一連のルールを表します。各トークンはブロックチェーンアドレスに属しています。

これは本質的に、ブロックチェーンに安全に保存されるデジタル資産です。

なぜあなたはあなた自身のトークンを作りたいのですか?

トークンはデジタルアセットであるため、独自のトークンを作成できます。

雑草に深く入り込むことなく、お金/資産の基本的な考え方は、人々が何かに価値があることに同意するという事実の結果です。法定紙幣が生まれる前は、人々は商品だけを取引するのが一般的でした。

彼らは、比較的おおよその価値のある別のものを得るために、(彼らが合意したことに応じて)価値のある製品を交換しました。

今日と同じように、コミュニティや製品があり、(自国の通貨のように)従来の通貨を使用しないことにした場合は、代わりにデジタルアセットを作成し、代わりにそれを通じて支払うように人々に要求することができます。

お金と同じように、ある種の不足があるはずです—暗号/ブロックチェーンの世界では、これは「総供給」として知られています。総供給量とは、現在存在し、流通しているコインまたはトークンの数を指します。理想的には、価値のあるものを無制限に供給してはなりません。

 

トークンを作成するもう1つの理由は、学習のためです。トークンを作成すると、一般的なブロックチェーンとWeb3の開発に役立つブロックチェーンに関するいくつかの非常に重要な概念がわかります。

前提条件

Solanaを使用してトークンを作成するには、マシンに次のものが必要です。

実行solana -versionして、インストールしたことを確認します。

これを実行するように求められる場合があります。

PATH="/home/localhost/.local/share/solana/install/active_release/bin:$PATH"

SPL(Solana Program Library)CLIをインストールします。これは、トークンの作成に必要です。

を実行しますcargo install spl-token-cli

いくつかの重要な用語を理解する

さらにSPLトークンを作成する前に、次に共有したいいくつかの概念があります。

メインおよび開発環境

Solanaは、メイン環境と開発環境の2つの環境で動作します。あなたは開発者としてこれらの用語に精通しているかもしれません。メインネットワークは、本番用のメインのSolanaネットワークが保持される場所です。

開発とテストの目的では、開発環境が使用されます。このチュートリアルでは、開発環境を使用します。

デフォルトでは、環境は「メイン」に設定されています。先に進む前に、環境を「開発」に設定する必要があります。

solana config set --url https://api.devnet.solana.com

Solanaクラスター環境を確認するには、次のコマンドを実行しますsolana config get

これは、環境が開発中であることを確認します。

取引手数料

あるウォレットから別のウォレットに一定量の暗号通貨が転送されるときに、取引手数料が支払われます。

取引手数料は本質的に柔軟であり、ブロックチェーンのネットワークがどれだけ混雑しているかによって異なります。

総供給

先に述べたように、総供給量とは、現在存在し、流通しているか、何らかの理由でロックされているトークンの数を指します。

これは、採掘された(または発行された;採掘された)コインの合計から、破壊または燃焼されたコインの総数を引いたものです。

財布

暗号通貨ウォレットは、暗号通貨取引の公開鍵や秘密鍵を保存するデバイス、物理メディア、プログラム、またはサービスです。

キーを保存するこの基本的な機能に加えて、暗号通貨ウォレットは、情報を暗号化および/または署名する機能も提供することがよくあります。

あなたがあなたのお金を保つあなたの物理的な財布としてそれを考えてください。物理的に複数のウォレットを持つことができるのと同じように、同じ原則をブロックチェーンに適用することもできます。

トークンの作成

トークンを作成する前に、いくつかのSolanaが必要です(1つのSolanaでも十分です)。ソラナを1つ入手するには、ターミナルからエアドロップ(次のように:)できsolana airdrop 1ます。

(注:開発環境にあるため、これは実際のSolanaではないことに注意してください)

トークンを作成するには、以前にインストールしたSPLツールを使用します。を実行しますspl-token create-token。これによりトークンが作成されます。トークンはトークン識別子とも呼ばれます。コピーして保存してください。

アカウントの作成

上記で行ったことはトークンを作成することです—今度はそれを保存するためのアカウントが必要です。

銀行口座にお金を保管するのと同じように、口座ごとに異なる暗号通貨を使用できます。

トークンを保存するアカウントを作成するには、次のコマンドを実行しますspl-token create-account <token-identifier><token identifier> コピーした実際のトークン識別子に置き換えます。

鋳造

暗号の作成は、データを認証し、新しいブロックを作成し、プルーフオブステークプロトコルを介して情報をブロックチェーンに記録することにより、新しいコインを生成するプロセスです。

Solanaを使用してトークンを作成するには、次のコマンドを実行しますspl-token mint <token-identifier> <token-amount>

このために1,000,000トークンを作成しました。あなたはそれ以上またはそれ以下のことを自由に行うことができます。もう一度実行することで、アカウント内のミントされたばかりのトークンにさらにトークンをミントできることに注意してくださいspl-token mint <token-identifier> <token-amount>

次のコマンドを実行すると、いつでも残高を確認できますspl-token balance <token-identifier>

トークンはいくつでも作成でき、それらに個別のアカウントを作成できます。

総供給と燃焼を制限する

総供給量を制限するということは、それ以上のトークンが作成されないようにすることを意味します。この理由は経済学に埋め込まれています。

余剰が多すぎると値が下がる傾向があります。今、あなたは疑問に思うかもしれません、もしコインがすでに余剰になっているとしたら?ここで燃焼が起こります。

暗号での書き込みとは、循環から多数のトークンを永久に削除することを意味します。これは通常、問題のトークンを書き込みアドレス(トークンを取得できないウォレットアドレス)に転送することによって行われます。これは、トークンの破棄として説明されることがよくあります。

私たちの総供給を制限することは、私たちのミントパワーまたは権限を無効にすることによってソラナで達成されます。特定のトークンに対してこれを行うには、次を実行しますspl-token authorize <token-identifier> mint --disable

これは、総供給量にトークンを追加する方法がないことを意味します。

いくつかのトークンを書き込むために、これを実行します:spl-token burn <token-account-addresss> <amount>

1,000,000トークン(総供給量)から200,000トークンを燃やします。

ウォレット間の転送

ウォレット間の転送は、銀行口座で定期的に行うのと同じくらい簡単です。あるアカウントから別のアカウントに転送できます。必要なのはウォレットアドレスだけです。転送コマンドの詳細については、を実行してspl-token transfer --helpください。

転送を実行するために必要なすべての情報を提供する必要があります。

spl-token transfer \[FLAGS\] [OPTIONS] <TOKEN_ADDRESS> <TOKEN_AMOUNT> <RECIPIENT_ADDRESS or RECIPIENT_TOKEN_ACCOUNT_ADDRESS>

トークンに名前を付ける

トークンに任意の名前を付けることができます。メイン環境にいて、 src / tokens / solana.tokenlist.jsonの大きなトークンリストに別のJSONブロックを追加した後、プルリクエストを行うのと同じくらい簡単です。

詳細については、このページをご覧ください。

結論

おめでとうございます!このガイドの最後まで続きました。これは、Solanaネットワークを使用したトークンの作成に焦点を当てたチュートリアルであり、お役に立てば幸いです。

要件を満たす以外に、トークンを保存するアカウント(ウォレット)を作成する前に、先に進んでトークンを作成しました。また、コンテキストを提供するために、総供給、燃焼、ミンティングなどのいくつかの用語についても検討しました。

このストーリーは、もともとhttps://blog.logrocket.com/building-token-solana/で公開されました

#solana #token 

Solanaで独自のトークンを作成する

イーサリアムでのサインインがゲームチェンジャーである理由

イーサリアムでのサインインは、インターネット上でユーザーが選択するためのゲームチェンジャーです。

「ビッグログイン」に送信する代わりに、ユーザーは、仲介なしで、ブロックチェーンアカウントを制御する同じキーを使用してログインできるようになりました。このアプローチには有望ですが、ユーザーに有利なパワーダイナミクスのバランスを取り直すことを保証するものではありません。イーサリアムでのサインインにより、大企業がユーザーのサービスへのアクセス能力を剥奪したり、彼らの行動をスパイしたりすることができなくなる道が開かれます。

イーサリアムでのサインインは、完全にオープンで開発された認証のオープンスタンダードであり、dapps、アプリ、ウォレット、セキュリティ会社などのコミュニティメンバーとの公開討論を通じて通知されます。すべての会議の記録とメモはlogin.xyzにあります。このアプローチは、プライバシーとデジタル権利の擁護者によって正当に抗議された、ハイテク巨人や政府ベンダーに見られる独自のIDシステムのクローズド開発とはかけ離れています。

対照的に、Sign-In with Ethereum(EIP-4361)は、Webベースのサービスで安全に認証するためのEthereumアカウントのオープンクリエイティブコモンズ(CC)署名形式を定義します。これは、イーサリアム財団とENSからの直接の支援を受けてコミュニティによって構築され、昨年末にスプルースが主導権を握りました。イーサリアムでのサインインの重要性と、それがWeb3のすべてのビルダーにとって「ConnectWallet」をはるかに超えていることについて話し合うことに興奮しています。

ウォレットとサインインを接続する

「ウォレットの接続」ボタンは、今日のdappsの定番です。ボタンを押すと、Web3とブロックチェーンの相互作用への旅が始まります。

ただし、ウォレットを接続すると、使用していると主張するアカウントをアプリに伝えることができ、保証はそこで止まります。スマートコントラクトとやり取りしたり、暗号を送信したり、dappを介してメッセージに署名したりするために、どのアカウントを使用するかをウォレットが理解することが重要です。ウォレットの接続は非常に基本的です。dappはあなたのことを何も覚えておらず、簡単なやり取りの最前線を確立しています。

アプリケーションが、ユーザーの設定やプライベートチャットメッセージの読み込みなど、ユーザーとのより豊富なコンテキストインタラクションを必要とする場合は、アカウントを制御するふりをしているだけでなく、アカウントの背後にある実際のキーホルダーと話していることを最初に確認する必要があります。「ConnectWallet」はこの保証を提供しませんが、Ethereumでのサインイン(SIWE)は提供します。言い換えると、ユーザーを認証して、データを安全に読み書きするためのセッションを確立する必要があります。この例では、ConnectedCarlとSessionSamを紹介します。

Connected Carlはdappsを使用しており、素晴らしい時間を過ごしています。彼は、財布を接続するだけで、ユニスワップで取引したり、Aaveで貸したり、OpenSeaでNFTを購入したりすることができます。しばらくの間、カールはある日まで状況は非常にうまくいっています。彼は問題にぶつかります。彼は、3回目、4回目、5回目に使用したときに、これらのdappsが彼について何かを覚えて、より良い体験を提供できるようにしたいと考えています。 。

カールは、ユニスワップが彼の清算設定を自動的にインポートした場合、Aaveが彼のお気に入りの貸付市場を覚えているか、OpenSeaでさえ0x2Fe1a3 ...アカウントではなく彼の名前を覚えていれば、彼の経験がどれほど良くなるかを考えています。カールは財布を接続するたびに正方形から再開する必要があります。

セッションサムにはこの問題はありません。dappsで認証し、セッションを確立した後、この情報が保存されます。サムが切断して再度認証した場合でも、サムは中断したところから続行し、アプリケーションでサムのことをすべて覚えています。彼の情報は、彼が管理するリモートデータボールトに保存することもできます。

サインインとイーサリアムの統合

Web3全体で、何らかの形の「イーサリアムによるサインイン」を提供する多くの既存のサービスがありますが、標準的なサービスは多くありません。彼らは通常、これを使用して、アカウントに関する特権メタデータを管理できるユーザーとのCookieベースのセッションを確立します。たとえば、ユーザーがWebサイトで自分のプロファイルをカスタマイズできるようにする場合(OpenSeaのように)、ユーザーが変更を加える前にユーザーを認証して、ユーザーだけが自分のプロファイルを編集できるようにする必要があります。このためのワークフローは次のようになります。

ウォレットを接続した後の最初のステップは、ユーザーが自分が何に夢中になっているのかを理解できるように、人間が読めるメッセージをユーザーに提供することです。ユーザーに「ログイン」、「サインイン」についての一貫性のない言い回し、場合によっては任意の数字(「ここでは、このランダムでクレイジーな文字と数字のセットに署名する」)が表示される場合がたくさんあります。代わりに、既存の慣行、いくつかの優れたセキュリティ対策、および人間が読める形式と安全な形式のバランスをとる厳格な文法に基づいて、一連の必須フィールドを定義できます。さらに、ウォレットは、少なくともこの種のメッセージをユーザーに提供し続けるために、既存のインターフェースとプラクティスを変更する必要はありません。

まず、これらのごちゃごちゃした「イーサリアムでのサインイン」メッセージをすべて受け取り、ユーザーにリクエストを提示する一般的な方法を受け入れます。

共通メッセージ-共通インターフェース

合意された署名メッセージ形式により、アプリとウォレットは同じ言語を話すことができるようになりました。アプリがユーザーに署名リクエストを提示すると、ウォレットはリクエストをチェックし、EIP-4361メッセージとして適合するかどうかをチェックし、ユーザーにWebサイトにサインインしていることを通知できます。

この時点で、ユーザーに署名する任意のテキストブロックを提示する代わりに、ウォレットは、ユーザーが実行しようとしているアクションについての疑念を取り除き、心地よい、使いやすい定型化されたインターフェイスを提示できます。ウォレットは署名要求を理解しているため、ユーザーは確認ダイアログをクリックするだけで「サインイン」できるようになりました。完全な透明性を確保するために、仕様では、メッセージ全体とフィールドを追加のサブインターフェイス(詳細ビューなど)で引き続き使用できるようにする必要があると規定されています。

EIP-4361メッセージから、よりクリーンなインターフェイスが得られます。

この仕様では、フィッシング攻撃を防ぐためのドメインバインディングや、リプレイ攻撃を防ぐためのナンスなど、ウォレットの追加のセキュリティ要件も導入されており、ユーザーはエクスペリエンス全体を通じてさらに保護されます。たとえば、ウォレットが有効なSIWEメッセージを検出したが、ユーザーがサインインしてexample.comいるが実際にはオンexampie.comになっている場合、ウォレットはユーザーに状況について警告できます。

今日のほとんどのdappは、保存された設定(ライトモードまたはダークモード)、ユーザー作成コンテンツのドラフト、またはプライバシー、スケーラビリティ、またはアクセス制御のためにパブリックブロックチェーンに保存されていないものでユーザーエクスペリエンスを強化することはできません。これらは、web2で頻繁に遭遇する優れたUXを提供するためのテーブルステークです。

幸い、イーサリアムでのサインインは、イーサリアムユーザーが既存のウォレットでサービスに安全にログインすることでこれらすべての機能を楽しむ方法を標準化しました。すべてパスワードを覚えておく必要はありません。ユーザーがdappsにログインすると、web2アプリケーションに匹敵するUXとの豊富なコンテキストインタラクションを楽しむことができます。Dappsは、ユーザーを単なる公開鍵以上のものとして記憶し、ユーザーが認証した後、プロファイル、資格情報、設定などをセッションに持ち込むことができるようになりました。

ウォレット、ただしSIWEを利用

これらの新機能を使用して、分散型サービスがユーザーデータを尊重して処理する方法の最初の主要なエントリポイントとしてイーサリアムを使用したサインインにより、web2とweb3の間のギャップを埋めることが可能になりました。これで、web2組織がEthereumでサインインを採用する利点のいくつかがここにあります。

イーサリアムでのサインインは無料です

web2企業がweb3エコシステムに有意義に関与できる最も単純でリスクの低い方法の1つは、Ethereumでのサインインです。イーサリアムでのサインインは、ユーザーエクスペリエンスやユーザーベース全体の関連性を損なうことなく、Web3コンポーネントの統合パスとして機能します。安全で、web3に興味のない既存のユーザーには影響を与えず、web2企業のweb3戦略に直接情報を提供する重要なユーザー調査を提供します。

この点を最もよく説明するために、簡単なメッセージに絞り込むことができます。

最小限の組み立てが必要

SIWEは、メッセージに署名するだけなので、エンドユーザーには何の費用もかかりません。ブロックチェーントランザクションは必要なく、鉱夫にガスを支払う必要もありません。SIWEは、ログインしていることを確認し、ログインするために署名するだけのユーザーです。

サインインとイーサリアムを使用すると、既存のユーザーにイーサリアムアカウントを関連付けさせ、それを認証の手段として使用することもできます。この場合、ユーザーは新しい形式のパスワードなしのログインを受け取り、プロセスで制御する識別子に切り替えることができます。これらすべてに加えて、web3に精通していない既存のユーザーは、この統合の影響を受けないため、心配する必要はありません。コアユーザーエクスペリエンスから何も変更することなく、当初の意図どおりにアプリケーションまたはサービスを引き続き使用できます。

イーサリアムでのサインインは、単に追加のログインオプションを追加し、ユーザーがあなたとどのように関わるかについて少し柔軟性を提供します。web2企業として、あなたはweb3へのユーザーの最初のタッチポイントとなり、彼らが自分のデジタルIDを制御する旅を支援する機会があります。これは当然、ユーザーの信頼を高め、ブランドの成長につながる可能性があります。

イーサリアムでサインインするとユーザーがパワーアップ

さらに、ユーザーがイーサリアムでサインインするとき、ログインに選択したアカウントに応じて、ユーザーは自分自身についてもう少し自発的にサービスに紹介することがあります。これには、チェーン上の履歴が含まれる(または含まれない)可能性があり、これらの事実に基づいて追加のメリットを提供できる可能性があります。これにより、既存のサービスの履歴と、オンチェーンアクティビティによって示される新しい関心の両方を持つ新しい形態のパワーユーザーが生まれます。

これは、ユーザーがその特定の履歴をサービスと共有することを決定したかどうかに依存するという事実を強調することが重要です。サービスでは、ユーザーにこの事実を認識させることをお勧めします。常に完全にオプトインする必要があり、サービスは、必要に応じて新しいアカウントを使用できることをユーザーに通知する必要があります。

ユーザーはいつでも新しいアカウントを生成してログインしたり、オンチェーンアクティビティを備えた既存のアカウントでログインしたりできます。

セッションサムに会ったことがない場合は、元の投稿をチェックしてください

ユーザーがウォレットを使用してログインしたり、既存のアカウントにリンクしたりできる場合、ユーザーは自分の条件で組織とより多くの情報を共有でき、これにより、両方の当事者に大きな価値をもたらすことができます。たとえば、企業は、インセンティブを与えたいものや作成したいプログラムに応じて、NFTの保有、DAOメンバーシップ、さらにはDeFiアクティビティに基づいてゲートエクスペリエンスを作成できます。例えば、

  • NFTを保持しているアーティストの長年のファンは、スカルパーが売り上げに群がる前に、早期割引のコンサートチケットを購入できます。
  • 開発者に焦点を当てたDAOのメンバーは、特別なサーバーホスティングパッケージを利用できるようになる場合があります。
  • 分散型取引所のパワーユーザーには、レバレッジ、クレジット、専任のアカウントマネージャーなどの金融サービス会社のプレミアム機能が提供される場合があります。

これにより、企業は、イーサリアムでのサインインを使用してログインした可能性のある既存のアカウントへのNFTドロップやPOAP配布など、ユーザーに新しいweb3エクスペリエンスを作成するための扉も開かれます。

組織は、ユーザーに直接的なメリットを提供するだけでなく、SIWEを使用して、すべてのデジタルプロパティ全体でネットワーク効果を調整し、部門や外部パートナー間の相互運用性の障壁を取り除くこともできます。たとえば、スポーツイベントのチケットスタブはNFTとして表すことができ、イベントの重要性(チャンピオンシップの勝利など)により、同じNFTを使用すると、ユーザーはAAAビデオゲームで仮想化粧品を着用できます。

イーサリアムでのサインインは新規ユーザーに等しい

最後に、ユーザー制御のIDとデータの擁護者として、 Web3ユーザーというサービスを利用することに興奮している何百万ものユーザーに製品を紹介できるようになりました。

これは、既存のユーザーベースに機能を追加するだけでなく、Web3の決定を気にする何百万ものユーザーにサービスを紹介します。これらのユーザーはweb3の精神に従って生活しており、暗号ネイティブであり、少なくとも自律性を高めるサービスを試してみることをいとわない。

イーサリアムでサインインを導入することにより、あなたはあなたがweb3フォワード組織であることを示し、あなたが気にかけていることを示すための最初の一歩を踏み出します。

EIP-4361のおかげで、Ethereumアカウントを使用してユーザーを認証する標準的な方法があります。これは、web3の機能をweb2にもたらすだけでなく、web3ユーザーにweb3に適した従来の組織を紹介します。 

この物語はもともとhttps://blog.spruceid.com/ で公開されました

#ethereum #web3 #blockchain #token 

イーサリアムでのサインインがゲームチェンジャーである理由

Por Qué iniciar Sesión Con Ethereum Cambia Las Reglas Del Juego

Iniciar sesión con Ethereum es un cambio de juego para la elección del usuario en Internet.

En lugar de enviar " Big Login ", los usuarios ahora pueden iniciar sesión con las mismas claves que controlan sus cuentas de blockchain, sin un intermediario. Este enfoque tiene la promesa, pero no la garantía, de reequilibrar la dinámica de poder a favor del usuario. Con Iniciar sesión con Ethereum, abrimos un camino en el que las grandes corporaciones ya no pueden privar a un usuario de la capacidad de acceder a los servicios ni espiar sus acciones.

El inicio de sesión con Ethereum es un estándar abierto para la autenticación desarrollado completamente al aire libre, informado a través del discurso público con miembros de la comunidad a través de dapps, aplicaciones, billeteras, empresas de seguridad y mucho más. Puede encontrar todas las grabaciones y notas de las reuniones en login.xyz . Este enfoque está muy lejos del desarrollo cerrado de los sistemas de identidad patentados que se encuentran en los gigantes tecnológicos o los proveedores gubernamentales, protestados legítimamente por los defensores de la privacidad y los derechos digitales .

Por el contrario, Iniciar sesión con Ethereum (EIP-4361) define un formato de firma de creative commons (CC) abierto para las cuentas de Ethereum para autenticarse de forma segura con cualquier servicio basado en la web. Fue construido por la comunidad con el apoyo directo de la Fundación Ethereum y ENS, con Spruce elegido para liderar el cargo a fines del año pasado. Me emociona hablar sobre la importancia del inicio de sesión con Ethereum y cómo es mucho más que "conectar billetera" para todos los desarrolladores en Web3.

Conectar Wallet vs. Iniciar sesión

El botón "Conectar billetera" es un elemento básico de las dapps en la actualidad. Presionar el botón inicia el viaje de uno a Web3 y las interacciones de blockchain.

Sin embargo, conectar una billetera le permite decirle a la aplicación qué cuenta afirma estar usando, y las garantías terminan ahí. Es más para su billetera entender qué cuenta desea usar para interactuar con contratos inteligentes, enviar criptografía o incluso firmar mensajes a través de la dapp. Conectar una billetera es increíblemente básico: la dapp no ​​recuerda nada sobre usted y está estableciendo un frente para interacciones simples.

Cuando las aplicaciones desean interacciones contextuales más ricas con los usuarios, como cargar sus preferencias o mensajes de chat privados, primero debemos asegurarnos de que estamos hablando con el titular de la clave real detrás de la cuenta, y no con alguien que simplemente pretende controlar la cuenta. "Connect Wallet" no proporciona esta garantía, pero Sign-In with Ethereum (SIWE) sí. Dicho de otra manera, necesitamos autenticar al usuario para establecer una sesión con él para leer y escribir sus datos de forma segura. Para este ejemplo, me gustaría presentar a Connected Carl y Session Sam:

Connected Carl usa dapps y se divierte mucho. Puede realizar transacciones en Uniswap, prestar en Aave o incluso comprar un NFT en OpenSea, simplemente conectando su billetera. Durante un tiempo, las cosas van bastante bien para Carl hasta que un día se encuentra con un problema: desea que estos dapps recuerden algo sobre él para brindarle una mejor experiencia cuando vuelva la tercera, cuarta y quinta vez que los usó. .

Carl está pensando en cuánto mejor podría ser su experiencia si Uniswap importara automáticamente sus preferencias de liquidación, Aave recordara sus mercados de préstamos favoritos o incluso OpenSea recordara su nombre en lugar de una cuenta 0x2Fe1a3... Carl tiene que reiniciar desde cero cada vez que conecta su billetera.

Sesión Sam no tiene este problema. Después de autenticarse con dapps y establecer una sesión, esta información se guarda. Incluso si Sam se desconecta y se autentica nuevamente, Sam continúa desde donde lo dejó y todavía recuerda todo sobre él en la aplicación. Su información puede incluso guardarse en una bóveda de datos remota que él controla.

Unificación del inicio de sesión con Ethereum

En Web3, encontrará muchos servicios existentes que ofrecen alguna forma de "Iniciar sesión con Ethereum", pero no muchos de manera estándar. Por lo general, usarán esto para establecer una sesión basada en cookies con un usuario que puede administrar metadatos privilegiados sobre la cuenta. Por ejemplo, si desea brindarles a los usuarios la capacidad de personalizar sus propios perfiles en su sitio web (como lo hace OpenSea), debe autenticar al usuario antes de que pueda realizar cambios, asegurándose de que solo el usuario pueda editar su propio perfil. El flujo de trabajo para esto se parece a lo siguiente:

El primer paso después de conectar una billetera es dar a los usuarios un mensaje legible para que puedan entender en qué se están metiendo. Ha habido muchos casos en los que a los usuarios se les presenta "INICIAR SESIÓN", algunas frases inconsistentes sobre "iniciar sesión" o incluso, a veces, solo un número arbitrario ("aquí, firme este loco conjunto aleatorio de letras y números"). En su lugar, podemos definir un conjunto de campos obligatorios en función de las prácticas existentes, una serie de buenas medidas de seguridad y una gramática rígida que logra el equilibrio entre legible por humanos y seguro. Además, las billeteras no tendrían que cambiar sus interfaces y prácticas existentes para al menos continuar brindando a los usuarios este tipo de mensaje.

Primero podemos tomar todos estos mensajes confusos de 'Iniciar sesión con Ethereum' y tener una forma común aceptada de presentar a los usuarios la solicitud:

Mensaje común - Interfaz común

Con un formato de mensaje de firma acordado, las aplicaciones y las billeteras ahora pueden hablar el mismo idioma. A medida que la aplicación presenta al usuario una solicitud de firma, la billetera puede verificar la solicitud, verificar si encajaría como un mensaje EIP-4361 e informar al usuario que está iniciando sesión en un sitio web.

En este punto, en lugar de presentarle al usuario un bloque de texto arbitrario para que lo firme, la billetera puede presentar una interfaz estilizada y amigable que se siente bien y elimina cualquier duda sobre la acción que el usuario está a punto de realizar. El usuario ahora puede simplemente "Iniciar sesión" haciendo clic en un cuadro de diálogo de confirmación porque la billetera entiende la solicitud de firma. Para una transparencia total, la especificación establece que el mensaje completo y los campos aún deben estar disponibles en subinterfaces adicionales (como una vista detallada).

Del mensaje EIP-4361, ahora obtenemos una interfaz más limpia:

La especificación también introduce requisitos de seguridad adicionales para las billeteras, como el enlace de dominio para evitar ataques de phishing y nonces para evitar ataques de repetición, el usuario está más protegido durante toda la experiencia. Por ejemplo, si la billetera encuentra un mensaje SIWE válido pero el usuario está firmando example.compero en realidad está activado exampie.com, la billetera puede advertir al usuario sobre la situación:

La mayoría de las dapps actuales no pueden mejorar la experiencia del usuario con preferencias guardadas (modo claro u oscuro), borradores de contenido creado por el usuario o cualquier cosa mejor que no esté almacenada en una cadena de bloques pública para privacidad, escalabilidad o control de acceso. Estas son apuestas en la mesa para proporcionar una excelente UX que se encuentra con frecuencia en web2.

Afortunadamente, Sign-In with Ethereum ha estandarizado la forma en que los usuarios de Ethereum pueden disfrutar de todas estas funciones mediante el inicio de sesión seguro en los servicios con sus billeteras existentes, todo sin una contraseña que recordar. Una vez que los usuarios inician sesión en dapps, pueden disfrutar de ricas interacciones contextuales con UX que rivaliza con las aplicaciones web2. Dapps ahora puede recordar a un usuario como algo más que una clave pública y traer perfiles, credenciales, preferencias y más a una sesión después de que los usuarios se autentiquen.

Monederos, pero alimentados por SIWE

Usando estas nuevas capacidades, ahora es posible cerrar la brecha entre web2 y web3, con el inicio de sesión con Ethereum como el primer punto de entrada importante sobre cómo los servicios descentralizados tratan respetuosamente los datos de los usuarios. Con eso, estas son algunas de las ventajas para que las organizaciones web2 adopten el inicio de sesión con Ethereum.

Iniciar sesión con Ethereum es gratis

Una de las formas más simples y de bajo riesgo en que las empresas de web2 pueden participar de manera significativa en el ecosistema web3 es a través del inicio de sesión con Ethereum. El inicio de sesión con Ethereum sirve como una ruta de integración para los componentes web3 que no compromete la experiencia del usuario ni la relevancia en toda la base de usuarios. Es seguro, no afecta a los usuarios existentes que no están interesados ​​en web3 y proporciona una importante investigación de usuarios que informa directamente una estrategia de web3 para cualquier empresa de web2.

Para ilustrar mejor este punto, podemos reducirlo a un mensaje simple:

Montaje mínimo requerido

SIWE no cuesta nada a los usuarios finales porque solo están firmando un mensaje. No se requiere una transacción de blockchain y no se debe pagar gas a los mineros. SIWE es solo un usuario que confirma que está iniciando sesión y firmando para hacerlo.

Con el inicio de sesión con Ethereum, también puede permitir que sus usuarios existentes asocien sus cuentas de Ethereum y usar eso como su medio de autenticación. En este caso, el usuario recibe una nueva forma de inicio de sesión sin contraseña y puede cambiar a un identificador que controla en el proceso. Además de todo esto, los usuarios existentes que no están familiarizados con web3 no deben preocuparse porque nada de lo que ya hacen se ve afectado por esta integración. Pueden continuar usando la aplicación o el servicio como se pretendía originalmente, sin que nada cambie la experiencia del usuario principal.

Iniciar sesión con Ethereum simplemente agrega una opción de inicio de sesión adicional y ofrece a los usuarios un poco más de flexibilidad sobre cómo interactúan con usted. Como empresa de web2, tendrá la oportunidad de ser el primer punto de contacto de un usuario en web3 y ayudarlo en su viaje para controlar su propia identidad digital. Naturalmente, esto puede conducir a una mayor confianza de los usuarios y al crecimiento de su marca.

Iniciar sesión con Ethereum potencia a los usuarios

Además, cuando los usuarios inician sesión con Ethereum, pueden introducir voluntariamente un poco más sobre ellos mismos en su servicio, según la cuenta con la que eligieron iniciar sesión. Potencialmente, esto puede venir con un historial en cadena (o no) que puede permitirle ofrecer beneficios adicionales basados ​​en esos hechos. Esto da lugar a una nueva forma de usuario avanzado que tiene un historial en un servicio existente y nuevos intereses indicados por la actividad en la cadena.

Es importante enfatizar el hecho de que esto depende de si el usuario decide o no compartir ese historial en particular con un servicio, y alentamos a los servicios a informar a los usuarios sobre este hecho. Siempre debe ser completamente opcional, y los servicios deben informar a los usuarios que pueden usar cuentas nuevas y frescas si lo desean.

Los usuarios siempre pueden generar nuevas cuentas para iniciar sesión o iniciar sesión con cuentas existentes con actividad en la cadena.

Si no has conocido a Session Sam, mira la publicación original

Cuando los usuarios pueden iniciar sesión con sus billeteras o vincularlas a cuentas existentes, pueden compartir más con su organización en sus términos, y esto puede agregar mucho valor para ambas partes. Por ejemplo, una empresa puede crear experiencias cerradas en función de las participaciones de NFT, la membresía de DAO o incluso la actividad de DeFi, según lo que deseen incentivar o los programas que deseen crear. Por ejemplo,

  • Los fanáticos veteranos de un artista que tiene sus NFT pueden comprar boletos para conciertos con descuento anticipado antes de que los revendedores invadan las ventas.
  • Se puede poner a disposición un paquete de alojamiento de servidor especial para los miembros de una DAO centrada en los desarrolladores.
  • Los usuarios avanzados de intercambios descentralizados pueden recibir funciones premium en una empresa de servicios financieros, como apalancamiento, crédito o un administrador de cuenta dedicado.

Esto también abre la puerta para que las empresas creen nuevas experiencias web3 para sus usuarios, como una distribución NFT o POAP a cuentas existentes que pueden haber iniciado sesión mediante Iniciar sesión con Ethereum.

Además de brindar beneficios directos a los usuarios, las organizaciones también pueden usar SIWE para orquestar efectos de red en todas sus propiedades digitales, eliminando las barreras de interoperabilidad entre departamentos y socios externos. Por ejemplo, el talón de un boleto de un evento deportivo se puede representar como un NFT y, debido a la importancia del evento (p. ej., la victoria del campeonato), ese mismo NFT permite a los usuarios usar un artículo cosmético virtual en un videojuego AAA.

Iniciar sesión con Ethereum equivale a nuevos usuarios

Finalmente, como campeón de la identidad y los datos controlados por el usuario, ahora puede presentar su producto a millones de usuarios entusiasmados por usar servicios que se preocupan por ellos: usuarios web3 .

No se trata solo de agregar una función para su base de usuarios existente, sino de presentar su servicio a millones de usuarios que se preocupan por las decisiones que toma en web3. Estos usuarios viven según el espíritu de web3, son criptonativos y están dispuestos a probar al menos cualquier servicio que aumente su autonomía.

Al presentar el inicio de sesión con Ethereum, está demostrando que es una organización web3forward y está dando los primeros pasos para demostrar que le importa.

Gracias a EIP-4361, tenemos una forma estándar de autenticar a los usuarios usando sus cuentas de Ethereum. Esto no solo trae la funcionalidad de web3 a web2, sino que también presenta a los usuarios de web3 organizaciones tradicionales amigables con web3. 

Esta historia se publicó originalmente en https://blog.spruceid.com/

#ethereum #web3 #blockchain #token 

Por Qué iniciar Sesión Con Ethereum Cambia Las Reglas Del Juego