'링크모음 > BlockChain News' 카테고리의 다른 글
[GETH] GETH 명령어 모음 (0) | 2018.05.10 |
---|---|
크립토 키티 소스 (0) | 2018.04.30 |
SMT 백서 (0) | 2018.04.12 |
블록체인 관련 참조 사이트 모음 (0) | 2018.02.12 |
[GETH] GETH 명령어 모음 (0) | 2018.05.10 |
---|---|
크립토 키티 소스 (0) | 2018.04.30 |
SMT 백서 (0) | 2018.04.12 |
블록체인 관련 참조 사이트 모음 (0) | 2018.02.12 |
Document Metadata
• Creators: @ned; @theoretical
• Developers: @theoretical; @vandeberg; @itwasntme; @zgredek; @pychol-mychol;
@small.minion; @youkaicountry; @picokernel
• Contributors:
@sneak; @vandeberg; @valzav; @youkaicountry; @justinw;
@goldibex; et al.
• Sketch designs: @pkattera
• Copyright (c) Steemit, Inc. 2017
• GitHub: https://github.com/steemit/smt-whitepaper/blob/master/smt-manual/
manual.md
Smart Media Tokens (SMTs)
A Token Protocol for Content Websites, Applications, Online
Communities and Guilds Seeking Funding, Monetization and
User Growth
Steem’s Smart Media Tokens (SMTs) give anyone the power to launch and sell Proof-ofBrain [1] tokens, which are tokens distributed by “upvote” and “like”-based algorithms
and can be integrated with websites to align incentives and spur growth, while websites are
empowered to adopt sustainable, currency-centric revenue models. This model has been
tested and continues to be proven by steemit.com, busy.org, chainbb.com, dsound.audio,
dtube.video and other Steem interfaces, which are monetizing content, tokens and media
in a way never before seen.
Several popular token protocols, such as Ethereum’s ERC-20, allow you to create and
launch arbitrary tokens, but no protocol enables content businesses to leverage those tokens by aligning incentives between users and applications. Due to suboptimal transaction
cost structures that incur fees for basic actions such as voting or posting, misalignment
of interests between meta and core tokens that aren’t built for influencing distributions
based on Proof-of-Brain, private key hierarchies that don’t cater to social versus financial
operations, and slow transaction speeds that are out of sync with real-time websites - none
of these protocols could ever provide an acceptable user experience for content websites,
such as Twitter, Reddit (even subreddits) or The New York Times.
For content websites and tokens, incentive alignment between websites and users comes
from a steady, as well as decentralized and mathematically guaranteed, release of new
tokens, and incentives that must be allocated to the users - including bloggers, vloggers,
commenters and curators. The distribution of new tokens occurs based on stake-weighted
voting to prevent gaming and eliminate the need for a counterparty. Quality user experience comes from tokens that can be transacted safely (through separate private keys for
distinct sets of actions), without fees, and at real-time speeds. Further incentive alignment comes from a company’s ability to raise capital in ICOs. All Smart Media Tokens
have built-in ICO support, should the issuer wish to launch one.
1
Table of Contents
Document Metadata
1
Smart Media Tokens (SMTs)
A Token Protocol for Content Websites, Applications, Online Communities and
Guilds Seeking Funding, Monetization and User Growth . . . . . . . . . .
1
1
Introduction
Leveraging Tokens for Autonomous User Growth . . . . . .
New Fundraising Opportunities . . . . . . . . . . . . . . . .
Immediate Liquidity . . . . . . . . . . . . . . . . . . . . . .
Shared Bootstrap Tools . . . . . . . . . . . . . . . . . . . .
Monetizing with Shared Token Rewards . . . . . . . . . . .
Can My Entity Participate in SMTs? . . . . . . . . . . . . .
Use Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 - Content Publishers - Single Token Support . . . . .
2 - Forums - Multiple Token Support . . . . . . . . . .
3 - Comments Widget for Online Publishers . . . . . .
4 - Sub-Community Moderators and Managers . . . .
5 - Arbitrary Assets - Tokens Representing Real World
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
Assets
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
6
6
6
6
7
7
7
8
9
10
11
Owner’s manual
Create a control account . . . . . . . . . . . .
Control account security . . . . . . . . .
Token consensus . . . . . . . . . . . . .
Token Generation and Initialized Parameters
SMT object creation . . . . . . . . . . .
SMT pre-setup . . . . . . . . . . . . . .
SMT setup . . . . . . . . . . . . . . . .
Token units . . . . . . . . . . . . . . . .
Unit ratios . . . . . . . . . . . . . . . .
Cap and min . . . . . . . . . . . . . . .
Hidden caps . . . . . . . . . . . . . . . .
Generation policy data structure . . . .
Examples and rationale . . . . . . . . .
Launch . . . . . . . . . . . . . . . . . .
Full JSON examples . . . . . . . . . . .
Inflation Parameters . . . . . . . . . . .
Named token parameters . . . . . . . .
Parameter constraints . . . . . . . . . . . . .
SMT vesting semantics . . . . . . . . . . . . .
Content rewards . . . . . . . . . . . . . . . .
Curve definitions . . . . . . . . . . . . . . . .
Target votes per day . . . . . . . . . . . . . .
SMT Setup GUI Sketch . . . . . . . . . . . .
Votability and Rewardability . . . . . . . . .
Static Token Parameters . . . . . . . . . . . .
Mandatory token parameters . . . . . . . . .
SMT interaction with existing operations . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
12
12
13
13
13
13
14
15
16
16
16
17
18
18
20
21
29
33
34
34
35
35
38
39
40
41
42
42
2
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Automated Market Makers for SMTs
Setup . . . . . . . . . . . . . . . . . . .
Basic Definitions . . . . . . . . . .
Notes on Conventions . . . . . . .
Finite Trades . . . . . . . . . . . . . . .
Basic Definitions . . . . . . . . . .
Computing the Restoring Trade . .
Computing the Equilibrium Price .
Example . . . . . . . . . . . . . . .
Infinitesimal Trades . . . . . . . . . . .
Setting up the Problem . . . . . .
Solving the DE’s . . . . . . . . . .
Qualitative discussion . . . . . . . . . .
FAQ . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
43
43
43
43
44
44
44
44
45
46
46
46
47
47
Costs of SMT Operations And Bandwidth Rate Limiting
Fee-less Operations Necessary for Quality User Experience . . . . . . . . . . . .
50
51
Decentralized Exchange
Automatic Order Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Diverse Asset Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Zero Trading and Transfer Fees . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
51
51
52
Augmenting SMTs with Additional Native Contracts
Community Building with Paid Positions . . . . . . . . . . . . .
Democratic SMTs using Whitelist Oracles . . . . . . . . . . . . .
Secondary ICOs for Contiguous Fundraising . . . . . . . . . . . .
Bandwidth Sharing with SMTs Based on Reserve Liquidity Pools
52
52
53
53
53
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
What Makes SMTs Better Suited to Application-Specific Blockchains, such
as Steem, than Application-General Blockchains, such as Ethereum?
SMTs are Safer and More Cost Effective in Application-Specific Blockchain Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
SMTs on Steem have Aligned Proof-of-Brain Incentives with the Core Token .
SMTs on Steem Have Transaction Pricing that Contributes to a Quality User
Experience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
SMTs Benefit from a Blockchain that has Scaling Processes Programmed to a
Specialized Set of Applications . . . . . . . . . . . . . . . . . . . . . . . .
SMTs Benefit from a Blockchain with Content Management System (CMS)
Primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Increasing Market Demand for STEEM with SMTs and Implicit Value
Drivers rather than Fees
STEEM Purchased for Transaction Bandwidth Enables Maximally Profitable
Participation across SMTs . . . . . . . . . . . . . . . . . . . . . . . . . . .
STEEM Supply is Locked into Liquidity Pools by Automated Market Makers .
STEEM and SMT Demand Increases with Advent of New Powers of Influence .
STEEM Demand Increases with Proliferation of SMT ICOs . . . . . . . . . . .
Steem: The World’s Advertising Network . . . . . . . . . . . . . . . . . . . . .
Steem Ecosystem Support for SMTs
Integrating SMTs into Websites and Apps . . . . . . . . . . . . . . . . . . . . .
3
53
54
54
55
55
56
56
56
56
57
57
57
58
58
APIs and Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . .
Shared Tools for Account Creation, Key Signing, and Wallet Functions . .
58
58
Conclusion
58
References
58
Appendix
Implementation Notes . . . . . . . . . . . . . .
SMT naming standards . . . . . . . . . .
Asset directory standards . . . . . . . . .
UI guidelines for SMT names . . . . . . .
Operational guidelines for asset directories
Asset directory formats . . . . . . . . . .
Unit Tests . . . . . . . . . . . . . . . . . . . . .
4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
58
58
59
59
60
60
60
61
Introduction
Smart Media Tokens (SMTs) is a proposal to build a consensus-level token issuance protocol on the Steem blockchain. Inspired by the revolutionary properties of the STEEM
token, including automatic distributions to content creators, SMTs will be an upgrade
beyond previously created blockchain token issuance protocols due to carefully designed
token sale programmability, automated liquidity providers, decentralized token markets
and dynamic token distribution parameters, as well as a large ecosystem of tools (open
source wallets, shared key signing tools, etc.) for integrations at website and application
layers.
SMTs are an evolution of the successful relationship established between STEEM and
the social websites sitting atop of it, such as steemit.com, which has grown to be a top
2100 website in Alexa rankings in less than one year, solely from integrating the incentive
model of STEEM. With SMTs, any website or content library across the internet may have
one or more tokens integrated into its interface to facilitate fundraising and autonomous
growth.
These tokens are designed to allow website operators flexibility during the integration of
the token into their community by choosing from many parameters that may be structured
creatively at outset or refined over time. Any tokens launched as SMTs shall benefit from a
blockchain ecosystem built with an inbuilt decentralized exchange, as well as an ecosystem
of open-source applications and libraries to support successful deployment, fundraising,
and growth.
Leveraging Tokens for Autonomous User Growth
SMTs are a breakthrough for bridging the world’s content applications to tokens in a way
that aligns incentives between the users of a network and the entrepreneurs building the
applications. By leveraging the concepts of inflation (new token emissions) and token allocations by post-based voting, SMTs exist in a manner where value must be distributed
to users who are participating in their related content networks and applications. Entrepreneurs may now create tokens to integrate with their blog, application, or an entire
network of applications and topics. With SMTs, the entrepreneurs have the flexibility
to decide on the economics of the tokens they integrate into their products, from the
inflation rates to the algorithms that distribute the tokens.
Two unique properties align incentives and make SMTs “smart and social” compared to
other tokens (such as bitcoin, ether and ERC-20s). The first is a pool of tokens dedicated
to incentivizing content creation and curation (called the “rewards pool”). The second
is a voting system that leverages the wisdom of the crowd to assess the value of content
and distribute tokens to it. These two unique properties when combined are referred
to as Proof-of-Brain, which is an entendre based on Proof-of-Work, meant to emphasize
the human work required to distribute tokens to community participants. Proof-of-Brain
positions SMTs as a tool for building perpetually growing communities, which encourage
their members to add value to the community through the built in rewards structure.
Entrepreneurs and established entities may rely on SMTs to grow their content network
because of the automated and continuous generation of new tokens that are allocated
to producers of content by the holders of the existing tokens, through the process of
competitive voting. As the tokens are distributed to users of the network, the interests of
5
existing token holders are further aligned with content creators, the businesses running
the applications, and the entrepreneurs that support them. These unique properties of
the tokens’ economics continue to provide incentives for new users to join and participate
in growing the network. Any application, whether it is an existing publisher behemoth or
a stealth-mode social media startup, will be able to integrate and leverage these special
tokens for their own growth.
New Fundraising Opportunities
Blockchain-based tokens, beginning strongly with the advent of ERC20 on Ethereum,
represent a new manner of bringing capital into an organization through the process of
Initial Coin Offerings (ICOs). ICOs are an opportunity for one group to sell an initial
supply of tokens, privately or publicly, for-specific-purpose, for-profit or not-for-profit.
Depending on how these tokens are sold, different regulatory bodies could see them as
commodities, securities, derivatives, or as none of the above. Regardless, it is clear we
have seen north of one billion dollars (USD) raised through ICOs in 2017, and to support
this trend, it is possible to conveniently launch and sell tokens via the built in ICO contract
of SMTs. The launch of SMTs can be structured for ICOs with hard, soft, and no caps,
and can be tailored to receive STEEM and cryptocurrencies on other blockchains.
Immediate Liquidity
By leveraging a recently designed automated market maker concept [2], SMT-based ICOs
allow a portion of STEEM tokens received to be sent into an SMT’s on-chain, off-orderbook market maker in order to provide liquidity to the SMT at a specified reserve ratio.
Beyond the social and specialized distribution mechanisms of SMTs, this feature advances
the concept of automated market makers by pairing it alongside SMT’s decentralized
markets, which also facilitate bids and asks by human participants. The combination
of these two markets enables on-chain and trustless exchange opportunities for market
makers while enabling liquidity for token users.
Shared Bootstrap Tools
SMTs may be created with reward pool parameters tuned for “Shared Influence” between
Steem Power and other vesting SMTs, which means a SMT creator may specify that
Steem Power can control a portion of the SMT’s rewards pool for an unlimited or limited
amount of time, with increasing or decreasing influence. Altogether, Shared Influence may
allow SMTs to be wholly or partially bootstrapped by the interest of existing and active
Steem or other SMT community members. Through these tools, community managers
and entrepreneurs launching a token may leverage existing user bases to accelerate the
distribution of the SMT to a target market.
Monetizing with Shared Token Rewards
All Steem based interfaces have the option of splitting token rewards among a set of
arbitrary recipients, which could include an interface, community manager, referrer, a
paid position donation pool, and more. An interface can also provide this optionality
6
of how to split the tokens to the authors. The number of potential Reward Sharing
beneficiaries is initially soft capped by block producers at eight while the feature proves
its use, however the blockchain is capable of handling up to 256 beneficiaries per post.
Can My Entity Participate in SMTs?
An SMT can be launched by a person or entity; they only need 1 USD to cover the network
fee (this fee prevents spam and unused tokens while accruing value to the network), and
a namespace on Steem - which can be obtained by registering at anon.steem.network,
steemit.com, steemconnect.com, or any other Steem sign-up service.
Once an account name to register the token with is secured, the account issues the token
by using a Steem-based command line tool or any tool created in the future to support
token launches. The token can be structured to support an initial sale or distribution of
the token. Certain properties of an SMT, such as its inflation rate, must also be defined
by the person or entity creating the token. These properties dictate how the token is used
inside applications and respective communities.
From launch, the token becomes immutable on the blockchain, and leveraged correctly,
the token can have dramatic effects on the growth of businesses that choose to integrate
these tokens.
Use Cases
We have identified five ways in which existing businesses and future entrepreneurs can
leverage specially designed SMTs to transform the internet. Among these use cases you
may discover other ways of structuring and leveraging tokens inside applications. This list
is by no means exhaustive, and we will update this paper as more use cases demonstrate
their value.
1 - Content Publishers - Single Token Support
A mainstream media website’s growth has been slowing and they are looking for ways
to get ahead of the changing tech landscape. The website migrates to a Disqus-like
application based on Steem, or taps directly into Steem APIs for a custom integration.
Now their subscribers can be rewarded with cryptocurrency while commenting. When
the website is ready, they can issue their own token through the comments interface - the
token will allow them to 1) raise capital by selling tokens 2) catalyze autonomous growth.
7
Figure 1: Single Token Content Publishers
2 - Forums - Multiple Token Support
An up-and-coming forum business is looking to integrate cryptocurrency to create cash
flow and spark growth to get the business to the next level, however they are not cryptocurrency security experts and would prefer not to host a cryptocurrency wallet. They
issue an SMT and integrate it into their website. Focusing solely on the social aspects, the
forum business can integrate other applications, such as SteemConnect into their forum
to handle the wallet and transfer capabilities. This allows them to focus on their business
(growing communities) without focusing on the security aspects of cryptocurrency. The
forum enables additional tokens to be exposed or launched, to represent specific topics
of discussion. The ability to launch these tokens can be retained by the company behind
the website, or granted to the website’s community managers. Tokens dedicated to the
website’s specific topics will further spur autonomous growth of the website niche by niche.
An example of this multi-token model could eventually be found in organizations such as
ChainBB (chainbb.com) if it were to enable its own globally available token on its domain,
8
as well as narrowly available tokens for specific community niches - such as “gardening.”
Figure 2: Multiple tokens Forum
3 - Comments Widget for Online Publishers
One of the ways in which publishers will be onboarded faster to SMT integrations is by
offering a Steem-based comments widget that can easily be integrated into existing blogs
that are built on software such as WordPress and Blogger. The developer employing
the widget would be able to take a percentage of the tokens (called “Shared Rewards”)
distributed to the commenters for themselves, thereby creating a business opportunity for
the next generation of Disqus-like companies that are cryptocurrency enabled. It would
alleviate the burdens of transaction signing support, private key management, wallet
functionality, and hosting costs for the publisher - by outsourcing all of these functions
to the comments widget maintainer.
9
Figure 3: Comment Widget
4 - Sub-Community Moderators and Managers
Imagine you are a moderator for a specific topic inside a forum, such as a Reddit “subreddit” or a Steemit “community”. If a website integrates SMTs for these specific topics,
then the topic moderator/s can launch these tokens to empower the subscribers of their
topic, raise funds, and increase the quality of content curation for the community.
10
Figure 4: Sub-community
5 - Arbitrary Assets - Tokens Representing Real World Assets
Let’s examine an instance in which an entrepreneur is looking to provide liquidity in the
Steem ecosystem. The entrepreneur can issue an SMT without inflation properties, and
imply that they will provide structure to peg it to USD (or any other debt, contract, or
asset), making it like an IOU or basic derivative. The structure they provide to the asset
includes buying and selling it near $1, similar to Tether. The entrepreneur sets up bank
wire capabilities for buying and selling, and takes a small % on each transaction. The
derivative trades against STEEM, and also brings capital into the ecosystem to be used
across all tokens.
11
Figure 5: IOU Asset Token Exchange
Owner’s manual
This manual will explain the nuts and bolts of how SMTs work. The intended audience
is technical users who want to create their own SMT.
Create a control account
The first step to creating an SMT is to create a control account for the SMT. Any STEEM
account may serve as a control account, however it is highly recommended to create a
dedicated account solely for the purpose. It is also highly recommended that a control
account does not post, vote, or hold any STEEM, SBD, or other tokens (other than a
small amount of vested STEEM for transaction bandwidth).
12
The control account’s name will not occupy a high visibility position in most user interfaces, so it does not much matter if the control account’s name is not the best match for
the SMT brand.
Control account security
Security on the control account is important for persons who plan to use the account post
launch:
• The control account should use 2-of-3 or 3-of-5 multi-signature security.
• The control account’s authorities should have other accounts, not specific keys, as
multi-signature members.
• For additional security, each of the accounts in the control account’s multi-signature
group should itself use multi-signature security.
• A subset of keys should be kept offline, in air-gapped machines.
• Transactions should be generated by an online interface, and physically transferred
to the air-gapped machines via removable media.
• Signatures should be returned via physically removable media to the online system
for transmission via the UI.
Of course, once authorities are set up, you should verify the account is still able to transact.
It is advisable to test your authorities and transaction signing setup using a testnet, or a
less-important account on the main network.
Once the token is launched, you may consider burning the account’s keys by assigning
them to @null, initiating a token for which the dynamic properties can never be adjusted.
Token consensus
Since tokens participate in atomic transactions also involving STEEM, they have been
designed as part of the STEEM blockchain’s consensus.
Token Generation and Initialized Parameters
SMT object creation
The first operation to be executed is an smt_create_operation. This operation creates
an SMT object in the blockchain state. After executing the smt_create_operation, the
newly created SMT object is not yet fully configured.
Most of the configuration occurs in subsequent operations (smt_set_setup_parameters_operation,
smt_setup_inflation_operation and smt_setup_operation). These later operations
may occur in the same transaction, but they may also occur at any later point in time.
struct smt_create_operation
{
account_name_type control_account;
asset
smt_creation_fee;
asset_symbol_type symbol;
extensions_type
extensions;
};
13
Numerical asset identifiers
An SMT is referred to by a numerical asset identifier or NAI, consisting of two at-signs
followed by nine decimal digits, for example @@314159265. The blockchain enforces that
the identifier placed by a UI into the smt_create_operation must match the result of
the get_next_smt_identifier RPC. Therefore, an NAI cannot be chosen freely by the
SMT creator. It is not even possible to “mine” a “vanity NAI” (analogous to the “vanity
Bitcoin address” some people use).
The reason for this restriction is that the blockchain designers want to discourage users
from using the consensus level identifiers as symbol names, and instead use a nonconsensus directory system to attach human meaningful symbols to assets. Distinguishing
a “namesquatter” from the legitimate owner of a brand is not something that a blockchain
can do, especially if the squatter is willing to pay the SMT creation fee.
SMT naming
The solution to the namesquatting problem is to publish an asset directory mapping NAIs
to names. An asset directory is non-consensus, meaning that all blockchain operations
are serialized only with NAIs. Asset names are only used for UI presentation.
A UI may include an asset directory as a file, URL, or a blockchain account which publishes
directory entries with custom operations. The publisher of an asset directory should
ensure that directory entries meet whatever standards of legitimate brand ownership the
publisher chooses to enforce.
SMT creation fee
Issuing a smt_create_operation requires payment of smt_creation_fee. The amount
required is set by the smt_creation_fee field of dynamic_global_properties_object.
This field may contain a value in STEEM or SBD. If specified in SBD, an equivalent
amount of STEEM will be accepted, at the current price feed.
Initially, smt_creation_fee will be set to 1 SBD, and no means will be provided
to update it. Updates to the smt_creation_fee amount may occur in future hardforks, however, so user-agents should read the smt_creation_fee value from the
dynamic_global_properties_object. User-agents should not assume the fee will
always be 1 SBD and they should be prepared to charge a separate fee paid to the
user-agent if the aim of the interface is to enable only a curated set of tokens.
The fee is destroyed by sending it to STEEM_NULL_ACCOUNT.
SMT pre-setup
Two pre-setup operations are included:
smt_setup_inflation_operation and
smt_setup_parameters. These operations must be issued after smt_create_operation,
and before smt_setup_operation. They may be issued in the same transaction, or in
prior blocks.
The reason pre-setup operations are not made a part of smt_setup_operation is to allow
a large number of pre-setup operations to be executed over multiple blocks.
14
SMT setup
Each SMT has an associated descriptor object which has permanent configuration
data. This data cannot be changed after launch! The descriptor is set by the
smt_setup_operation:
struct smt_setup_operation
{
account_name_type
control_account;
asset_symbol_type
smt_name;
int64_t
max_supply = STEEM_MAX_SHARE_SUPPLY;
smt_generation_policy
initial_generation_policy;
time_point_sec
time_point_sec
time_point_sec
time_point_sec
generation_begin_time;
generation_end_time;
announced_launch_time;
launch_expiration_time;
extensions_type
extensions;
};
The symbol precision in smt_setup_operation is authoritative. It may differ from, and
will override, any previously specified operations’ precision. Subsequently issued operations must have matching precision.
The operation must be signed by the control_account key. The named SMT must have
been created earlier by the control_account. The symbol’s embedded decimal places
may be distinct from prior smt_setup_operation.
The decimal_places field is used by UIs to display units as a number of decimals.
The generation_begin_time is when participants can begin to contribute to the ICO.
It is allowed to be in the future so users have time to study the ICO’s final terms before
the ICO begins.
The generation_end_time is when the ICO stops accepting contributions, and
the announced_launch_time is when the ICO token is created (assuming the ICO
reached the minimum participation level). Some pause is allocated between the
generation_end_time and announced_launch_time to allow for the possibility of
ICOs that wish to have hidden caps that aren’t revealed while the ICO is open for
contributions. It also gives the ICO creator time to use the final ICO numbers to aid in
pre-launch business activities.
At launch_expiration_time, if the ICO has not yet launched, all contributors will be
automatically refunded (with virtual operations) and the ICO will be cancelled. The
symbol will remain reserved to the specified control_account. However, in order to
launch the token, an smt_create_operation must be issued and the smt_creation_fee
must be paid again.
15
Token units
Initial token generation is driven by a contributions of STEEM units from contributors.
To simplify rounding concerns, a contribution must be an integer number of STEEM
units. The ICO creator sets the size of a STEEM unit - it can be large or small. It is
better to keep the unit small (for example, 1 STEEM or 0.1 STEEM), as this allows the
ICO to be accessible to the maximum possible audience.
A STEEM unit also specifies a routing policy which determines where the STEEM goes
when the token launches. (STEEM for tokens which do not launch may be refunded on
demand.) The routing policy may split the STEEM in the unit among multiple parties.
When the ICO occurs, the tokens are generated in token units. Multiple token units are
generated per STEEM unit contributed. Token units also have a routing policy.
The units and their routing policies are specified in the smt_generation_unit structure:
struct smt_generation_unit
{
flat_map< account_name_type, uint16_t >
flat_map< account_name_type, uint16_t >
};
steem_unit;
token_unit;
Each (key, value) pair in the flat_map determines the routing of some satoshis. The
total STEEM/tokens in each unit is simply the sum of the values.
Unit ratios
When an SMT launches, token units are created for STEEM units in a R-for-1 ratio. The number R is called the unit ratio. Maximum and minimum allowable values
for R are specified respectively in the min_unit_ratio and max_unit_ratio fields of
smt_generation_policy.
The maximum number of token units that can be created in the ICO is limited to
max_token_units_generated, a parameter which is set by the ICO creator. (More tokens can be created after the token has launched, but this later creation is called inflation
and is not considered to be part of the ICO.)
The unit ratio is set to the largest integer that would not result in exceeding
max_token_units_generated for the number of STEEM units actually contributed.
Cap and min
ICOs may specify a minimum number of STEEM units min_steem_units. If the ICO
does not reach min_steem_units before generation_end_time, then it does not occur,
and contributors become eligible for refunds.
Likewise, ICOs may specify two maximum numbers of STEEM units: A hard cap and
a soft cap. Units in excess of the soft cap have different routing for their STEEM and
tokens. STEEM units in excess of the hard cap are rejected and do not generate any
SMTs.
16
The effects of the soft cap are divided proportionally among all contributors. I.e. if a
ICO has a soft cap of 8 million STEEM, and 10 contributors each contribute 1 million
STEEM, then 0.2 million of each user’s STEEM is routed via the soft cap’s policy.
The effects of the hard cap fall solely on the last contributors. I.e. if a ICO has a hard
cap of 8 million STEEM, and 10 contributors each contribute 1 million STEEM, then
the first 8 users fully participate in the ICO, and the last 2 users are refunded 1 million
STEEM.
Hidden caps
The min and hard caps are hidden in the generation policy. This means that these
numbers are fixed at setup time, but the ICO creator has the option to keep them secret.
This functionality is implemented by a commit/reveal cryptographic protocol: A hash
called the commitment is published at setup time, and the actual amount must match
the commitment. (A nonce is also included in the hash to prevent an attacker from finding
the hidden cap with a brute-force guess-and-test approach.)
The SMT designer may wish to pre-publish a guarantee that the hidden values are within
a certain range. The lower_bound and upper_bound fields provide this functionality: A
revealed amount that is not in the specified range is treated the same as a hash mismatch.
struct smt_cap_commitment
{
share_type
lower_bound;
share_type
upper_bound;
digest_type
hash;
};
struct smt_revealed_cap
{
share_type
uint128_t
};
amount;
nonce;
struct smt_cap_reveal_operation
{
account_name_type
control_account;
smt_revealed_cap
cap;
extensions_type
extensions;
};
All caps are hidden, but the cap may be revealed at any point in time. Therefore, an
ICO with a non-hidden minimum or cap may be implemented by simply including the
smt_cap_reveal_operation in the same transaction as the smt_setup_operation. UIs
should provide functionality for this.
A UI should provide one or more of the following means to ensure the nonce and amount
are recoverable:
• Force the user to type in the amount and nonce again, as confirmation they have
been backed up.
17
• Set nonce to some deterministic function of the private key and public data, for example nonce = H(privkey + control_account + lower_bound + upper_bound
+ current_date).
• Provide functionality to brute-force the uncertain fields when the nonce is known
(e.g. the current date and amount).
• Require the amount to be low-entropy to facilitate brute-forcing when the nonce is
known (e.g. a number between 1-999 times a power of 10).
Generation policy data structure
The SMT generation policy data structure looks like this:
struct smt_capped_generation_policy
{
smt_generation_unit pre_soft_cap_unit;
smt_generation_unit post_soft_cap_unit;
smt_cap_commitment
smt_cap_commitment
min_steem_units_commitment;
hard_cap_steem_units_commitment;
uint16_t
soft_cap_percent = 0;
uint32_t
uint32_t
min_unit_ratio = 0;
max_unit_ratio = 0;
extensions_type
extensions;
};
Note, the max_token_units_generated parameter does not appear anywhere in the operation. The reason is that it is actually a derived parameter: max_token_units_generated
= min_unit_ratio * hard_cap_steem_units.
Additionally, the smt_generation_policy is defined as a static_variant, of which
smt_capped_generation_policy is the only member:
typedef static_variant< smt_capped_generation_policy > smt_generation_policy;
This typedef allows the potential for future protocol versions to allow additional generation policy semantics with different parameters.
Examples and rationale
Example ICO
ALPHA wants to sell a token to the crowd to raise funds where: 70% of contributed
STEEM goes to the Alpha Organization Account (@alpha_org), 23% of contributed
STEEM goes to Founder Account A (@founder_a), and 7% of contributed STEEM goes
to Founder Account B (@founder_b).
ALPHA defines a STEEM unit as:
steem_unit = [["alpha_org", 70], ["founder_a", 23], ["founder_b", 7]]
18
This STEEM-unit contains 100 STEEM-satoshis, or 0.1 STEEM.
For every 1 STEEM contributed, an ALPHA contributer will receive 5 ALPHA tokens,
and Founder Account D will receive 1 ALPHA token. This five-sixths / one-sixth split is
expressed as:
token_unit = [["$from", 5], ["founder_c", 1]]
This ratio is defined in the following data structure:
struct smt_generation_unit
{
flat_map< account_name_type, uint16_t >
flat_map< account_name_type, uint16_t >
};
steem_unit;
token_unit;
This token-unit contains 6 ALPHA-satoshis, or 0.0006 ALPHA (if ALPHA has 4 decimal
places).
Next we define the unit ratio as the relative rate at which token_unit are issued as
steem_unit are contributed. So to match the specification of 6 ALPHA per 1 STEEM,
we need to issue 1000 ALPHA-units per STEEM-unit. Therefore the unit ratio of this
ICO is 1000. This unit ratio is placed in the min_unit_ratio and max_unit_ratio fields
of the smt_capped_generation_policy data structure:
min_unit_ratio = 1000
max_unit_ratio = 1000
A special account name, $from, represents the contributor.
Also supported is
$from.vesting, which represents the vesting balance of the $from account.
Why unit ratios?
Why does the blockchain use unit ratios, rather than simply specifying prices?
The answer is that it is possible to write ICO definitions for which price is ill-defined. For
example:
•
•
•
•
"$from" does not occur in token_unit.
"$from" occurs in both token_unit and steem_unit.
A combination of "$from" and "$from.vesting" occurs.
Future expansion allows new special accounts.
All of these ICO definitions have a unit ratio, but defining a single quantity to call “price”
is complicated or impossible for ICOs like these.
UI treatment of unit ratios
As a consequence of the above, the concept of “ICO price” is purely a UI-level concept.
UIs which provide an ICO price should do the following:
• Document the precise definition of “price” provided by the UI.
• Be well-behaved for pathological input like above.
• Have a button for switching between a unit ratio display and price display.
19
Hidden cap FAQ
• Q: Should my ICO have a cap?
• A: Some set of people stay away from uncapped ICOs due to perceived “greed”, or
want a guaranteed lower bound on the percentage of the ICO their contribution will
buy. If you want this set of people to participate, use a cap.
• Q: Should my cap be hidden?
• A: Some people like the transparency and certainty of a public cap. Other people
think a hidden cap creates excitement and builds demand. One possible compromise
is to publish the previous and next power of 10, for example “this ICO’s cap is
between 1 million and 10 million STEEM.”
• Q: How do I disable the cap?
• A: Set it so that the cap would occur above STEEM_MAX_SHARE_SUPPLY.
Launch
The effective launch time is the time at which tokens become transferable. Two possibilities occur based on the timing of revealing of the hard cap:
• When min_steem_units and hard_cap_steem_units are revealed before the
announced_launch_time, the launch is an on-time launch. The launch logic is
executed by the blockchain as soon as announced_launch_time arrives, regardless
of further user action.
• When min_steem_units and hard_cap_steem_units have not been revealed before
the announced_launch_time, the launch will be a delayed launch. The launch logic
is executed by the blockchain when min_steem_units and hard_cap_steem_units
have been revealed.
• If the launch is delayed, then any contributor may use smt_refund_operation to
get their STEEM back at any time after announced_launch_time, and before the
launch logic is executed.
The reasons for this design are as follows:
• The hidden cap isn’t published immediately (that’s the definition of hidden).
• Publishing the hidden cap is an action that must be done by the ICO creator (again,
any action requiring non-public information to occur cannot happen automatically
on a blockchain).
• If the ICO creator never acts, then the launch logic will never execute.
• In the case of such a malicious or unresponsive ICO creator, contributors’ STEEM
would effectively be trapped forever, and they would never receive any tokens.
• To keep the STEEM from being trapped in this way, the smt_refund_operation
is implemented.
struct smt_refund_operation
{
account_name_type
contributor;
asset
amount;
extensions_type
extensions;
};
20
Note, users are not required to use smt_refund_operation; each individual contributor
must opt-in to receiving a refund. If the ICO creator publicizes a legitimate reason they
failed to publish before announced_launch_time, it is possible that all/most contributors
will voluntarily choose not to use smt_refund_operation. In this case, the launch will
occur as soon as the ICO creator publishes the hidden values.
The launch logic considers a contribution followed by a refund to be equivalent to not
having contributed at all. Therefore, when a delayed launch occurs, each contributor will
be in exactly one of the following two states:
• The contributor has executed smt_refund_operation, received their STEEM back,
and will not participate in the ICO.
• The contributor has not been issued a refund, and will participate in the ICO.
It is possible for a delayed launch to have exceeded its min_steem_units value at the
announced launch time, but subsequently falls below its min_steem_units value as a
result of refunds. In such a case, the ICO will not occur; it will be treated as if it had
never reached its min_steem_units.
Full JSON examples
ALPHA
This example builds on the ALPHA example from earlier. This ICO has the following
characteristics:
•
•
•
•
•
•
•
•
70% of contributed STEEM goes to Alpha Organization Account (@alpha_org).
23% of contributed STEEM goes to Founder Account A (@founder_a).
7% of contributed STEEM goes to Founder Account B (@founder_b).
Minimum unit of contribution is 0.1 STEEM.
For every 1 STEEM contributed, the contributor gets 5 ALPHA (@contibutor_a).
For every 1 STEEM contributed, Founder Account C gets 1 ALPHA (@founder_c).
No minimum, hard cap, or soft cap.
No post-launch inflation after launch.
21
Figure 6: Alpha ICO Flow
These are the operations for the ALPHA launch:
[
["smt_setup",
{
"control_account" : "alpha",
"decimal_places" : 4,
"max_supply" : "1000000000000000",
"initial_generation_policy" : [0,
{
"pre_soft_cap_unit" : {
"steem_unit" : [["alpha_org", 70], ["founder_a", 23], ["founder_b", 7]],
"token_unit" : [["$from", 5], ["founder_c", 1]]
},
"post_soft_cap_unit" : {
"steem_unit" : [],
"token_unit" : []
},
"min_steem_units_commitment" : {
"lower_bound" : 1,
"upper_bound" : 1,
"hash" : "32edb6022c0921d99aa347e9cda5dc2db413f5574eebaaa8592234308ffebd2b"
},
22
"hard_cap_steem_units_commitment" : {
"lower_bound" : "166666666666",
"upper_bound" : "166666666666",
"hash" : "93c5a6b892de788c5b54b63b91c4b692e36099b05d3af0d16d01c854723dda21"
},
"soft_cap_percent" : 10000,
"min_unit_ratio" : 1000,
"max_unit_ratio" : 1000,
"extensions" : []
}
],
"generation_begin_time" : "2017-08-10T00:00:00",
"generation_end_time" : "2017-08-17T00:00:00",
"announced_launch_time" : "2017-08-21T00:00:00",
"smt_creation_fee" : "1000.000 SBD",
"extensions" : []
}
],
["smt_cap_reveal",
{
"control_account" : "alpha",
"cap" : { "amount" : 1, "nonce" : "0" },
"extensions" : []
}
],
["smt_cap_reveal",
{
"control_account" : "alpha",
"cap" : { "amount" : "166666666666", "nonce" : "0" },
"extensions" : []
}
]
]
Some things to note:
• We disable the soft cap by setting soft_cap_percent to STEEM_100_PERCENT =
10000.
• post_soft_cap_unit must be empty when the soft cap is disabled.
• The unit ratio does not change so min_unit_ratio / max_unit_ratio must be set
accordingly.
• We disable the hidden caps by using a zero nonce and setting lower_bound
==
upper_bound.
• We still need to reveal the caps with smt_cap_reveal_operation.
• The hard cap specified is the largest hard cap that does not result in created tokens
exceeding STEEM_MAX_SHARE_SUPPLY.
BETA
The BETA token is created with the following rules:
• For every 5 STEEM contributed, 3 STEEM go to founder account Fred.
23
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
For every 5 STEEM contributed, 2 STEEM go to founder account George.
10% of the initial token supply goes to founder account George.
20% of the initial token supply goes to founder acconut Henry.
70% of the initial token supply is divided among contributors according to their
contribution.
Each STEEM unit is 0.005 STEEM.
Each token unit is 0.0010 BETA.
The minimum raised is 5 million STEEM units, or 25,000 STEEM.
The maximum raised is 30 million STEEM units, or 150,000 STEEM.
Each contributor receives 7-14 BETA per STEEM contributed, depending on total
contributions.
George receives 1-2 BETA per STEEM contributed, depending on total contributions.
Harry receives 2-4 BETA per STEEM contributed, depending on total contributions.
If the maximum of 30 million STEEM units are raised, then min_unit_ratio
=
50 applies.
The maximum number of token units is min_unit_ratio times 30 million, or 1.5
billion token units.
Since each token unit is 0.0010 BETA, at most 1.5 million BETA tokens will be
generated.
If 75,000 STEEM or less is contributed, the contributors George and Harry will
receive the maximum of 14, 2, and 4 BETA per STEEM contributed (respectively).
If more than 75,000 STEEM is contributed, the contributors, George and Harry
will receive BETA in a 70% / 10% / 20% ratio, such that the total is fixed at 1.5
million BETA.
As a consequence of the hard cap, the contributors, George and Harry will receive
at least 7, 1, and 2 BETA per STEEM contributed (respectively).
This example is chosen to demonstrate how the ratios work. It is not a realistic example,
as most ICOs will choose to either set min_unit_ratio = max_unit_ratio like ALPHA,
or choose to use a large max_unit_ratio like BETA.
[
[
"smt_setup",
{
"control_account" : "beta",
"decimal_places" : 4,
"max_supply" : "1000000000000000",
"initial_generation_policy" : [0,
{
"pre_soft_cap_unit" : {
"steem_unit" : [["fred", 3], ["george", 2]],
"token_unit" : [["$from", 7], ["george", 1], ["henry", 2]]
},
"post_soft_cap_unit" : {
"steem_unit" : [],
"token_unit" : []
},
"min_steem_units_commitment" : {
"lower_bound" : 5000000,
"upper_bound" : 5000000,
24
"hash" : "dff2e4aed5cd054439e045e1216722aa8c4758b22df0a4b0251d6f16d58e0f3b"
},
"hard_cap_steem_units_commitment" : {
"lower_bound" : 30000000,
"upper_bound" : 30000000,
"hash" : "f8e6ab0e8f2c06a9d94881fdf370f0849b4c7864f62242040c88ac82ce5e40d6"
},
"soft_cap_percent" : 10000,
"min_unit_ratio" : 50,
"max_unit_ratio" : 100,
"extensions" : []
}
],
"generation_begin_time" : "2017-06-01T00:00:00",
"generation_end_time" : "2017-06-30T00:00:00",
"announced_launch_time" : "2017-07-01T00:00:00",
"smt_creation_fee" : "1000.000 SBD",
"extensions" : []
}
],
[
"smt_cap_reveal",
{
"control_account" : "beta",
"cap" : { "amount" : 5000000, "nonce" : "0" },
"extensions" : []
}
],
[
"smt_cap_reveal",
{
"control_account" : "beta",
"cap" : { "amount" : 30000000, "nonce" : "0" },
"extensions" : []
}
]
]
This spreadsheet will make the relationship clear.
GAMMA
The GAMMA token is like BETA, but with one difference: The large max_unit_ratio
means that the maximum issue of 1.5 million tokens is reached very early in the ICO.
This ICO effectively divides 1.5 million GAMMA tokens between contributors (provided
at least 5 STEEM is contributed).
[
[
"smt_setup",
{
25
"control_account" : "gamma",
"decimal_places" : 4,
"max_supply" : "1000000000000000",
"initial_generation_policy" : [0,
{
"pre_soft_cap_unit" : {
"steem_unit" : [["fred", 3], ["george", 2]],
"token_unit" : [["$from", 7], ["george", 1], ["henry", 2]]
},
"post_soft_cap_unit" : {
"steem_unit" : [],
"token_unit" : []
},
"min_steem_units_commitment" : {
"lower_bound" : 5000000,
"upper_bound" : 5000000,
"hash" : "dff2e4aed5cd054439e045e1216722aa8c4758b22df0a4b0251d6f16d58e0f3b"
},
"hard_cap_steem_units_commitment" : {
"lower_bound" : 30000000,
"upper_bound" : 30000000,
"hash" : "f8e6ab0e8f2c06a9d94881fdf370f0849b4c7864f62242040c88ac82ce5e40d6"
},
"soft_cap_percent" : 10000,
"min_unit_ratio" : 50,
"max_unit_ratio" : 300000,
"extensions" : []
}
],
"generation_begin_time" : "2017-06-01T00:00:00",
"generation_end_time" : "2017-06-30T00:00:00",
"announced_launch_time" : "2017-07-01T00:00:00",
"smt_creation_fee" : "1000.000 SBD",
"extensions" : []
}
],
[
"smt_cap_reveal",
{
"control_account" : "gamma",
"cap" : { "amount" : 5000000, "nonce" : "0" },
"extensions" : []
}
],
[
"smt_cap_reveal",
{
"control_account" : "gamma",
"cap" : { "amount" : 30000000, "nonce" : "0" },
"extensions" : []
}
26
]
]
DELTA
In this ICO we have one million DELTA tokens created for the founder, and none for
contributors. A modest contribution of 0.1 STEEM can be made by any user (including
the founder themselves) to trigger the generation.
[
[
"smt_setup",
{
"control_account" : "delta",
"decimal_places" : 5,
"max_supply" : "1000000000000000",
"initial_generation_policy" : [0,
{
"pre_soft_cap_unit" : {
"steem_unit" : [["founder", 1]],
"token_unit" : [["founder", 10000]]
},
"post_soft_cap_unit" : {
"steem_unit" : [],
"token_unit" : []
},
"min_steem_units_commitment" : {
"lower_bound" : 10000000,
"upper_bound" : 10000000,
"hash" : "4e12522945b8cc2d87d54debd9563a1bb6461f1b1fa1c31876afe3514e9a1511"
},
"hard_cap_steem_units_commitment" : {
"lower_bound" : 10000000,
"upper_bound" : 10000000,
"hash" : "4e12522945b8cc2d87d54debd9563a1bb6461f1b1fa1c31876afe3514e9a1511"
},
"soft_cap_percent" : 10000,
"min_unit_ratio" : 1000,
"max_unit_ratio" : 1000,
"extensions" : []
}
],
"generation_begin_time" : "2017-06-01T00:00:00",
"generation_end_time" : "2017-06-30T00:00:00",
"announced_launch_time" : "2017-07-01T00:00:00",
"smt_creation_fee" : "1000.000 SBD",
"extensions" : []
}
],
[
"smt_cap_reveal",
27
{
"control_account" : "delta",
"cap" : { "amount" : 10000000, "nonce" : "0" },
"extensions" : []
}
],
[
"smt_cap_reveal",
{
"control_account" : "delta",
"cap" : { "amount" : 10000000, "nonce" : "0" },
"extensions" : []
}
]
]
Vesting contributions
It is possible to send part or all of contributions to a vesting balance, instead of permitting
immediate liquidity. This example puts 95% in vesting.
"token_unit"
: [["$from.vesting", 95], ["$from", 5]]
Burning contributed STEEM
In this ICO, the STEEM is permanently destroyed rather than going into the wallet of
any person. This mimics the structure of the Counterparty ICO.
{
"steem_unit" : [["null", 1]],
"token_unit" : [["$from", 1]]
}
Vesting as cost
In this ICO, you don’t send STEEM to the issuer in exchange for tokens. Instead, you
vest STEEM (to yourself), and tokens are issued to you equal to the STEEM you vested.
{
"steem_unit" : [["$from.vesting", 1]],
"token_unit" : [["$from", 1]]
}
Non-STEEM & Hybrid ICO’s
ICOs using non-STEEM contributions – for example, SBD, BTC, ETH, etc. – cannot
be done fully automatically on-chain. However, such ICOs can be managed by manually
transferring some founder account’s distribution to buyers’ Steem accounts in proportion
to their non-STEEM contribution.
28
Inflation Parameters
Creation of SMT after launch is called inflation.
Inflation is the means by which the SMT rewards contributors for the value they provide.
Inflation events use the following data structure:
struct smt_inflation_unit
{
flat_map< account_name_type, uint16_t >
};
token_unit;
// Event: Support issuing tokens to target at time
struct token_inflation_event
{
timestamp
schedule_time;
smt_inflation_unit unit;
uint32_t
num_units;
};
This event prints num_units units of the SMT token.
Possible inflation target
The target is the entity to which the inflation is directed. The target may be a normal
Steem account controlled by an individual founder, or a multi-signature secured account
comprised of several founders.
In addition, several special targets are possible representing trustless functions provided
by the blockchain itself:
• Rewards. A special destination representing the token’s posting / voting rewards.
• Vesting. A special destination representing the tokens backing vested tokens.
Event sequences
Traditionally blockchains compute inflation on a per-block basis, as block production
rewards are the main (often, only) means of inflation.
However, there is no good reason to couple inflation to block production for SMTs. In fact,
SMTs have no block rewards, since they have no blocks (the underlying functionality of
block production being supplied by the Steem witnesses, who are rewarded with STEEM).
Repeating inflation at regular intervals can be enabled by adding interval_seconds and
interval_count to the token_inflation_event data structure. The result is a new
data structure called token_inflation_event_seq_v1:
// Event seq v1: Support repeatedly issuing tokens to target at time
struct token_inflation_event_seq_v1
{
timestamp
schedule_time;
smt_inflation_unit unit;
asset
new_smt;
29
int32_t
uint32_t
interval_seconds;
interval_count;
};
The data structure represents a token inflation event that repeats every interval_seconds
seconds, for interval_count times. The maximum integer value 0xFFFFFFFF is a special
sentinel value that represents an event sequence that repeats forever.
Note, the new_smt is a quantity of SMT, not a number of units. The number of units is
determined by dividing new_smt by the sum of unit members.
Adding relative inflation
Often, inflation schedules are expressed using percentage of supply, rather than in absolute
terms:
// Event seq v2: v1 + allow relative amount of tokens
struct token_inflation_event_seq_v2
{
timestamp
schedule_time;
smt_inflation_unit unit;
uint32_t
num_units;
int32_t
uint32_t
interval_seconds;
interval_count;
asset
uint32_t
abs_amount;
rel_amount_numerator;
};
Then we compute new_smt as follows from the supply:
rel_amount = (smt_supply * rel_amount_numerator) / SMT_REL_AMOUNT_DENOMINATOR;
new_smt = max( abs_amount, rel_amount );
If we set SMT_REL_AMOUNT_DENOMINATOR to a power of two, the division can be optimized
to a bit-shift operation. To gain a more dynamic range from the bits, we can let the shift
be variable:
// Event seq v3: v2 + specify shift in struct
struct token_inflation_event_seq_v3
{
timestamp
schedule_time;
smt_inflation_unit unit;
int32_t
uint32_t
interval_seconds;
interval_count;
asset
uint32_t
uint8_t
abs_amount;
rel_amount_numerator;
rel_amount_denom_bits;
};
Then the computation becomes:
30
rel_amount = (smt_supply * rel_amount_numerator) >> rel_amount_denom_bits;
new_smt = max( abs_amount, rel_amount );
Of course, the implementation of these computations must carefully handle potential
overflow in the intermediate value smt_supply * rel_amount_numerator!
Adding time modulation
Time modulation allows implementing an inflation rate which changes continuously over
time according to a piecewise linear function. This can be achieved by simply specifying the left/right endpoints of a time interval, and specifying absolute amounts at both
endpoints:
// Event seq v4: v3 + modulation over time
struct token_inflation_event_seq_v4
{
timestamp
schedule_time;
smt_inflation_unit unit;
int32_t
uint32_t
interval_seconds;
interval_count;
timestamp
timestamp
lep_time;
rep_time;
asset
asset
uint32_t
uint32_t
lep_abs_amount;
rep_abs_amount;
lep_rel_amount_numerator;
rep_rel_amount_numerator;
uint8_t
rel_amount_denom_bits;
};
Some notes about this:
• Only the numerator of relative amounts is interpolated, the denominator is the same
for both endpoints.
• For times before the left endpoint time, the amount at the left endpoint time is
used.
• For times after the right endpoint time, the amount at the right endpoint time is
used.
Code looks something like this:
if( now <= lep_time )
{
abs_amount = lep_abs_amount;
rel_amount_numerator = lep_rel_amount_numerator;
}
else if( now >= rep_time )
{
abs_amount = rep_abs_amount;
rel_amount_numerator = rep_rel_amount_numerator;
31
}
else
{
// t is a number between 0.0 and 1.0
// this calculation will need to be implemented
// slightly re-arranged so it uses all integer math
t = (now - lep_time) / (rep_time - lep_time)
abs_amount = lep_abs_amount * (1-t) + rep_abs_amount * t;
rel_amount_numerator = lep_rel_amount_numerator * (1-t) + rep_rel_amount_numerator * t;
}
Inflation operations
The inflation operation is specified as follows:
struct smt_setup_inflation_operation
{
account_name_type
control_account;
timestamp
smt_inflation_unit
schedule_time;
inflation_unit;
int32_t
uint32_t
interval_seconds = 0;
interval_count = 0;
timestamp
timestamp
lep_time;
rep_time;
asset
asset
uint32_t
uint32_t
lep_abs_amount;
rep_abs_amount;
lep_rel_amount_numerator = 0;
rep_rel_amount_numerator = 0;
uint8_t
rel_amount_denom_bits = 0;
extensions_type
extensions
};
The setup_inflation_operation is a pre-setup operation which must be executed before
the smt_setup_operation. See the section on pre-setup operations.
Inflation FAQ
• Q: Can the SMT inflation data structures express Steem’s current inflation scheme?
• A: Yes (except for rounding errors).
• Q: Can the SMT inflation data structures reward founders directly after X
months/years?
• A: Yes.
• Q: I don’t care about time modulation. Can I disable it?
32
• A: Yes, just set the lep_abs_amount == rep_abs_amount and lep_rel_amount_numerator
== rep_rel_amount_numerator to the same value, and set lep_time = rep_time
(any value will do).
• Q: Can some of this complexity be hidden by a well-designed UI?
• A: Yes.
• Q: Can we model the inflation as a function of time with complete accuracy?
• A: The inflation data structures can be fully modeled / simulated. For some issue
structures, the amount issued depends on how much is raised, so the issue structures
cannot be modeled with complete accuracy.
Named token parameters
Some behaviors of STEEM are influenced by compile-time configuration constants which
are implemented by #define statements in the steemd C++ source code. It makes sense
for the equivalent behaviors for SMTs to be configurable by the SMT creator.
These parameters are runtime_parameters and setup_parameters. The setup_parameters
are a field in smt_setup_operation; they must be set before smt_setup_operation, and
cannot be changed once smt_setup_operation is executed. The runtime_parameters
are a field in smt_set_runtime_parameters_operation, and they can be changed by
the token creator at any time.
These operations are defined as follows:
struct smt_set_setup_parameters_operation
{
account_name_type
flat_set< smt_setup_parameter >
extensions_type
control_account;
setup_parameters;
extensions;
};
struct smt_set_runtime_parameters_operation
{
account_name_type
flat_set< smt_runtime_parameter >
extensions_type
control_account;
runtime_parameters;
extensions;
};
Currently the following setup_parameters and runtime_parameters are defined:
struct smt_param_allow_vesting
struct smt_param_allow_voting
{ bool value = true;
{ bool value = true;
typedef static_variant<
smt_param_allow_vesting,
smt_param_allow_voting
> smt_setup_parameter;
struct smt_param_windows_v1
{
33
};
};
uint32_t cashout_window_seconds = 0;
// STEEM_CASHOUT_WINDOW_SECONDS
uint32_t reverse_auction_window_seconds = 0;
// STEEM_REVERSE_AUCTION_WINDOW_SECONDS
};
struct smt_param_vote_regeneration_period_seconds_v1
{
uint32_t vote_regeneration_period_seconds = 0; // STEEM_VOTE_REGENERATION_SECONDS
uint32_t votes_per_regeneration_period = 0;
};
struct smt_param_rewards_v1
{
uint128_t
content_constant = 0;
uint16_t
percent_curation_rewards = 0;
uint16_t
percent_content_rewards = 0;
curve_id
author_reward_curve;
curve_id
curation_reward_curve;
};
typedef static_variant<
smt_param_windows_v1,
smt_param_vote_regeneration_period_seconds_v1,
smt_param_rewards_v1
> smt_runtime_parameter;
UIs which allow inspecting or setting these parameters should be aware of the type and
scale of each parameter. In particular, percentage parameters are on a basis point scale
(i.e. 100% corresponds to a value of STEEM_100_PERCENT = 10000), and UIs or other
tools for creating or inspecting transactions must use the basis point scale.
Parameter constraints
Several dynamic parameters must be constrained to prevent abuse scenarios that could
harm token users.
• 0 < vote_regeneration_seconds < SMT_VESTING_WITHDRAW_INTERVAL_SECONDS
• 0 <= reverse_auction_window_seconds + SMT_UPVOTE_LOCKOUT < cashout_window_seconds
< SMT_VESTING_WITHDRAW_INTERVAL_SECONDS
SMT vesting semantics
SMTs have similar vesting (powerup / powerdown) semantics to STEEM. In particular:
• SMTs can be “powered up” into a vesting balance.
• SMTs in a vesting balance can be “powered down” over 13 weeks (controlled by static
SMT_VESTING_WITHDRAW_INTERVALS, SMT_VESTING_WITHDRAW_INTERVAL_SECONDS
parameters).
• Voting is affected only by powered-up tokens.
• Vesting balance cannot be transferred or sold.
34
Additionally, some token inflation may be directed to vesting balances. These newly
“printed” tokens are effectively split among all users with vesting balances, proportional
to the number of tokens they have vested. As the number of tokens printed is independent
of users’ vesting balances, the percentage rate of return this represents will vary depending
on how many tokens are vested at a time.
Content rewards
Tokens flow from SMT emissions into the reward fund. The blockchain uses algorithms
to decide:
• (1) How to divide the token-wide rewards among posts.
• (2) How to divide rewards within a post among the author and curators (upvoters)
of that post.
The algorithms to solve these problems operate as follows:
• (1) Posts are weighed against other posts according to the reward curve or rc.
• (2a) The curators collectively receive a fixed percentage of the post, specified by the
curation_pct parameter.
• (2b) The author receives the remainder (after applying any beneficiaries or limited/declined author reward).
• (2c) Curators are weighted against other curators of that post according to the
curation curve or cc.
Figure 7: Flow of initial tokens and SMT emissions
Curve definitions
The reward curve can be linear or quadratic. The linear reward curve rc(r) = r passes
the R-shares (upvotes) through unchanged. The quadratic reward curve rc(r) = rˆ2 +
35
2rs has increasing slope.
For an illustration of the meaning of reward curves, imagine grouping the most-upvoted
posts as follows:
• Section A consists of the top 10% of posts by upvotes.
• Section B consists of the next 10% of posts by upvotes.
Here’s how the rewards differ:
• With either reward curve, Section A posts will have greater rewards than Section
B posts, since they have more upvotes.
• With the quadratic reward curve, Section A posts will have an additional boost
relative to Section B posts, since Section A posts will get more rewards per upvote.
• With the linear reward curve, Section A and Section B will get the same reward
per upvote.
Possible curation curves are:
• Linear cc(r) = r
• Square-root cc(r) = sqrt(r)
• Bounded cc(r) = r / (r + 2s)
To help visualize, here are some plots called pie charts. Each colored area represents how
curation rewards are divided among curators with equal voting power.
36
Figure 8: Reward curves and curation curves
• The rectangular vertical column shows the immediate reward upon making an upvote.
• The colored area extending to the right shows how the rewards of a curator grow
as later curators vote.
• When both curves are linear, everyone gets the same curation reward regardless of
37
which post they vote on.
• In the case of rc_linear + cc_sqrt and rc_quadratic + cc_bounded, the same
height rectangles means everyone gets about the same initial curation reward, call
this ICR=.
• In the case of rc_linear + cc_bounded, the rectangles are decreasing in height.
This represents a progressive handicap against voting for already-popular posts, call
this ICR-.
• In the case of rc_quadratic + cc_sqrt and rc_quadratic + cc_linear, the
rectangles are increasing in height. Call this ICR+.
Fundamentally, curation is making a prediction that upvotes will occur in the future. As
reward system designers, our criterion for selecting a curve should be to reward successful
predictions. Which curve satisfies this criterion depends on the relationship between
current and future upvotes.
• If a post’s future upvotes are independent of its current upvotes, we should choose
an ICR= curve.
• If a post’s future upvotes are positively correlated with its current upvotes, we should
choose some ICR- curve, ideally somehow tuned to the amount of correlation.
• If a post’s future upvotes are negatively correlated with its current upvotes, we should
choose some ICR+ curve, ideally somehow tuned to the amount of correlation.
In practice, independence or a modest positive correlation should be expected, so an ICR=
or ICR- curve should be chosen. For STEEM itself, curation was originally the quadratic
ICR=, as of the Steem hard fork 19 it is the linear ICR=.
Target votes per day
Each account has a voting_power, which is essentially a “mana bar” that fills from 0%
to 100% over time at a constant rate. That rate is determined by two parameters:
• (a) The time it takes to regenerate the bar to 100%, vote_regeneration_period_seconds.
• (b) The voting_power used by a maximum-strength vote.
The vote_regeneration_period_seconds is specified directly. For (b), instead of
specifying the voting power of a maximum-strength vote directly, instead you specify
votes_per_regeneration_period. Then the maximum-strength vote is set such that a
user casting that many max-strength votes will exactly cancel the regeneration.
38
SMT Setup GUI Sketch
39
Figure 9: SMT Configuration
Votability and Rewardability
In this section, we introduce the concepts of votability and rewardability.
•
•
•
•
A token is votable for a comment if the balance of that token influences the comment.
For a given vote, each votable token of the comment is either rewardable or advisory.
If a token is rewardable, then the vote affects the comment’s reward in that token.
If a token is advisory, then the vote does not affect the comment’s reward in that
token.
Advisory votes do not affect rewards or voting power. However, the ranking algorithms
and estimated reward calculations still apply advisory votes, so UIs may display advisory
posts accordingly.
The votable token set is determined by allowed_vote_assets which is a comment_options_extension.
struct allowed_vote_assets
{
flat_map< account_name_type, votable_asset_info >
};
votable_assets;
struct votable_asset_info_v1
{
share_type
max_accepted_payout
= 0;
bool
allow_curation_rewards = false;
};
typedef static_variant< votable_asset_info_v1 >
votable_asset_info;
The following rules are applied to determine whether tokens are votable:
• STEEM is votable for every post.
• A token is votable for a post if it appears in the post’s votable_assets.
• Otherwise, the token is not votable for this post.
And these are the rules for whether a token is rewardable:
• In order to be rewardable for a post, a token must be votable for that post.
• If, for some post/token, that post’s max_accepted_payout of the token is zero, then
the token is not rewardable for that post.
• If some voter (i.e. upvoter / downvoter) has a zero balance of a token, then that
token is not rewardable for that voter’s votes.
• If the max_accepted_payout for any non-STEEM token is nonzero, then
the max_accepted_payout for STEEM/SBD must be at least the default
max_accepted_payout.
Implementation notes:
• For an advisory vote, all rewards are zero, including curators and beneficiaries. This
is because the blockchain applies the max_accepted_payout cap before the curator
/ beneficiary computations.
40
• Currently (as of Steem hard fork 19), the Steem blockchain does deduct voting
power for advisory Steem votes. This behavior will be changed in a future Steem
hard fork (Steem issue #1380).
• At most two tokens may be specified in votable_assets. This means that each
post is voted with at most three tokens (including STEEM).
• The default max_accepted_payout is stored in max_accepted_steem_payout_latch
member of dynamic_global_properties_object.
Clients should populate
max_accepted_payout of a post based on this member, in case the default value
changes in a future version.
No consensus level restriction forces any particular post to have any particular
allowed_vote_assets. As a consequence, any post may mark itself as eligible to be
rewarded in any token. However, UI’s may impose their own non-consensus validation
rules on allowed_vote_assets, and hide posts that violate these non-consensus
validation rules.
For example, in a Hivemind community with a corresponding token, there may be a validation rule that the allowed_vote_assets specified in each post within that Hivemind
community must include the token of that community. This is a non-consensus validation rule, since the entire concept of a post existing within a Hivemind community is a
non-consensus concept. Since it is a non-consensus validation rule, no consensus logic can
enforce it. However, UIs that are aware of Hivemind communities may refuse to index or
display posts that violate this validation rule.
Static Token Parameters
Static parameters are configuration constants that affect the behavior of SMTs, but are
deliberately excluded from smt_setup_parameters or smt_runtime_parameters. The
reason they are designed to be non-configurable is that allowing these parameters to
significantly deviate from the values used for STEEM would result in significant risks,
such as:
•
•
•
•
May
May
May
May
result in a very complicated implementation.
result in extreme end-user frustration.
threaten the security and stability of the token.
threaten the security and stability of STEEM.
Here is the list of such static parameters:
• SMT_UPVOTE_LOCKOUT_HF17 : Static – This value locks out upvotes from posts at a
certain time prior to “CASH OUT”, to prevent downvote abuse immediately prior
to “CASH OUT.”
• SMT_VESTING_WITHDRAW_INTERVALS : Static
• SMT_VESTING_WITHDRAW_INTERVAL_SECONDS : Static
• SMT_MAX_WITHDRAW_ROUTES : Static
• SMT_SAVINGS_WITHDRAW_TIME : Static
• SMT_SAVINGS_WITHDRAW_REQUEST_LIMIT : Static
• SMT_MAX_VOTE_CHANGES : Static
• SMT_MIN_VOTE_INTERVAL_SEC : Static
• SMT_MIN_ROOT_COMMENT_INTERVAL : Static
• SMT_MIN_REPLY_INTERVAL : Static
• SMT_MAX_COMMENT_DEPTH : Static
41
• SMT_SOFT_MAX_COMMENT_DEPTH : Static
• SMT_MIN_PERMLINK_LENGTH : Static
• SMT_MAX_PERMLINK_LENGTH : Static
Mandatory token parameters
The token parameters set by smt_setup_parameters or smt_runtime_parameters have
default values. A few STEEM-equivalent parameters are specified by smt_setup_operation
fields. These are the parameters which do not have a default value, and thus, must be
specified for every asset.
• SMT_MAX_SHARE_SUPPLY : Set by smt_setup_operation.max_supply
• SMT_BLOCKCHAIN_PRECISION : Set by pow(10, smt_setup_operation.decimal_places)
• SMT_BLOCKCHAIN_PRECISION_DIGITS : Set by smt_setup_operation.decimal_places
SMT interaction with existing operations
• comment_payout_beneficiaries : The existing comment_payout_beneficiaries
will only redirect STEEM. In the future, comment_payout_beneficiaries functionality which allows redirecting SMT rewards may be added.
• comment_options : max_accepted_payout, allow_votes only affects STEEM, see
here to restrict max_accepted_payout for assets. allow_curation_rewards affects
all tokens.
• vote_operation : Multiple tokens in the comment’s votable set vote.
• transfer_operation : Supports all SMTs.
• Escrow operations: Do not support SMTs.
• transfer_to_vesting_operation : Supports all SMTs that support vesting.
• withdraw_vesting_operation : Supports all SMTs that support vesting.
• set_withdraw_vesting_route_operation : Does not support SMTs.
• account_witness_vote_operation : SMTs do not affect witness votes.
• account_witness_proxy_operation : SMTs do not affect witness votes.
• feed_publish_operation : Feeds may not be published for SMTs.
• convert_operation : SMTs cannot be converted.
• Limit order operations : Limit orders are fully supported by SMTs trading against
STEEM.
• transfer_to_savings_operation : SMTs support savings.
• decline_voting_rights_operation : Affects SMT votes as well as STEEM votes.
• claim_reward_balance_operation : Restrictions on this operation are relaxed to
allow any asset in any of the three fields, including SMTs.
• delegate_vesting_shares_operation : Supports all SMTs that support vesting.
• Multisig Native: There is nothing “special” about the handling of SMT operations
signed by multiple signatures. If you set up your account to require multi-signature
security, then everything your account signs will need to be signed with multiple
signatures, as you specified. This includes operations your account does as a control
account managing an SMT, and operations your account does as a user holding SMT
tokens.
42
Automated Market Makers for SMTs
Automated Market Makers are smart contracts, largely based on the Bancor Protocol [2],
that may be constructed during the initial ICO setup of an SMT for providing perpetual
liquidity to an SMT community. For simplicity, Automated Market Makers in Steem may
only trade between STEEM and any given SMT.
Setup
Basic Definitions
In this article, we’ll let s represent a quantity of STEEM, let t represent a quantity of
some token (SMT), and let p represent a price, such that pt is STEEM-valued (i.e. if
MYTOKEN is trading at p = 0.05 STEEM / MYTOKEN then t = 120 MYTOKEN has
a value of pt = (0.05 STEEM / MYTOKEN) · (120 MYTOKEN) = 6 STEEM.
Suppose we have a market maker (or any economic agent) with a two-asset “portfolio”
(inventory) of s STEEM and t tokens. If the price of tokens is t, then we may measure of
the value of this portfolio, in units of STEEM, as v(p, s, t) = s + pt.
One common portfolio management policy is to require that STEEM should be some
constant fraction r of the portfolio, i.e. s = rv(p, s, t) where 0 < r < 1. We call this
policy the constant portfolio ratio or CPR policy, and the equation s = rv(p, s, t) is the
CPR invariant.
A different portfolio management policy, discussed by the Bancor whitepaper, is called
CRR or constant reserve ratio. To discuss CRR, let us notate the total number of tokens
in existence as T . The CRR invariant is then defined as s = rv(p, 0, T − t).
Notes on Conventions
We must discuss where our convention varies from the Bancor whitepaper. At some times,
when some user Alice interacts with the market maker, Alice will remove some tokens
from her balance to get STEEM from the market maker’s balance. On the other hand,
Bob may add some tokens to his balance in exchange for sending STEEM to the market
maker’s balance.
Bancor takes the convention that in this example, the market maker destroys tokens in
its interaction with Alice, and creates tokens in its interaction with Bob. The Bancor
convention suggests the market maker is not an ordinary actor, but needs system-level
“special powers” – specifically, the privilege to operate the token printing press – in order
to function.
In this paper, we adopt the convention that the tokens sent by Alice to the market maker
are not destroyed, but are instead added to the inventory (balance) of the market maker.
Likewise, the tokens sent to Bob by the market maker are not created out of thin air; they
already exist and are merely transferred from the inventory of the market maker to Bob.
Thus, we show that the market maker is essentially an ordinary economic agent acting
according to a deterministic algorithm – it doesn’t actually need “special powers”!
43
Finite Trades
Basic Definitions
A trade is a change in the market maker’s balance from (s, t) → (s+∆s, t+∆t). The price
at which the trade occurs is defined as p = −∆s
∆t . We restrict ourselves to well-formed
trades where either ∆s = ∆t = 0, or ∆s and ∆t are both nonzero and have opposite sign.
Theorem: A trade at price p conserves value at price p. More rigorously, if ∆s, ∆t
represent a trade at price p, then v(p, s, t) = v(p, s + ∆s, t + ∆t).
Let us more rigorously define the market maker’s state as a tuple M = (s, t, T, r). Given
some price p, we may define the restoring trade at p (also called a relaxing trade or a
relaxation) to be a trade which occurs at price p and results in a state that satisfies the
CRR invariant.
Computing the Restoring Trade
The restoring trade consists of functions ∆s(M, p) and ∆t(M, p). We may actually compute these functions from the definition of price and the CRR invariant:
∆s = −p∆t
s + ∆s = rv(p, 0, T − (t + ∆t))
⇒ s − p∆t = rv(p, 0, T − t − ∆t)
= rp(T − t − ∆t)
= rpT − rpt − rp∆t
⇒ rp∆t − p∆t = rp(T − t) − s
rp(T − t) − s
⇒ ∆t =
rp − p
(
)(
)
1
s
=
− r(T − t)
1−r
p
⇒ ∆s = −p∆t
(
)
1
=
(rp(T − t) − s)
1−r
Computing the Equilibrium Price
Given a state M , there exists some price peq (M ) for which the restoring trade is zero;
call this price the equilibrium price. We may compute the equilibrium price by setting
∆s = 0:
44
∆s = (
0
)
1
=
(rpeq (T − t) − s)
1−r
⇒ rpeq (T − t) − s = 0
⇒ rpeq (T − t) =
⇒ peq
=
s
s
r(T − t)
Theorem: Relaxation is idempotent. That is, after relaxing at price p, the equilibrium
price of the resulting state is p, and a second relaxation at price p will be a zero trade.
Example
Example: Suppose M = (1200, 3600, 12000, 0.25) and p = 0.5. Then of the T = 12000
TOKEN in existence, t = 3600 TOKEN is held by the MM, so T −t = 12000−3600 = 8400
TOKEN are “circulating” (i.e. exist in balances outside the MM). These circulating tokens
are worth p(T − t) = 4200 STEEM total, so they “should be” backed by a target reserve
level of rp(T − t) = 4200 ∗ 0.25 = 1050 STEEM.
In this example, there is “too much” STEEM in the reserve, so relaxation will buy tokens
in the market. This sale will cause two effects: It will decrease the reserve STEEM, and
also decrease circulating tokens. The decrease in circulating tokens, in turn, causes the
target reserve level to decline. For every 1 STEEM used to buy tokens, the target reserve
level declines by r STEEM; since r < 1 eventually the declining reserve will “catch up”
to its more slowly declining target level.
(
)
1
The above algebra shows that we will catch up at ∆s = 1−r
(rp(T − t) − s) and
(
)(
)
1
s
∆t = 1−r
p − r(T − t) . Running the calculations with the numbers defined in this
example gives ∆s = −200 STEEM and ∆t = 400 TOKEN.
Let’s check that these computed values ∆s = −200, ∆t = 400 (a) represent a trade with
price 0.5, and (b) that the CRR invariant holds for the new state Mnew = (s + ∆s, t +
∆t, T, r). Calculating p = −∆s
∆t we indeed get p = 0.5. After this trade executes, the
market maker has snew = s + ∆s = 1200 − 200 = 1000 STEEM, and tnew = t + ∆t =
3600 + 400 = 4000 tokens.
To check condition (b), that the CRR invariant holds, we effectively repeat the analysis
in the initial paragraph of this example with the new numbers. We know Mnew =
(1000, 4000, 12000, 0.25) and p = 0.5. Then of the T = 12000 TOKEN in existence,
tnew = 4000 TOKEN is now held by the MM, so T −tnew = 12000−4000 = 8000 TOKEN
are now circulating. These circulating tokens are worth p(T − tnew ) = 4000 STEEM total,
so they “should be” backed by a target reserve level of rp(T − t) = 4000 ∗ 0.25 = 1000
STEEM. Since the target reserve level indeed exactly matches the actual reserve level of
snew = 1000 STEEM, we conclude that the CRR invariant is satisfied after this relaxing
trade.
45
Infinitesimal Trades
This section is fairly technical; the reader will need a good grasp of calculus and differential
equations to follow the results.
Setting up the Problem
Suppose we satisfy the invariant condition at some price p = pe q; by the CRR invariant
s = rv(p, 0, T − t) = rp(T − t). Suppose the price then increases to p + ∆p and a relaxing
trade ∆s, ∆t occurs at this new price.
In this section we consider the limiting situation where ∆p is infinitesimally small, so we
will use Leibniz notation (dp for a small change in p, ds for a small change in s, dt for a
small change in t).
Solving the DE’s
By applying the substitution p ← p+dp to the expression for ∆s computed in the previous
section, we obtain an expression which simplifies to a separable DE which can be solved:
ds
=
=
=
=
1
⇒ dp =
p
∫
1
⇒
dp =
p
⇒ ln(p) =
⇒p
1
(r(p + dp)(T − t) − s)
1−r
1
(rp(T − t) + rdp(T − t) − rp(T − t))
1−r
1
r(T − t)dp
1−r
( )
s
1
dp
1−r p
(
)
1
(1 − r)
ds
s
∫
1
(1 − r)
ds
s
(1 − r) ln(s) + C0
= k0 s1−r
Similarly for t, we can start from dt = −ds/p and again obtain and solve a separable DE:
46
= −ds/p
r
= −
(T − t)dp/p
1−r
( )
r
1
1
dt = −
dp
⇒
T −t
1−r p
(
)
1
1−r
1
⇒ dp =
dt
p
r
t−T
∫
∫
1−r
1
1
dp =
dt
⇒
p
r
t−T
1−r
⇒ ln(p) =
ln |t − T | + C1
r
1−r
⇒ p = k1 (T − t) r
dt
Qualitative discussion
In a CRR market maker, where does the “backing” for newly emitted tokens come from?
One option is to lower the reserve ratio r. This option results in no immediate market
activity, but will weaken the response of the market maker to any future price changes.
This is called the “pay later” option.
Another option is to change the dynamical system’s initial conditions, i.e. edit the constants of integration. This option will cause the equilibrium price peq to drop, meaning
the market maker will more aggressively sell tokens to replenish the reserve. If order
books are deep compared to the amount of emission, and there are adequate buyers for
the tokens, then the sales will be able to replenish the reserve and keep the equilibrium
price near its old value; the deep order books provide resistance to the price change being
driven by the market maker. If order books are thin compared to the amount of emission,
and there are few/no buyers for the tokens, then the equilibrium price will fall, breaking through the thin orders and lowering the market price. Even though few/no many
tokens were sold, so even though the absolute amount of STEEM in the reserve is still
nearly/exactly the same as before, the reserve’s value relative to the now-lower market
cap of the token has increased to the reserve ratio. This option is the “pay now” option.
FAQ
Q: What is the relevance of constant portfolio ratio policy?
A: It may become a supported market maker policy in the future.
Q: Can the reserve ratio go over 100 percent?
A: No.
Q: Can the reserve ratio be exactly 100 percent?
A: Not with the system described in this paper. It might be possible to code as a special
case.
47
Q: In a CRR market maker, where does the “backing” for newly emitted tokens come
from?
A: As blockchain designers, we have two options for sourcing the “backing”. One option
is to lower the reserve ratio r. This option results in no immediate market activity, but
will weaken the response of the market maker to any future price changes. This is called
the “pay later” option.
Another option is to change the dynamical system’s initial conditions, i.e. edit the constants of integration. This option will cause the equilibrium price peq to drop, meaning
the market maker will more aggressively sell tokens to replenish the reserve. If order
books are deep compared to the amount of emission, and there are adequate buyers for
the tokens, then the sales will be able to replenish the reserve to its target level while
keeping the equilibrium price near its old value. The deep order books provide resistance
to the price change being driven by the market maker.
If order books are thin compared to the amount of emission, and there are few/no buyers
for the tokens, then the equilibrium price will fall, breaking through the thin orders and
lowering the market price. Even though few/no many tokens were sold, so even though
the absolute amount of STEEM in the reserve is still nearly/exactly the same as before,
the reserve’s value relative to the now-lower market cap of the token has increased to the
reserve ratio. This option is the “pay now” option.
Q: Where’s the “don’t pay” option?
A: You have to come up with some answer to where the “backing” for newly emitted
tokens will come from. Unless there’s no emission. Or unless there’s no “backing” for any
tokens. So the “don’t pay” option would be to have an SMT with either no emission, or
no market maker.
Q: Don’t fractional exponents require floating point to implement?
A: Only if you need fairly high precision (we don’t), don’t care about bit-for-bit reproducibility across compilers, OS’s, CPU’s, etc (we do), and need to do massive numbers
of calculations quickly (we don’t). A fast, approximate, all-integer implementation is
possible.
Q: Does this market maker interact with the order book through the existing limit order
system, or is it a separate set of operations?
A: In theory, it could be implemented either way. However, the likely outcome is that
the market maker will be implemented outside of order-book markets to allow its code to
be modularized. In practice, if implemented as a completely separate subsystem, people
will run arbitrage bots which will trade away any price differences between the reserve
system and the existing market system.
Q: Where do the market maker’s initial token balances come from?
A: ICO units can specify the market maker as a destination. An ICO creator may direct
a percentage of their ICO’s STEEM contributions to the MM by specifying the market
maker similarly to specifying a founder. Or may use the soft cap system to specify all
STEEM above a pre-determined amount goes to the ICO. Likewise, a fixed or percentage
amount of tokens can be added in the ICO to increase the MM’s token balance.
Q: Can someone send STEEM or tokens to the market maker?
A: Yes.
48
Q: What are the side effects of sending STEEM or tokens to the market maker?
A: The constants of integration are re-initialized, meaning the equilibrium price will
change. The market maker will become more aggressive about selling the asset.
Q: Can’t this cause manipulation or appropriating the market maker’s inventory to private
profit?
A: Sending assets to the market maker does cause it to engage in trading activity which
affects the price. However, dumping an identical amount on the market will result in a
larger amount of trading activity and a larger effect on the price. If Eve is willing to
spend her tokens/STEEM to manipulate prices, she would prefer the strategy of simply
dumping tokens/STEEM on the market, as that strategy is more cost-effective for her.
Q: Does the market maker’s activity generate profits (losses)?
A: It depends on how you measure “profits”. If you measure the value of STEEM and
tokens in some external third currency such as US dollars or bitcoins, the market maker’s
inventory, valued in that currency, can definitely increase or decrease. If people voluntarily
send STEEM or tokens to the market maker, such activity definitely increases the value
of the market maker regardless of your measurement.
Another way to define profits is by the constants of integration. If both of the constants
of integration increase, or one increases while the other remains the same, a tiny increase
occurs with each trade when the market maker is in “taker” mode.
Q: What is “taker” mode? How can a market maker be set to operate in “taker” mode?
A: When orders execute, the order used to set the price is called the maker; the maker’s
counterparty is the taker. In the STEEM on-chain market (and on almost all trading
platforms) the older order is always the maker.
When the market maker is in taker mode, its actions are always considered to be taker
orders, which execute at the price specified by the user acting as its counterparty – this
price is always at least a little bit more favorable than the market maker is willing to
accept. When the market maker is not in taker mode, its actions are always considered
to be maker orders, which don’t generate changes in the constants of integration.
Taker mode is a runtime parameter that can be set by the SMT’s control account.
Q: Who benefits from the profits of a market maker in taker mode?
A: Maybe nobody, or maybe everybody. It’s decentralized.
Q: OK, if my SMT reaches a steady price, the STEEM in the reserve is basically locked
up forever. That seems not cool. How do I set it up so that this STEEM can be unlocked
for the benefits of my SMT users?
A: Set the DRR (decaying reserve ratio) setup parameter. If you set DRR, then the reserve
ratio will slowly drop over time to a pre-set value, using its excess STEEM reserves to
buy excess tokens. Setting DRR is an excellent, fair, decentralized way to return excess
capitalization to contributors in a more-popular-than-anticipated ICO that raises more
than the sponsor can effectively spend.
Q: If the reserve ratio can change over time due to pay-later emissions or DRR, it’s not
really a constant reserve ratio, is it?
49
A: No, they’re not. The reserve ratio’s called “constant” because it’s constant over the
short-term, in normal conditions, or in the conditions in Bancor which is where it was
named. But the name could be regarded as slightly misleading.
Q: If the constants of integration can change over time, they’re not really constants either,
are they?
A: No, they’re not. They’re called constants of integration because that’s their mathematical role in the calculation that introduces them. Maybe they’ll be differently named
in a future version of this paper.
Q: Can I specify a contribution to a DRR to be a pay-later contribution, that increases
its reserve ratio, the increase to be eventually negated over time by future decay? Why
would I want to?
A: Yes. This is effectively contributing to the market maker, subject to the condition
that it’s not allowed to immediately dump a portion of the contribution. It’s useful if
you want to make a large contribution to a market maker without causing it to create a
disturbance by immediately dumping a significant fraction of your contribution onto the
market.
Q: Can I specify a DRR with emission to use pay-later for emissions when the RR is
decaying?
A: Yes.
Q: Is the market maker specified here equivalent to a Bancor token changer?
A: No. A Bancor token changer has multiple reserve ratios that must sum to one hundred
percent, and involves a third token that effectively represents equity in the token changer.
This paper’s market maker has none of these features.
Q: I want to have an initial “price discovery” period where people trade without action
from the market maker, then have tokens and STEEM from the ICO gradually flow in
over time to the market maker so it has a delayed, slow start from zero to full power. Can
I do it?
A: This is called “gradual seeding” and it may be supported.
Q: What about numerical stability?
A: A market maker will be restricted to only operate when its balances exceed a certain
minimum for both assets. Also, reserve ratios will be restricted to a certain range, all
the mechanisms that can set / increase / decrease a reserve ratio will be restricted to not
allow it to move outside the range. Tentative numerical experiments suggests these limits
should be about 10,000 satoshis of both assets, 5 percent and 50 percent, respectively.
These values are subject to change based on future experimentation, worst-case analysis,
and testing.
Costs of SMT Operations And Bandwidth Rate Limiting
Like STEEM, SMTs can be transferred on the Steem blockchain with zero fees. Steem
replaces fees with bandwidth rate limiting based on the percentage of STEEM an ac-
50
count has staked, which means the blockchain calculates how much STEEM an account
has temporaily vested to determine how much bandwidth the account is permitted for
transfers, posting, and other operations across a period of time. In a future version of
Steem, possesion of an account name could permit some small degree of bandwidth to
allow for even greater user experience.
Fee-less Operations Necessary for Quality User Experience
Because of bandwidth rate limiting, Steem may never charge applications or users transaction fees for basic operations such as voting, posting, and transferring tokens. This lack
of fees allows Steem based apps to compete with their non-blockchain counterparts, such
as Facebook or Reddit, which certainly do not charge fees for actions such as ‘Like’ and
‘Upvote’. If these applications did charge fees, adoption would suffer.
Decentralized Exchange
One of the valuable features of SMTs is their immediate access to functioning unmanned
markets against the liquid asset, STEEM.
Automatic Order Matching
The Decentralized Exchange (DEX) structures of Steem allow assets to be automatically
matched for best possible price when bids and asks overlap, unlike other DEXs - which
require a “man in the middle” or user-agent to match orders. Automatic, rather than
middle-man-facilitated, order matching is important for the security of Steem-based assets,
and for the replicability and safety of DEX interfaces.
Diverse Asset Types
There are several assets that SMT users and creators will have access to by way of the
Steem DEX: STEEM, SBD, SMTs, and Simple Derivatives (IOUs). These neighboring
assets can increase the visibility and network effect of all created SMTs.
STEEM is the gateway token for assets issued on Steem, staying relevant by acting as
the bandwidth usage measuring stick across Steem’s SMTs. STEEM is also the common
denominator asset, acting as a trading pair for all of Steem’s SMTs.
SBD (Steem Blockchain Dollars) are an experimental asset on Steem that relate to the
US Dollar, originating with Steem’s launch in 2016. It is unclear if SBD will bring value
to holders of USD as they will compete, possibly poorly, with USD IOU tokens; however,
SBDs will bring value to speculators.
SMTs as described in this proposal are an important part of growing the token ecosystem,
and bringing crypto assets to the mainstream. SMTs will trade against STEEM across
the DEX.
Simple Derivatives (IOUs) will be possible via SMT issuance. For instance, if an SMT
is issued without inflation or rewards pool properties, then the issuer can reliably back
51
the token with another real world asset such as bitcoin or USD. In this instance, the
issuer could create a business functioning as a gateway, by trading their IOU for BTC
or USD. Users would buy the IOU to gain access to the Steem DEX. This market would
add diversity and value flow to the Steem ecosystem, while adding to the DEX’s network
effect.
Zero Trading and Transfer Fees
The Steem DEX is the first DEX to exist without trading fees, to the benefit of SMT
creators and traders alike. This is made possible by bandwidth rate limiting (described
in the original Steem Whitepaper and Bluepaper), as the process by which the blockchain
calculates transaction “prices” on a per byte basis, and deducts transaction bandwidth
available to an account temporarily. These “prices” are an internal blockchain accounting
and do not debit any token balances.
Augmenting SMTs with Additional Native Contracts
There are several potentially valuable programmable contracts that are not in the immediate scope of SMTs, however, these contract capabilities can be created as modular,
follow-on projects that increase the creativity entrepreneurs and communities may apply
to growth of SMT ecosystems.
Community Building with Paid Positions
SMT communities may be bolstered with paid positions, guild roles, or jobs that are
defined in programmable, native smart contracts and matched with continuously elected
participants. Rewards received through the elected position come from some portion of
the token’s Founder allocations or donations that are sent to a paid position contract.
Paid position contracts may be defined for length of position, frequency and volume of
payments, particular token used for stake-weighted elections, percentage of the token
required for a participant to be elected, and how tokens in paid position contracts are
socialized or forfeited given no participant is elected.
The paid roles may be leveraged to support various applications, games, and businesses
built around an SMT. A contract for a paid position, the postion’s reward schedule, and
the voting thresholds required to elect an account into a paid position may be created by
anyone for a fee. To establish the purpose of these positions, job descriptions or constitutions that encourage adherence to performance expectations may be established by the
issuer or the token’s community. There can be an unlimited number of paid positions,
and paid position contracts can receive any amount of a token’s Founder allocations or
community donations. The types of paid positions that may be employed includes everything from front end developer, to evangelist, including educational content creator,
business development representative, and many roles that have yet to be imagined.
52
Democratic SMTs using Whitelist Oracles
SMTs represent completely open access to tokens, however, some entities may wish to
enable one-whitelisted-account, one-vote-per-post and X-number-of-target-votes-per-day
algorithms to increase their token’s potential for accurate wisdom-of-the-crowd content
discovery mechanics and the democratic nature of their token community. To incorporate
this, the Rewards Pool for a token will need to have a manageable whitelist that can be
enabled only at launch. Whitelist management may be handled by the entity launching
the token or outsourced to an identity management service, such as Civic or Jumio. The
service would need to publish a feed of Steem usernames for known/identified people into
the Steem blockchain, along with periodic updates to ensure accuracy of the whitelist.
As the blockchain pays rewards to a token, it verifies the account receiving the token is
on the whitelist, otherwise the tokens are returned to the reward pool.
Secondary ICOs for Contiguous Fundraising
Entrepreneurs leveraging SMTs to finance ventures may want to have the option to perform token auctions after the initial launch of the token. The entrepreneur can reserve
Founders tokens at launch and earmark them for later sale, however, they may want to
auction these tokens rather than sell them into Bid/Ask order books or sell them OTC.
To enable secondary auction-style ICOs, a secondary auction contract may be established.
This contract requires definitions for when an ICO begins and how long it lasts, as well as
lockup periods for the tokens purchased. The lockup period allows the tokens to be sold
at a discount to the open markets and attract investment capital that would otherwise
stay out of the market. The entrepreneur will send tokens to this contract prior to the
beginning of the auction and the tokens will be distributed to the auction participants
immediately following the close of auction period.
Bandwidth Sharing with SMTs Based on Reserve Liquidity Pools
SMTs that use ICOs to create Automated Market Makers to boost token liquidity will
inherit bandwidth rights proportionate to the amount of STEEM in the Automated Market Maker’s reserve pool. This bandwidth inheritance confers transaction rights from
STEEM to all the of the “powered up” and vested SMT, basically permitting SMT owners to transact proportionate to their stake of SMT without owning STEEM outright.
Bandwidth Sharing based on liquidity pools enables new tokens to operate with an even
higher degree of independence while still contributing proportionate value to STEEM.
What Makes SMTs Better Suited to ApplicationSpecific Blockchains, such as Steem, than ApplicationGeneral Blockchains, such as Ethereum?
Throughout the history of software and hardware development, it has been observed
that specialized systems have the potential to greatly outperform generalized systems.
An example of this can be seen in GPUs outperforming CPUs through specialization,
which was followed by ASICs outperforming GPUs for particular tasks. In turn, some
53
wonder how a specialized blockchain, such as Steem, which hosts application-specific programmability, and static mechanics embedded in consensus, is more suited to SMTs than
application-general, open-programmability blockchains, such as Ethereum, which hosts
turing-complete (“infinitely”) programmable smart contracts in a layer beyond consensus, and has shown its use for discovering new cryptocurrency concepts. Without delving
into Steem’s advantages in network effect and developer team experience, the advantages
for SMTs on Steem can be seen through a set of computer science, consumer safety, and
economic perspectives.
SMTs are Safer and More Cost Effective in Application-Specific
Blockchain Environments
The value of SMTs in a native, specialized-programmability environment, such as Steem,
comes from reliability of the code and efficiencies created by that reliability, whereas
application-general platforms, such as Ethereum and Tezos, require costly and highlyassumptive audits on each new token and issuer to be deemed safe. Some of these
application-general protocols claim to have formal verification, which is valuable, however, the majority of the audit cost remains due to the need to audit the issuer’s choice
of token mechanics, choice of client for writing the code, and semantics of custom code
written to the token. Enabled by the purposeful design of its code, Steem enables SMTs
to support static (versus dynamic) crypto-economic properties that can be tuned after
the token’s launch without each change potentially harming their token holders. The
purposeful delineation between economic properties that should be static versus dynamic
makes the necessary token audits for safety simple and inexpensive to accomplish.
To elucidate this issue, imagine someone is offering you 20% of their currency in exchange
for $100 USD. You will have additional questions for the seller - essentially questions to
audit tertiary realities of the deal, such as: “does the seller maintain a right to print more
currency and therefore dilute me?” In SMTs, holders of SMTs will be able to rely on the
core economics of the SMTs they purchase due to static nature of the SMTs economic
properties - such as emissions or inflation rates, which cannot be changed by the issuer
after launch. Therefore, there can be no unexpected new currency emissions to harm
the consumer. In application-general, open-programmability blockchain protocols, such
as Ethereum and Tezos, there can be no such platform-spanning design principles and
reliabilities that protect consumer safety.
SMTs on Steem have Aligned Proof-of-Brain Incentives with the
Core Token
Unlike STEEM, core tokens (such as ETH) that do not carry Proof-of-Brain content
rewards, cannot offer monetization, primed active user-base, shared influence, and bootstrapping benefits to new SMT communities. STEEM, on the other hand, is able to lend
its reward pool features and primed-user base to new networks, to help them bootstrap,
market, and become successful independent clusters of participants on the network. Conversely, some entrepreneurs will identify and choose a strategy to employ SMTs largely
independent from STEEM, and like ERC20 to Ethereum, SMTs can run while only having STEEM run in the background to calculate the necessary bandwidth for transaction
costs.
54
SMTs on Steem Have Transaction Pricing that Contributes to a
Quality User Experience
Whether operating with bandwidth rate limiting, or outright fees, no general purpose
blockchain will price transactions effectively for more than a small fraction of its applications, and SMTs would have reduced user experience (UX) on application-general
blockchains (such as Ethereum) as a result. The clear example is that on blockchains
such as Ethereum, there are outright fees for all transactions, however, no content publisher would expect users to pay fees to leave comments or likes on their articles. With
SMTs on Ethereum, those fees would be required, which makes Ethereum a non-starter
as an SMT platform.
Unlike Ethereum, some open-programmability blockchains of the future may use bandwidth rate limiting as transaction costs, however, bandwidth rate limiting requires fine
tuning to meet the UX requirements of specific applications. As an example, in Steem,
bandwidth rate limiting is specifically tailored to support content applications and their
user interactions by leveraging bandwidth rights according to two objects: amount of
token ownership, and account ownership - and it’s taken over a year of production-level
research to refine the optimal bandwidth allowances to each. In general purpose, openprogrammability platforms, the burden and the need for accurate pricing may hinder the
ability for applications to have their users’ actions priced appropriately, and the problem may be exacerbated as a greater myriad of potential application experiments come
to exist, stretching and sharing the blockchain’s resources. Therefore, blockchains that
support native application-specificity may yield more suitable transaction pricing, as it
pertains to the UX with tokens in related applications.
SMTs Benefit from a Blockchain that has Scaling Processes Programmed to a Specialized Set of Applications
In blockchain scaling there are cutting-edge concepts of “sharding” (originated by Vitalik Buterin and the Ethereum project) and “multi-threaded parallelism” (originated by
Michael Vandeberg of Steem) that refer to how blockchains may scale by allowing multiple
operations to occur at once. General purpose platforms (such as Ethereum) are a great
test bed for these approaches to scaling, however, a platform that takes advantage of all
the product-market fit discovered by Ethereum, that then applies it to a more specialized,
iterative-upgrading model, such as Steem, can scale its processes more effectively to meet
the demand discovered by that product-market fit.
Looking to the 90s and early 00s for analogy, when the computer science world started
writing code specifically optimized for GPUs, the boundary pushing for greater scale occurred through FPGAs: field programmable gate arrays, which are chips that allow the
programmability of the set of logic gates into the form of any conceivable circuit, allowing for effectively a prototype ASIC (albeit with higher power consumption). This is not
quite the same performance per watt as an ASIC, but orders of magnitude faster than
a CPU for particular tasks. As these platforms move to more and more generalizations,
such as the idea that any contract may call on any other contract, they will move further away from ability to optimize for scale, as contracts that call on all other contracts
can reduce the capacity for multi-parallel processing to single-core processing. By analogy, like CPUs do not optimize better than GPUs, platforms like Ethereum, GEOS, and
Tezos do not optimize better than Turing-incomplete application-specific blockchains like
55
Steem. These CPU-like blockchains will be bottlenecked by unpredictable processing requirements, while the ultimate blockchain platforms will be specially-designed, like Steem,
and will scale by optimizing in the way FPGAs were optimized for parallel algorithms.
SMTs Benefit from a Blockchain with Content Management System (CMS) Primitives
Unlike application-general blockchains, such as Ethereum, that inherently avoid
application-specific primitives at the core of the protocol, Steem offers a structured
public content database for storing plain text and generic structured data in tandem with
content primitives that developers can build from: Account Names, Posts, Comments,
Votes and Account Balance. These primitives benefit the blockchain-based applications
by helping to establish application-interoperability and rapid developer on-boarding.
Without these primitives, second order databases need to be structured specifically for
a blockchain-based application, which may give rise to many second-order applicationspecific databases competing with each other. The rise of multiple second layer content
databases splits the potential network effect for the blockchain as a content management
system (CMS), and reduces the potential for application-interoperability, which provides
consumer safety benefits by allowing end users to move fluidly from one blockchain-based
application to another.
Increasing Market Demand for STEEM with SMTs and
Implicit Value Drivers rather than Fees
There are several new value drivers to STEEM with the creation of SMTs.
STEEM Purchased for Transaction Bandwidth Enables Maximally
Profitable Participation across SMTs
With the advent of SMTs, there is growing demand for users to hold STEEM, because
users need to increasingly hold STEEM in order to participate, consume, and use Steem
services at a rate maximally commensurate with their growing potentials in respective
SMT ecosystems. Put simply, as power users are growing their earning potential in SMT
communities, they need more STEEM to achieve the bandwidth allowance needed to
perform at their highest possible rate of return in SMT ecosystems. At an application
level, the demand for bandwidth may be satisfied by users or by businesses, which can
delegate surplus bandwidth to their users.
STEEM Supply is Locked into Liquidity Pools by Automated Market Makers
Each SMT that leverages Automated Market Makers augments the ratio of demand for
STEEM to available supply of STEEM. The effect of the Automated Market Maker to
STEEM is that each Automated Market Maker represents a permanent holding pool for
STEEM, which represents a decrease in available supply. Given demand were to stay
56
equal, the price of STEEM is caused to rise with the advent of each new Automated
Market Maker.
STEEM and SMT Demand Increases with Advent of New Powers
of Influence
From a potential utility perspective, demand for STEEM increases as each SMT is created
with Influence Sharing for Steem Power over a SMT’s rewards pool. The advent of each
trace of Steem Power-based Shared Influence over an SMT’s Reward Pool gives new rights
and usage to STEEM, which in turn drives demand for STEEM. These rights can also
be granted from SMT to SMT, and the flow of value follows an identical pattern.
STEEM Demand Increases with Proliferation of SMT ICOs
At a platform level, other cause for demand may include exclusive financing opportunities,
such as ICOs, which attract new capital into ecosystems, first flowing into the base asset,
STEEM, and then flowing into SMTs. Increased capital in the ecosystem due to ICOs
always presents an opportunity for net positive capital retained in STEEM, and at worst,
a wash on the value of the base asset, where all of the STEEM is sold by the organization
making the offering. The example of the worst case scenario is that an ICO occurs and
$100 USD buys STEEM to buy the ICO’d SMT, then 100% of the STEEM received by the
ICO is sold for USD - and no explicit net effect related to the value of STEEM. However,
even when the net effect contribution of an ICO to the value of STEEM is apparently
zero, it is an implicit net benefit in terms of attention received by STEEM and the Steem
ecosystem, if we consider all new attention valuable. Further, it is reasonable to expect,
based on the behavior of ICOs in Ethereum, that the majority of the STEEM received
by the ICO’ing organization will continue to be held on a speculative or promissory basis,
therefore creating holding value.
Steem: The World’s Advertising Network
Along with these new value creating mechanisms, it is imperative to recognize the original
value created for STEEM as an implicit attention and advertising network that now
applies to all SMTs that utilize Proof-of-Brain rewards. Smart Media Tokens, such as
STEEM, have inherent curation properties, such as their Rewards Pools, that give them
reliability and credentials as an implicit advertising network. The Rewards Pool in SMTs
demands that fully-SMT-integrated interfaces, such as steemit.com, respect the pending
SMT payouts on posts and then rank these posts from highest to lowest pending payout
in pages often referred to as “Trending” - such that the posts can be audited by the
community of SMT holders. The effect of this, which applies equally to STEEM as other
SMTs, is a sorted “Trending” page that users (bloggers, vloggers, advertisers) can reliably
use to evaluate the potential returns on buying higher placement on the page to attain
more attention, and then these participants make decisions to buy or rent STEEM and
SMTs to promote content. Through this process, as advertisers choose to buy and
rent STEEM/SMTs to gain exposure, demand for STEEM/SMTs increases. These value
driving properties can be described in a way similar to “Ethereum: the world computer”,
but instead as “Steem: The world’s advertising network.”
57
Steem Ecosystem Support for SMTs
Integrating SMTs into Websites and Apps
APIs and Documentation
To be continuously updated for SMTs. Current Steem APIs exist here: http://steem.
readthedocs.io/en/latest/index.html and https://steemit.github.io/steemit-docs/
Shared Tools for Account Creation, Key Signing, and Wallet Functions
Several shared tools exist to support applications that wish to outsource signup, transaction signing, and wallet functions - such as SteemConnect. SteemConnect enables
applications to support SMTs while the applications are backed by entrepreneurs who
may have little to no cryptocurrency experience.
Conclusion
Through a combination of specialized designs for open asset-issuance, bandwidth rate limiting as transaction costs, permanent-availability of content, real-time transaction speeds,
autonomous distribution of tokens, decentralized exchange, automated market making
and ICO contracts, Steem offers the premier token protocol for publishers across the
internet.
References
[1] Steemit, Inc., 2017. Steem Bluepaper. A protocol for bringing smart, social currency to publishers and content businesses across the internet. (https://www.steem.io/
steem-bluepaper.pdf)
[2] Eyal Hertzog, Guy Benartzi & Galia Benartzi, 2017. Bancor Protocol. Continuous
Liquidity and Asynchronous Price Discovery for Tokens through their Smart Contracts.
(https://www.bancor.network/static/bancor_protocol_whitepaper_en.pdf)
Appendix
Implementation Notes
Here is a timeline / state diagram of the events in an SMT launch:
58
Figure 10: Timeline of SMT Launch
SMT naming standards
• An SMT name should consist of 3-10 uppercase ASCII letters (A-Z).
• An SMT name should not equal STEEM, SBD or VESTS.
Asset directory standards
A directory maps each NAI to one of the following states:
Listed
Deprecated
Unlisted
Blacklisted
Each possible asset name is mapped to one of the following states:
Free
Reserved
59
A Listed or Deprecated NAI has an associated name, which should be listed as Reserved
in the mapping.
UIs may provide asset directory union functionality to augment directories by combining
multiple asset directories into a single asset directory. Asset directory union should use
the following algorithm to resolve situations where an NAI is listed differently by different
directories:
• (1) If the NAI is Blacklisted in any component directory, return Blacklisted.
• (2) If the NAI is Listed or Deprecated in multiple component directories, and
all of the component directories do not agree on the associated name, return
Unlisted.
• (3) If the NAI is Listed in at least one component directory, return Listed.
• (4) If the NAI is Deprecated in at least one component directory, return
Deprecated.
• (5) Return Unlisted.
Likewise, here are the rules for resolving names listed differently by different directories:
• (1) If the name is Reserved in any component directory, return Reserved.
• (2) Return Free.
A dynamic directory (based on a URL or blockchain account) should not be cached more
than 5 minutes.
UI guidelines for SMT names
• A UI may, but need not, have a default asset directory.
• A UI may choose to hide unlisted NAIs.
• A UI should allow users to override or augment the UIs defaults with their own
asset directories.
• A UI should reconsider hiding unlisted NAIs in which the user has actively transacted.
Operational guidelines for asset directories
• An asset directory should not confuse users by setting a well-known NAI to refer to
a different name, or setting a well-known name to refer to a different NAI.
• An asset directory should make the process for listing clear to both SMT creators
seeking to add their asset to the directory, and UI developers considering adding
the directory to their UI.
Asset directory formats
URL and file-based asset directories will be a JSON format. The details will be developed concurrently with the implementation. Blockchain-based asset directories will use
a custom JSON operation. Again, the details will be developed concurrently with the
implementation.
60
Unit Tests
The details of the unit tests will be developed concurrently with the implementation.
61
[GETH] GETH 명령어 모음 (0) | 2018.05.10 |
---|---|
크립토 키티 소스 (0) | 2018.04.30 |
블록체인 공부 모음 링크 (0) | 2018.04.13 |
블록체인 관련 참조 사이트 모음 (0) | 2018.02.12 |
시작하기전 기본 설정
DB는 AWS RDS 이용합니다.
파일 서버는 S3 이용합니다.
aws ec2는 콘솔에서 설정가능
참고 링크 :
https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-16-04
ufw allow 80
일반적으로 우분투에 설치시 최신버전을 설치를 안한다. 그래서 저장소 키를 가져와서 직접 최신걸로 업데이트를 할 수 있다.
참고내용 :
https://websiteforstudents.com/install-nginx-latest-version-ubuntu-16-10-17-04/
https://bjornjohansen.no/install-latest-version-of-nginx-on-ubuntu
사인키 등록
// get nginx key
curl -O https://nginx.org/keys/nginx_signing.key
// install
apt-key add nginx_signing.key
$ sudo echo -e "deb https://nginx.org/packages/mainline/ubuntu/ `lsb_release -cs` nginx\ndeb-src https://nginx.org/packages/mainline/ubuntu/ `lsb_release -cs` nginx" > /etc/apt/sources.list.d/nginx.list
sudo apt-get update
sudo apt-get install nginx
업그레이드시
sudo apt-get dist-upgrade
버전 체크
nginx -v
-> nginx version: nginx/1.10.3 (Ubuntu)
명령어 모음
// 시작
$ sudo service nginx start
$ sudo systemctl start nginx
$ sudo /etc/init.d/nginx start
// 재시작
$ sudo service nginx restart
$ sudo systemctl restart nginx
$ sudo /etc/init.d/nginx restart
// 중지
$ sudo service nginx stop
$ sudo systemctl stop nginx
$ sudo /etc/init.d/nginx stop
// 상태
$ sudo service nginx status
$ sudo systemctl status nginx
// 설정 reload
$ sudo service nginx reload
$ sudo systemctl reload nginx
$ sudo nginx -s reload
FTP 설치
sudo apt-get install vsftpd
설정페이지
nano /etc/vsftpd.conf
기본 설정
# chroot 적용
# 아래와 같은 설정을 할 경우 사용자들은 자신의 계정에서 상위 디렉토리로 이동할수 없게된다.
chroot_local_user=YES
# 수정 가능
write_enable=YES
*chroot_local_user=YES 주석을 풀면 아래와 같은 에러가 발생하는 경우가 있음
500 OOPS: vsftpd: refusing to run with writable root inside chroot()
이러한 경우 vsftpd.conf 파일에
allow_writeable_chroot=YES 을 추가 저장
출처: http://nahosung.tistory.com/40 [nahos]
그리고 유저를 추가
adduser test
//비번 입력창 나오면 알아서 입력
FTP 권한 및 옵션 확인
echo 'allow_writeable_chroot=YES' >> /etc/vsftpd.conf //YES로 변경
chmod 777 /home/{유저이름}
sudo /etc/init.d/vsftpd restart //재가동
기본 설정
vsftpd.conf 와 관련된 내용
1. FTP의 포트를 바꾸고 싶으면 어떻게 해야 할까?
listen=YES 라고 써 있는 부분 아래에
listen_port=21022 (원하는 포트번호로 넣어줌) 적어준다
2. 비계정(guest)의 접속을 허용하게 하려면
anonymous_enabled=NO 를 YES로 변경해 준다.
3. 업로드 가능하게 하려면?
write_enable=YES에 걸려있는 주석을 제거해야 한다.
4. 파일 업로드 했을 때 파일의 권한을 설정하려면 어떻게 해야 하나?
local_umask=022 (마스킹 처리가 되기 때문에 022로 하면 해당파일은 755 라고 설정된다)에 걸려있는 주석을 제거한다.
5. 계정사용자가 상위디렉토리(root 디렉토리)에 접근하는 것을 허용하려면?
chroot_local_user=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd.chroot_list
최초에 설치하면 주석이 걸려있는데 이것은 상위 디렉토리로 이동하는것을 막기 위함이다.
이 주석을 제거하고 난 후에 chroot_list_file 항목에 적혀있는 경로로 파일을 만들고 그 안에 계정이름을 적으면 상위로 이동하 것이 허용된다.
$sudo vi /etc/vsftpd.chroot_list
user1
user2
이렇게 하면 user1과 user2는 상위 디렉토리로 이동이 가능해진다.
6. root 계접 접속을 허용하려면?
$sudo vi /etc/ftpusers
root 계정에 앞에 주석처리 해준다 (또는 삭제)
노드 설치
nvm 으로 설치
https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-16-04
또는 정식 설치
https://nodejs.org/en/download/package-manager/
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
or
curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -
sudo apt-get install -y nodejs
네이티브 애드온 설치
sudo apt-get install -y build-essential
노드를 백그라운드에서 돌릴수 있는 PM2 설치
sudo npm install pm2 -g
pm2 startup systemd //서버 재시동시 자동으로 올릴수 있도록
pm2 start ./bin/www //express app.js 실행
그리고 pm2을 통해서 외부에서 모니터링 설치
https://app.keymetrics.io 추천 ( 1개까진 무료 )
//create bucket 후
pm2 link b3o4z****** rxio4******** //메뉴얼대로 진행
이제 ngnix에서 내부 서버로 포워딩해야함
참고 사이트 :
vi /etc/nginx/sites-available/default
..........
# / 루트로 오는 경우 3000번으로 포워딩처리
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
.........
혹시 site-avavailable
폴더가 안만들어진 경우...
cd /etc/nginx/sites-enabled
//없는 폴더일 경우
//1. sites-enabled 폴더를 만든다.
//2. default(사이트이름가능) 만든다.
vi /etc/nginx/sites-enabled/default
///etc/nginx/sites-enabled/default 내용
server {
listen 80;
server_name {도메인주소};
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
nginx sites-enabled에 symlink 생성하기
이제sites-available
에 있는 파일들에 대해서sites-enabled
에 symlink를 추가합시다
mkdir /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/[아까 만든 default] /etc/nginx/sites-enabled/
그리고 문법에 이상없는 지 체크
nginx -t
-> nginx: configuration file /etc/nginx/nginx.conf test is successful
그럼 ngnix 재시작
// 재시작은 아래에 있는 명령어 중 하나로 실행하시면 됩니다.
$ sudo service nginx restart
$ sudo systemctl restart nginx
$ sudo /etc/init.d/nginx restart
-> [ ok ] Restarting nginx (via systemctl): nginx.service.
그러면 포스트맨이나 웹에서 접속 테스트를 해본다.
도메인은 router 53에서 A 레코드로 하나 추가함.
그리고 value 에 해당 아이피를 입력함
시간이 다소 걸릴 수 있음
도메인 변경후 접속 테스트를 해서 통과가 되는 경우 HTTPS 설치로 넘어가자.
HTTPS 설치 - letsencrypt + certbot 이용
으로 가서 맞는 환경을 선택...
우선 우분투 버전 체크
lsb_release -a
--------------------------
Distributor ID: Ubuntu
Description: Ubuntu 16.04.3 LTS
Release: 16.04
Codename: xenial
certbot 환경선택 에서 우분투 16.04 + Nginx 선택
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx
그리고 certbot 인증 절차를 시작한다.
sudo certbot --nginx
// 이메일 등록하라고 함
// 기타 몇가지 설정하고 도메인이 꼭 설정이 되어 있어야 함
// 마지막은 http로 온걸 https로 리다이렉트할꺼냐 물어봄
그리고 letencrypt
이용시 3개월마다 갱신해야 한다. 이걸 자동 등록할수 있다.
sudo certbot renew --dry-run
그리고 https
로 접속 확인해본다.
마지막으로 제대로 되었는지 SSL 등급 테스트
를 해보자
그럼 이제 완료가 되었다. 터미널 끄고 개발하면 된다..ㅠㅠ
추가적으로 nginx 에 파일업로드시 용량부족이 뜨는 경우가 있다.
sudo nano /etc/nginx/nginx.conf
.............
http 부분에
client_max_body_size 2M; //2M 부분에 원하는 용량 적으면 된다.
.............
sudo /etc/init.d/nginx restart
Spring Boot + Pm2 배포하기(Deploy) (0) | 2018.08.02 |
---|
2장에서는 주된 설명은 배열과 연결된 리스트에 대한 비교 설명이 있었다.
배열
은 검색이 빠른 반면 삽입이 느리다는 것
연결된 리스트
의 경우 삽입이 빠르지만 검색부분에선 배열보다 느리다는 장단점이 있다.
2번째 알고리즘 문제이다.
만약 주어진 배열이 있으면 그 배열을 정렬하고자 할 때 선택정렬을 사용할 수 있다.
기본 흐름은 다음과 같다. (순차적 정렬시)
Python3 으로 구현시 다음과 같다.
def findSmallest(arr):
smallest = arr[0]
smallest_index = 0
for i in range(1, len(arr)):
print(">> {0},{1},{2}".format(smallest, smallest_index, arr[i]))
if arr[i] < smallest:
smallest = arr[i]
smallest_index = i
return smallest_index
def selectionSort(arr):
newArr = []
for i in range(len(arr)):
print("arr => ", arr)
smallest = findSmallest(arr)
print("smallest => ", smallest)
newArr.append(arr.pop(smallest)) # pop은 꺼낸 후 제거된다.
return newArr
print(selectionSort([5, 3, 6, 2, 10]))
try no | idx | smallest | smallest_index | arr[index] |
---|---|---|---|---|
0 | 0 | 5 | 0 | 3 |
0 | 1 | 3 | 1 | 6 |
0 | 2 | 3 | 1 | 2 |
0 | 3 | 2 ( 제일 작은값 리턴) | 3 | 10 |
2를 arr 에서 pop (2를 꺼내는 동시에 제거) 하고 다시 위의 반복문을 돌린다.
위 결과값 출력은 다음과 같다.
arr => [5, 3, 6, 2, 10]
>> 5,0,3
>> 3,1,6
>> 3,1,2
>> 2,3,10
smallest => 3
arr => [5, 3, 6, 10]
>> 5,0,3
>> 3,1,6
>> 3,1,10
smallest => 1
arr => [5, 6, 10]
>> 5,0,6
>> 5,0,10
smallest => 0
arr => [6, 10]
>> 6,0,10
smallest => 0
arr => [10]
smallest => 0
[2, 3, 5, 6, 10] <== 최종 정렬된 값
다트 버전의 경우는 온라인 다트 편집기 에서 테스팅 가능하다.
void main() { print("result => ${selectionSort([5, 3, 6, 2, 10])}");}
List<int> selectionSort(List<int> arr) { var newArr = []; var length = arr.length; for(var i = 0; i < length; i++) { var smallestIndex = findSmallestIndex(arr); newArr.add(arr[smallestIndex]); arr.removeAt(smallestIndex); } return newArr;}
int findSmallestIndex(List<int> arr) { int smallest = arr[0]; int smallestIdx = 0;
var newArr = arr.sublist(1,arr.length);
newArr.forEach((item) { if(item < smallest) { smallest = item; smallestIdx = arr.indexOf(item); } }); return smallestIdx;}
................result => [2, 3, 5, 6, 10]
빅오 표기법으로는 O(n^2) 이라고 한다.
[ch1]이진탐색 (Binary Search) [파이썬,다트] (0) | 2018.04.06 |
---|
시나리오
1~1000개 숫자 의 배열에서 특정 숫자만 뽑아내고 싶을 경우 어떻게 해야 될까?
예를 들어 100의 중간 50부터 시작되는 함수로 시작된다
4번만에 답을 찾은 경우이다. 그럼 log2 16 = 4 이라고 표시한다.
즉 로그는 거듭제곱의 반대말이다.
지수표현이 잘 안됨..
위의 경우에는 정렬된 배열에 한해 해당되는 내용이었다. 하지만 실무에서는 보통 정렬이 안되어있는 경우가 많은 편이다.
그럼 어떻게 구현이 될까? 파이썬으로 시작해보자.
우선 먼저 배열 전체 길이를 설정하자.
low = 0
high = len(list) - 1
그리고 가운데를 딱 나누어서 원소를 확인해보자.
mid = (low + high) / 2 //반으로 나눈값 설정
guess = list(mid) //리스트의 반이 무엇인지 예측
추측한 값이 너무 작으면 low 값을 다음과 같이 변경 처리
if guess < item:
low = mid + 1
또 큰 경우 high 값을 설정
if guess > item
hight = mid -1
표로 정리해보면 다음과 같다.
idx | low | high | mid | guess | result | result |
---|---|---|---|---|---|---|
0 | 0 | 99 | 49 (50 - 1) | 50 | 50 < 57 | 크다 |
1 | 50 | 99 | 74 (75 - 1) | 75 | 75 > 57 | 작다 |
2 | 50 | 73 | 61 (62 -1) | 62 | 62 > 57 | 작다 |
3 | 50 | 60 | 55 (56-1) | 56 | 56 < 57 | 크다 |
4 | 56 | 60 | 58 | 59 | 59 >57 | 작다 |
5 | 56 | 57 | 56 | 57 | 57 == 57 | 정답 |
def binary_search(list, item):
cnt = 0
low = 0
high = len(list) - 1
print("찾는 문자 : ", item)
while low <= high:
mid = (low + high) // 2
guess = list[mid]
print("번호:",cnt, " low:", low, " high:", high, " mid:", mid, " guess:", guess )
if guess == item:
print("in ", guess, "==", item, " return ", mid)
return mid
if guess > item: #item보다 guess 가 큰경ㅇ
print("in ", guess, ">", item, " 작다")
high = mid - 1
else:
print("in ", guess, "<", item, " 크다")
low = mid + 1
cnt=cnt+1
return None #아이템이 리스트에 없음
my_list = list(range(1,101))
print(binary_search(my_list, 57))
결과값
찾는 문자 : 57
번호: 0 low: 0 high: 99 mid: 49 guess: 50
in 50 < 57 크다
번호: 1 low: 50 high: 99 mid: 74 guess: 75
in 75 > 57 작다
번호: 2 low: 50 high: 73 mid: 61 guess: 62
in 62 > 57 작다
번호: 3 low: 50 high: 60 mid: 55 guess: 56
in 56 < 57 크다
번호: 4 low: 56 high: 60 mid: 58 guess: 59
in 59 > 57 작다
번호: 5 low: 56 high: 57 mid: 56 guess: 57
in 57 == 57 return 56
56
void main() { //1~100까지 생성 int targetNum = 57; var list = new List<int>.generate(100, (i) => i+1); print("final result = ${binary_search(list, targetNum)}");}
int binary_search(List<int> list, int item) { //초기화 int low = 0; //리스트 갯수 - 1 int high = list.length - 1;
//while low <= high while(low <= high) { int mid = ((low + high) / 2).floor(); int guess = list[mid]; print("$low,$high,$mid,$guess"); if(guess == item) { return mid; }else if(guess > item) { high = mid -1; }else{ low = mid + 1; } }
return 0;
}
결과
0,99,49,5050,99,74,7550,73,61,6250,60,55,5656,60,58,5956,57,56,57final result = 56
[ch2] 선택정렬 (Selection Sort) (0) | 2018.04.08 |
---|
안녕하세요. 수지아빠입니다.
요즘 알고리즘이 아주 핫합니다. 하지만 오랜 시간 개발을 해도 알고리즘을 따로 공부하지 않으면 늘 부족함을 느끼기 마련입니다.
그리고 바쁜 직장인 일정에서 스터디도 하긴 시간도 부족하고 늘어지기 쉽죠.
그래서 읽기 편한 알고리즘 왕초보 책 한권 우선 선정해서 다 같이 떼는 걸로 진행하는 온라인 스터디를 시작할 예정입니다.
인증도 같이 해야 되니 꼭 공부하실 분만 오세요.
인증방법은 챕터별 또는 공부한 내용에 대해서 GitHub 또는 자신의 블로그에 정리해서
올리시고 그 링크를 공유를 해주셔야 합니다.
정리하는 내용은 공부한 내용이므로 자세하지 않으셔도 됩니다. 자세하고 잘 적으시면 그만큼 더 뿌듯하셔도 됩니다. 멋지십니다. !!
그리고 책을 한번 다 끝까지 다 본 경우 졸업생 홍길동 이런식으로 계속 방에 계셔도 되거나 졸업하셔서 나가시면 됩니다.
http://www.yes24.com/24/Goods/37885448?Acode=101
공부하실분만 모십니다. 오랫동안 공부안하시면 강제 조퇴당할수 있습니다.
밑의 사진 클릭시 입장가능합니다.
Truffle 튜터리얼 중 펫샵을 활용해서 프론트 까지 적용해서 어떤식으로 웹에서 연동되는지 살펴보자.
아래 주소에 있는 내용은 깔끔하게 정리되어 있다. 따라해보는 걸 추천한다.
여기 글은 펫샵에 사용되는 소스를 분석해보자 한다.
이더리움 플랫폼 위 DAPP
개발 프레임워크 중 하나인 truffle
사용시 개발시 순서는 다음과 같다.
아래 이미지는 결과 화면이다. Adopt 버튼을 통해서 success 이 제대로 이루어 지는 지 보면 된다.
MetaMask 를 통해서 창이 열리면서 Submit를 클릭해준다.
최종적으로 채택이 된 것을 웹에서 확인해볼 수 있다. ( 새로고침을 해야한다. )
결과물은 따라하다보면 쉽게 볼 수 있을것이다. 여기서 특이한 점은
MetaMask
를 통한Custom RPC
로ganache private network
로 접속해서 진행
스마트 계약을 테스팅시 솔리디티 언어로 테스팅 하는 방법
우선 Adoption.sol 파일을 훝어보자.
pragma solidity ^0.4.17;
contract Adoption {
address[16] public adopters;
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
function getAdopters() public view returns (address[16]) {
return adopters;
}
}
주로 스마트 계약을 테스팅시 자바스크립트(mocha)로 계약 함수를 신뢰성을 테스트한다. 하지만 여기에선 바로 솔리디티 언어로 테스팅하는 걸 보여준다.
pragma solidity ^0.4.17;
//다양한 테스팅 도구 제공
import "truffle/Assert.sol";
//이미 서버에 배포된 계약 주소를 가져온다.
import "truffle/DeployedAddresses.sol";
//계약 소스를 가져온다.
import "../contracts/Adoption.sol";
contract TestAdoption {
//Adoption 초기화
Adoption adoption = Adoption(DeployedAddresses.Adoption());
//Pet에 Adopt를 할수 있는지 테스트
function testUserCanAdoptPet() public {
//8번 아이디를 adopt 함수 실행
uint returnedId = adoption.adopt(8);
uint expected = 8;
//값이 제대로 가져오는 지 체크
Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recoreded");
}
//아이디를 통해 Adopter 주소를 가져오는 걸 테스팅
function testGetAdopterAddressByPetId() public {
//트랙잭션을 할 예정이므로 예상 값을 this로 설정 가능하다. (펫 구매한 사람 주소)
address expected = this;
//adopters 변수 설정되면 public getter 가 되므로 거기 위치에 8의 값을 가져와서 테스팅해본다.
address adopter = adoption.adopters(8);
//값 맞게 들어오는지 체크
Assert.equal(adopter, expected, "Owner of pet Id 8 should be recored");
}
function testGetAdopterAddressByPetIdInArray() public {
//트랙잭션을 할 예정이므로 예상 값을 this로 설정 가능하다. (펫 구매한 사람 주소)
address expected = this;
//storeage, memory 를 설정해서 실제 물리적으로 저장 할 것인지 내부에서 저장할 건지 정한다.
//adoption 클래스에서 adopter 배열목록을 가져온다.
address[16] memory adopters = adoption.getAdopters();
//가져온 adopter의 8번째가 맞는지 체크
Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
}
}
그리고 다음과 같이 테스팅을 해본다.
> truffle test
or
devlope 모드일 경우 단지 test 만 입력
Using network 'development'.
Compiling ./contracts/Adoption.sol...
Compiling ./test/TestAdoption.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
TestAdoption
✓ testUserCanAdoptPet (91ms)
✓ testGetAdopterAddressByPetId (70ms)
✓ testGetAdopterAddressByPetIdInArray (89ms)
3 passing (670
그리고 웹쪽 프론트에서 이 계약 함수들을 사용하는 내용이다. App.js
에서 볼 수 있다.
주석을 달아봄으로써 해당 기능을 알아볼 예정이다.
App = {
web3Provider: null,
contracts: {},
//초기화
init: function() {
//펫 배열을 가져와서 리스트를 만든다.
$.getJSON("../pets.json", function(data) {
var petsRow = $("#petsRow");
var petTemplate = $("#petTemplate");
for (i = 0; i < data.length; i++) {
petTemplate.find(".panel-title").text(data[i].name);
petTemplate.find("img").attr("src", data[i].picture);
petTemplate.find(".pet-breed").text(data[i].breed);
petTemplate.find(".pet-age").text(data[i].age);
petTemplate.find(".pet-location").text(data[i].location);
petTemplate.find(".btn-adopt").attr("data-id", data[i].id);
petsRow.append(petTemplate.html());
}
});
//지갑설정 함수를 실행한다.
return App.initWeb3();
},
initWeb3: function() {
// Is there an injected web3 instance?
if (typeof web3 !== "undefined") {
//Metamask 가 실행시 현 지갑을 리턴한다.
App.web3Provider = web3.currentProvider;
} else {
//만약 지정된 지갑이 없는 경우 미리 설정된 Ganeche 지갑을 리턴한다.
App.web3Provider = new Web3.providers.HttpProvider(
"http://localhost:7545"
);
}
web3 = new Web3(App.web3Provider);
return App.initContract();
},
//계약 초기화
initContract: function() {
/*
Adoption.JSON 형태
{
"contractName": "Adoption",
"abi": [
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "adopters",
...............
*/
//Adoption 은 컴파일 시 나온 ABI JSON이다 여기에 기본 함수들이 표시가 된다.
//웹에서는 이 ABI JSON을 보고 실행을 할 수 있다.
$.getJSON("Adoption.json", function(data) {
// Get the necessary contract artifact file and instantiate it with truffle-contract
var AdoptionArtifact = data;
//미리 제공된 truffleContract 를 통해 Adoption 인스턴스 생성
App.contracts.Adoption = TruffleContract(AdoptionArtifact);
//지갑 설정
App.contracts.Adoption.setProvider(App.web3Provider);
//
// 이미 채택된 애완 동물이 있는 경우 함수를 호출해서 데이터 확인 후 업데이트 할수 있도록 한다.
return App.markAdopted();
});
return App.bindEvents();
},
//이벤트 바인딩
bindEvents: function() {
$(document).on("click", ".btn-adopt", App.handleAdopt);
},
//채택된 아이템이 있는 경우 버튼을 success 로 변경
markAdopted: function(adopters, account) {
var adoptionInstance;
App.contracts.Adoption.deployed()
.then(function(instance) {
adoptionInstance = instance;
//전체 adopte 배열 주소를 리턴한다.
return adoptionInstance.getAdopters.call();
})
.then(function(adopters) {
//for 문 돌면서 adopte 만큼 버튼을 success 로 바꾸고 비활성화를 시킨다.
for (i = 0; i < adopters.length; i++) {
if (adopters[i] !== "0x0000000000000000000000000000000000000000") {
$(".panel-pet")
.eq(i)
.find("button")
.text("Success")
.attr("disabled", true);
}
}
})
.catch(function(err) {
console.log(err.message);
});
},
//Adopt 버튼 클릭시 adopt 함수 실행하는 함수이다. (트랙잭션이 발생된다.)
handleAdopt: function(event) {
//기본 이벤트 블럭함
event.preventDefault();
//펫 아이디를 id 를 통해 쿼리해옴
var petId = parseInt($(event.target).data("id"));
var adoptionInstance;
//지갑상에 주소를 가져온다.
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
//처음 주소를 가져온다.
var account = accounts[0];
App.contracts.Adoption.deployed()
.then(function(instance) {
adoptionInstance = instance;
//petId, account를 넣어서 adopt 함수를 실행한다.
return adoptionInstance.adopt(petId, { from: account });
})
.then(function(result) {
//완료 된 후 success 버튼으로 변경...잘 안됨..
return App.markAdopted();
})
.catch(function(err) {
console.log(err.message);
});
});
}
};
$(function() {
$(window).load(function() {
//초기화
App.init();
});
});
Truffle을 이용해서 손쉽게 테스팅 가능하며 배포도 쉽게 가능한 걸 볼수 있다. 잘 배워놓으면 도움이 많이 된다고 본다.
Geth를 이용한 dapp 개발 #1 (1) | 2018.04.17 |
---|---|
[DAPP] Truffle로 스마트 계약을 테스팅하고 디버깅해보자. (0) | 2018.03.31 |
[DAPP] 크립토좀비 요약 내용[1 ~ 6장] (2) | 2018.03.25 |
[DAPP] ERC20 토큰을 만들어서 배포까지 (7) | 2018.03.21 |
[DAPP]예제로 배우는 DAPP 개발편 - 복권편 (3) | 2018.03.19 |
Truffle
기초 - 계약 디버깅과 테스팅Truffle
디버깅 및 테스팅 하는 방법을 알아보자.
Truffle 은 DAPP 개발을 편하게 해주는 프레임워크이다. 테스팅 및 컴파일, 배포까지 쉽게 해준다.
테스팅은 Ganache 를 이용한다. Ganache 은 개발모드에서 가상으로 테스팅 및 배포까지 하게 해주는 프레임워크
다른 웹에서 쉽게 테스팅을 할시 http://remix.ethereum.org/ 에서 쉽게 가능하다.
그리고 truffle
에서도 지원한다.
우선 작업 할 폴더를 구성하자.
mkdif simple-contract
cd simple-contract
truffle을 초기화를 해서 기본 구성을 만들 수 있다.
truffle init
기본 구성은 위와 같이 나온다.
build
: 컴파일시 나오는 스마트 계약 빌드 파일이다. ABI
파일이 나오는 걸로 보인다.contracts
: 스마트 계약 파일들을 여기에서 보관한다.migrations
: 이미 배폰 계약에 대해서 버전링을 하는 것이다. 자세한 내용은 여기를 참고하기 바란다.test
: 계약을 테스팅 할 수 있다.위에보이는 truffle.js
등은 서버설정 및 기타 설정이 가능하다.
초기화를 진행 한 후 스마트 계약(.sol) 을 만들어 보자.
여기에서는 visual stuio code
를 사용할 것이다.
플러그인으로는 solidity
설치해서 재 시작한다.
pragma solidity ^0.4.17;
contract SimpleStorage {
uint myVariable;
function set(uint x) public {
myVariable = x;
}
function get() constant public returns (uint) {
return myVariable;
}
}
두가지 간단한 함수들이 있다. Set, Get
등이다. 변수를 넣고 가져오는 계약이다.
이제 /migrations
폴더에 배포함수를 넣어보자. 이름은 2_deploy_contracts.js 으로 하자.
var SimpleStorage = artifacts.require("SimpleStorage");
module.exports = function(deployer) {
deployer.deploy(SimpleStorage);
};
그리고 컴파일을 해본다.
> truffle compile
...............
Compiling .\contracts\Store.sol...
Writing artifacts to .\build\contracts
컴파일 후에 build 폴더에 계약 관련 파일이 생성되었다.
실서버에 배포를 하기 전에 제대로 솔리디티에서 함수들의 신뢰성들을 테스팅 할 필요가 있다. Truffle 프레임워크 단에서 편하게 지원해주니 우린 잘 쓰면 된다.
그럼 개발 모드
로 들어가본다.
> truffle develop
Connected to existing Truffle Develop session at http://127.0.0.1:9545/
truffle(develop)> //입장
개발 모드로 실행 및 세팅된 상태이다.
그럼 배포를 해보자.
migrate
배포를 한 후 결과값을 보면 다음과 같다.
Replacing Migrations...
... 0x620c6bad3d8c76712a1262667b7944d125a882ac485f7ff73bb20af13b882035
Migrations: 0x8acee021a27779d8e98b9650722676b850b25e11
Saving successful migration to network...
... 0x5ef83bc84690e0a14a30bb02f358bc1ff44f19ab487996f7323f063e95e417f4
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying SimpleStorage...
... 0x79e9162e22395f000597903ac21f461f22240d4ccd22f51f2bcfde726147fb14
SimpleStorage: 0x4e71920b7330515faf5ea0c690f1ad06a85fb60c
Saving successful migration to network...
... 0xa56b2ca521875b0ee0e9ab32d12ce376b0f56930043f4ae55674a0c04510f2ec
Saving artifacts...
그러면 테스팅 서버에 배포가 된 걸로 보인다.
이제 테스팅 코드를 작성해서 실제 값이 잘 넣고 가져오는 지 테스트를 해보자.
/test 폴더내 testing_simplestorage.js
를 만들자.
그리고 내용에 다음과 같이 자바스크립트로 테스팅을 해본다.
테스팅시 Promise 사용하는 방법과 Await 문법을 각각 사용해서 테스팅 해보았다.
자바스크립트 테스팅 관련 자세한 문서 바로가기
var SimpleStorage = artifacts.require("SimpleStorage");
contract("SimpleStorage", function(accounts) {
it("should value is 0", function() {
SimpleStorage.deployed()
.then(function(instance) {
return instance.get.call();
})
.then(function(value) {
assert.equal(value, 0, "value is 0");
})
.catch(e => {
console.log(e);
});
});
it("should value is 4 when i put the 4 about value", async () => {
try {
let instance = await SimpleStorage.deployed();
await instance.set(4); //4를 넣어본다.
let instance2 = await SimpleStorage.deployed();
let value = await instance2.get.call();
assert.equal(value, 4, "value is 4"); // 그리고 결과값이 4로 나오는 지 확인해본다.
} catch (e) {
console.log(e);
}
});
});
콘솔에서 바로 테스팅도 가능하다.
테스팅전에 배포환경을 초기를 해야한다.
> migrate --reset
> test
> truffle develope
SimpleStorage.deployed().then(function(instance){return instance.get.call();}).then(function(value){return value.toNumber()});
> 0
SimpleStorage.deployed().then(function(instance){return instance.set(4);});
SimpleStorage.deployed().then(function(instance){return instance.get.call();}).then(function(value){return value.toNumber()});
> 4 //4로 나오는 걸 확인
자바스크립와 콘솔에서 테스팅시 차이점은 트랙잭션이 일어날시 로그가 좀 다르게 나온다.
// 트렉잭션 발생시켜본다.
SimpleStorage.deployed().then(function(instance){return instance.set(4);});
............................ 결과 출력
{ tx: '0x8a7d3343dd2aaa0438157faae678ca57cc6485825bb4ed2ebefe90609dd268ce',
receipt:
{ transactionHash: '0x8a7d3343dd2aaa0438157faae678ca57cc6485825bb4ed2ebefe90609dd268ce',
transactionIndex: 0,
blockHash: '0xd66fc7ddf829f1d1ee4a655c0b6a7de537748865de39de28b2167d8485fd9e92',
blockNumber: 5,
gasUsed: 41642,
cumulativeGasUsed: 41642,
contractAddress: null,
logs: [],
status: '0x01',
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs: [] }
출력값을 사용자별로 다르게 나올수 있다.
위와 같은 내용은 정상적으로 계약이 이루어 질 경우이다.
솔리디티(.sol) 에서 스마트 계약 개발시 컴파일은 되지만 런타임에서 디버깅을 해서 잘 동작되는지 체크 해볼 필요가 있다.
우선 솔리디티에서 강제적으로 오류를 발생시켜 보자.
계약 내 set 함수를 아래와 같이 바꾼 후 다시 배포를 해보자.
function set(uint x) public {
assert(x == 0); //0일 경우에만 통과를 하도록 한다.
myVariable = x;
}
다시 배포를 해보자.
> migrate --reset
디버깅시 실행시 개발모드에서 로그도 출력을 볼 수 있다.
그러기 위해선 Terminal 을 따로 열어서 확인을 할 수 있다.
+을 클릭해서 터미널을 추가한다.
그리고 개발에서 로그모드로 다시 접속한다.
> truffle develop --log
Connected to existing Truffle Develop session at http://127.0.0.1:9545/
그리고 처음 콘솔로 가서 다시 트렉젝션을 실행 시켜본다.
truffle(develop)> migrate --reset // 배포 초기화
Compiling .\contracts\Store.sol...
Writing artifacts to .\build\contracts
Using network 'develop'.
Running migration: 1_initial_migration.js
Replacing Migrations...
... 0xeab337bfe489629610d34f11044e77fbfc37cfbea4b614f2e12c87ec45ea6f35
Migrations: 0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4
Saving successful migration to network...
... 0x9b51540f5a7d75a8fc920e3e5e4ec66792ba31fd006bd176901f0e6347af2dba
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing SimpleStorage...
... 0x6622a3183323a916e08b9e1d55ec0f848ca75174adae954ebe796e0e66d0ae3f
SimpleStorage: 0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6
Saving successful migration to network...
... 0x69eaa7ed49cc72426706d54c4f52ba70b742ed6910f1223eb0df5f250b4b8ec3
Saving artifacts...
//트렉잭션 실행하기
truffle(develop)> SimpleStorage.deployed().then(function(instance){return instance.set(4);});
실행시 오류내용을 확인할 수 있다.
Error: VM Exception while processing transaction: invalid opcode //0이 아니므로 오류 발생
at XMLHttpRequest._onHttpResponseEnd (C:\Users\tommy\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:509:1)
at XMLHttpRequest._setReadyState (C:\Users\tommy\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:354:1)
at XMLHttpRequestEventTarget.dispatchEvent (C:\Users\tommy\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\xhr2\lib\xhr2.js:64:1)
at XMLHttpRequest.request.onreadystatechange (C:\Users\tommy\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\httpprovider.js:128:1)
at C:\Users\tommy\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\truffle-provider\wrapper.js:134:1
at C:\Users\tommy\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\requestmanager.js:86:1
at Object.InvalidResponse (C:\Users\tommy\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\web3\lib\web3\errors.js:38:1)
그리고 다시 2번째 콘솔로 가서 로그를 확인해본다.
Connected to existing Truffle Develop session at http://127.0.0.1:9545/
..............
develop:testrpc eth_sendTransaction +2s
develop:testrpc +17ms
develop:testrpc Transaction: 0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181 +1ms //이부분이 중요하다.
develop:testrpc Gas usage: 6721975 +0ms
develop:testrpc Block Number: 11 +0ms
develop:testrpc Block Time: Sat Mar 31 2018 09:56:57 GMT+0900 (대한민국 표준시) +1ms
develop:testrpc Runtime Error: invalid opcode +0ms
develop:testrpc +1ms
Transaction: 0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181
트렉잭션 아이디를 이용해서 디버깅이 가능하다. 첫번째 콘솔로 가서 아래와 같이 디버깅을 실행해본다.
debug 0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181
Gathering transaction data...
Addresses affected:
0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6 - SimpleStorage
Commands:
(enter) last command entered (step next)
(o) step over, (i) step into, (u) step out, (n) step next
(;) step instruction, (p) print instruction, (h) print this help, (q) quit
(b) toggle breakpoint, (c) continue until breakpoint
(+) add watch expression (`+:<expr>`), (-) remove watch expression (-:<expr>)
(?) list existing watch expressions
(v) print variables and values, (:) evaluate expression - see `v`
Store.sol:
1: pragma solidity ^0.4.17;
2:
3: contract SimpleStorage {
^^^^^^^^^^^^^^^^^^^^^^^^
debug(develop:0x2cc0d39f...)>
Store.sol:
4: uint myVariable;
5:
6: function set(uint x) public {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
debug(develop:0x2cc0d39f...)>
Store.sol:
5:
6: function set(uint x) public {
7: assert(x == 0); //0일 경우에만 통과를 하도록 한다.
^
debug(develop:0x2cc0d39f...)>
Store.sol:
5:
6: function set(uint x) public {
7: assert(x == 0); //0일 경우에만 통과를 하도록 한다.
^
debug(develop:0x2cc0d39f...)>
Store.sol:
5:
6: function set(uint x) public {
7: assert(x == 0); //0일 경우에만 통과를 하도록 한다.
^^^^^^
debug(develop:0x2cc0d39f...)>
Store.sol:
5:
6: function set(uint x) public {
7: assert(x == 0); //0일 경우에만 통과를 하도록 한다.
^^^^^^^^^^^^^^
엔터를 누르면 차례로 진행되면서 오류가 발생되는 부분에 표시가 된다. 이런식으로 단계별로 디버깅을 할 수 있다.
Remix 를 통해서 디버깅이 쉽게 되는 점도 알아놓으면 도움이 많이 된다.
이 외에에도 다양한 케이스에서 테스팅가능하다.
참조 링크
Geth를 이용한 dapp 개발 #1 (1) | 2018.04.17 |
---|---|
[DAPP] truffle을 활용한 Pet-Shop 튜터리얼 분석해보자. (0) | 2018.04.01 |
[DAPP] 크립토좀비 요약 내용[1 ~ 6장] (2) | 2018.03.25 |
[DAPP] ERC20 토큰을 만들어서 배포까지 (7) | 2018.03.21 |
[DAPP]예제로 배우는 DAPP 개발편 - 복권편 (3) | 2018.03.19 |
혹시 크립토키티 라는 걸 듣어보셨나요? 이더리움상에서 DAPP 형태로 고양이를 랜덤으로 만들어서 거래까지 가능한 플랫폼이다.
현재 매출은 1200억 달러를 넘어섰다고 한다.
관련 기사 모음
이런 플랫폼 게임 개발시 필요한 DAPP 개발 지식을 무료로 튜터리얼식으로 만들어서 공유 하는 곳이 있다.
왼쪽에는 설명이 나오고 오른쪽은 코드 실습을 통해서 하나씩 배워나가는 방식이다.
개발 내용은 DAPP 개발시 필요한 솔리티디 언어로 진행한다.
레벨이 현재 5개로 나누어 (계속 추가될 예정) 지며 각각의 레벨과 함께 게임도 같이 만들어 나가는 재미도 있다.
이 글은 여기 튜터리얼에 나온 레벨별로 요약을 해보았다. 종종 훝어 보기 편하게..^^
배열에는 정적
배열과 동적
배열 두 종류가 있다.
unit[2] fixedArray; //2개의 원소를 담을 수 있는 고정길이 배열
string[5] stringArray; //또다른 고정 배열으로 5개의 스트링을 담을 수 있다.
unit[] dynamicArray; //동적배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다.
구조체 배열을 생성할 수 있다.
Person[] people: //이는 동적 배열로 원소를 계속 추가 가능함
Public
으로 배열을 선언 가능하다. 이 경우는 자동으로 getter
함수가 추가된다.
Person[] public people //public이 중간에 붙는 걸 유의하자
배열에 추가를 하는 경우
Person satoshi = Person(172, "Satoshi");
people.push(satoshi) //이런식으로 추가 가능하다.
함수 선언은 다음과 같다.
function eat(string _name, uint _amount) {
//TODO
}
eat('hambuger',1000); //함수의 실행
private
으로도 생성을 할 수 있다.
function addToArray(uint _number) private {
numbers.push(_number)
}
반환값 반환하는 경우
string greeting = "what's up dog"
function sayHello() public returns (string) { //string 타입으로 리턴합니다.
return greeting;
}
함수제어자의 경우 view
와 pure
를 가지고 있다.
view의 경우 함수가 데이터를 보기만 하고 변경하지 않는 다는 뜻이다.
pure는 어떤 데이터도 접근하지 않는 것을 의미한다. 앱에서 읽지도 않고, 반환값이 함수에 전달된 인자값에 따라서 달라진다.
솔리디티 컴파일러는 이러한 부분에 대해서 무엇을 쓰면 좋을지 경고를 표시해준다.
function sayHello() public view returns (string) {
}
function _multiply(uint a, uint b) private pure returns (uint) {
return a * b;
}
랜덤문자 생성
솔리디티에서 랜덤 발생하는 방법은 SHA3의 한 종류인 keccak256
을 이용하면 된다.
완벽하고 안전한 난수 발생기는 아닌걸 유의하자.
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");
위 글자 하나때문에 값이 완전히 달라지는 걸 유념하자.
형 변환
가끔씩 형병환 할 경우가 있다.
uint a = 5;
uint b = 6;
// a * b가 uint8이 아닌 uint를 반환하기 때문에 에러메세지가 나옴
uint8 c = a * b
// b를 unit8 으로 형변환해서 코드가 제대로 동작하기 위해서...
uint8 c = a * uint8(b);
이벤트
이벤트는 계약이 블록체인 상에서 어떠한 이벤트 발생시 의사소통 하는 방법을 말한다.
계약은 특정이벤트가 발생되는지 리스닝하는 걸 뜻한다.
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
//이벤트를 실행하여 앱에게 add 함수가 실행되었음을 알린다.
IntegersAdded(_x, _y, result);
return result;
}
//////////////// 호출은
MyContract.IntegerAdded(function(error,result){
//결과와 관련된 행동을 취한다.
});
Address
이더리움 블록체인은 은행 계좌와 같은 계정들로 이루어져있음
예를 들어
0x0cE446255506E92DF41614C46F1d6df9Cc969183
이런 주소들을 이용해서 상호간에 이더등을 주고 받을수 있다.
주소는 특정 유저 ( 또는 스마트 컨트렉트)가 소유한다
Mapping
(key, Value) 형식으로 되어 있음
mapping (address => uint) public accountBalance; //유저의 계좌 잔액을 보유 하는 uint 지정가능
mapping (uint => string) userIdToName; //key는 uint 이고 값은 string 이다.
Msg.sender
솔리디티에서 함수 실행은 항상 호출을 해야지 실행이 되는 구조를 가지고 있다. 그래서 누군가가 항상 있다는 게 성립된다.
그래서 실행하는 계정이 msg.sender
로 정의된다.
위에 배운 mapping
을 이용해서 msg.sender
를 어떻게 이용하는지 살펴보자.
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
favoriteNumber[msg.sender] = _myNumber;
}
function getMyNumber() public view returns (uint) {
return favoriteNumber[msg.sender];
}
Require
특정 조건이 아닐 경우 에러 메시지를 발생하고 실행을 멈추는 기능을 말한다.
function checkName(string _name) public returns (string) {
require(keccak256(_name) == keccak256("tommy"));
return "toooooomy!!";
}
상속
하나의 긴 스마트 계약보다는 나누어서 여러개의 스마트 계약으로 만들 수 있다.
아래 계약은 Animal
이 있는 함수를 상속받고 있는 Dog
가 함수를 그대로 사용하고 있다는 것이다.
contract Animal {
function eat() public returns (string) {
return "eat";
}
}
contract Dog is Animal {
function eatAll() public returns (string) {
return eat();
}
}
Import
여러개의 솔리디티 파일(.sol) 을 가져와서 사용할 수 있다.
import './testContract1.sol';
contract testContract2 is testContract1 {
}
Storage vs Memory
Storage
는 블록체인 상에서 영구적으로 저장 하는 변수이다. (전역 변수)
Memory
는 함수내에(메모리) 임시로 저장되는 변수.
솔리디티는 함수 외부에 쓰인 변수에는 자동으로 storage
로 만들어 준다.
사용하는 경우는 전에 배웠던 구조체
와 배열
에서 사용된다.
contract SandwichFactory {
struct Sandwich {
string name;
string status;
}
Sandwich[] sandwiches;
function eatSandwich(uint _index) public {
//Sandwich mySandwich = sandwiches[_index];
//스토리지 변수에 넣으라는 경고 메세지를 볼수 있다(아래 그림 참고)
Sandwich storage mySandwich = sandwiches[_index];
//스토리지에 넣게 된다.
mySandwich.status = "Eaten!";
//Save to storage sandwiches array items
mySandwich.status = "Eaten!";
//스토리즈로 저장됨
Sandwich memory anotherSandwich = sandwiches[_index + 1];
//하지만 복사객체를 만들어서 메모리상으로 만듬
anotherSandwich.status = "Eaten!";
//메모리상으로 복사객체에 넣게 되서 sandwiches 배열과는 무관함
sandwiches[_index + 1] = anotherSandwich;
//다시 스토리지로 저장된다.
}
}
접근 제어자
솔리디티는 public
, private
, internal
, external
등 4가지의 제어자를 제공한다.
internal
의 경우 함수가 정의된 컨트렉트를 상소하는 컨트렉트에서 접근 가능
external
은 함수가 컨트렉트 밖에서만 호출될 수 있다. 즉 같은 컨트렉트안에서 해당 함수를 호출을 제한한다.
internal
와 private
의 경우 성격이 비슷하며
external
은 public
과 성격이 비슷하다고 볼 수 있다.
다른 컨트렉트와 상호작용
이미 올라가져 있는 컨트렉트를 인터페이스
로 가져와서 사용도 가능하다.
pragma solidity ^0.4.17;
contract LuckyNumber {
mapping(address => uint) numbers;
function setNum(uint _num) public {
numbers[msg.sender] = _num;
}
function getNum(address _myAddress) public view returns (uint) {
return numbers[_myAddress];
}
}
위와 같은 계약이 이미 올라가 있다고 가정해보자.
그럼 인터페이스를 만들고
contract NumberInterface {
//위 계약의 getNum 를 호출한다.
function getNum(address _myAddress) public view returns (uint); //껍데기만 정의한다.
}
이 인터페이스를 선언하고 다른 컨트렉트에서 사용하면 된다.
contract MyContract {
address addr = 0xabcdef..... //이더상의 계약 주소
NumberInterface numberContract = NumberInterface(addr);
//생성한다.
function someFunction() public {
//이제 getNum을 호출한다.
uint num = numberContract.getNum(msg.sender);
//이제 LuckyNumber 상의 getNum을 호출해서 사용할 수 있게 한다.
}
}
다수의 반환값 처리하기
솔리디티에서는 리턴값을 여러개를 가질 수 있다.
function multipleReturns() internal returns(uint a, uint b, uint c) {
return (1, 2, 3);
}
function processMultiReturns() external {
uint a;
uint b;
uint c;
//다수값을 설정함
(a, b, c) = multipleReturns();
}
function getLastReturnValue() external {
uint c;
//비어있는 건 빈값으로 설정가능
(,,c) = multipleReturns();
}
IF 문
솔리디티에서 If문은 자바스크립트의 if문과 동일하다.
function eatBLT(string sandwich) public {
//스트링(문자)간의 동일 여부를 판단하기 위해선 keccak256 을 사용해서 해시값 비교를 해야한다는 점 유의
if(keccak(sandwich) == keccak256("BLT")){
eat();
}
}
계약에 대해 소유권을
적용할 수 있는 라이버러리이다
.
소스를 잠깐 살펴보면 다음과 같다.
pragma solidity ^0.4.17;
contract Ownable {
address public owner;
event OwnerShipTransferred(address indexed previousOwner, address indexed newOwner);
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnerShipTransferred(owner, newOwner);
owner = newOwner;
}
}
위 코드엔 생성자 , 함수 제어자 (modify onlyOwner())( require를 확장해서 사용가능하다. )
_; 는 owner인걸 확인 한 후에 그 다음 코드를 포함한다는 뜻(실행함)
즉 위 코드는 Ownable 컨트렉트 생성시 msg.sender (컨트렉트를 배포한 사람) 을 대입한다.
그리고 onlyOwner 라는 접근 제어자가 붙은 경우 사용할 수 있게끔 접근 제어자를 붙인다.
그리고 새로운 소유자에게 이동할수 있게끔 하는 함수도 있다. ( transferOwnership
)
일반적인 dApp
에서 자주 쓰는 것이므로 확장해서 사용하는 편이 좋다.
함수 제어자
전에 require를 통해서 제어를 했었다.
이젠 함수 자체를 제어하는 방법을 배워보자.
modifier
를 함수에 붙여 주면 그 함수를 제어 할 수 있다.
.. Ownable 계약서에 있는
modifier onlyOwner() {
require(msg.sender == owner);
_
}
contract MyContract is Ownable {
event eat(string food);
function eatBread() external onlyOwner {
eat("Bread");
}
}
솔리디티에서는 사용자들이 자네가 만든 DApp의 함수를 실행할 때마다_가스_라고 불리는 화폐를 지불해야 하네. 사용자는 이더(ETH, 이더리움의 화폐)를 이용해서 가스를 사기 때문에, 자네의 DApp 함수를 실행하려면 사용자들은 ETH를 소모해야만 하네.
함수를 실행하는 데에 얼마나 많은 가스가 필요한지는 그 함수의 로직(논리 구조)이 얼마나 복잡한지에 따라 달라지네. 각각의 연산은 소모되는_가스 비용(gas cost)이 있고, 그 연산을 수행하는 데에 소모되는 컴퓨팅 자원의 양이 이 비용을 결정하네. 예를 들어, storage에 값을 쓰는 것은 두 개의 정수를 더하는 것보다 훨씬 비용이 높네. 자네 함수의 전체가스 비용_은 그 함수를 구성하는 개별 연산들의 가스 비용을 모두 합친 것과 같네.
함수를 실행하는 것은 자네의 사용자들에게 실제 돈을 쓰게 하기 때문에, 이더리움에서 코드 최적화는 다른 프로그래밍 언더들에 비해 훨씬 더 중요하네. 만약 자네의 코드가 엉망이라면, 사용자들은 자네의 함수를 실행하기 위해 일종의 할증료를 더 내야 할 걸세. 그리고 수천 명의 사용자가 이런 불필요한 비용을 낸다면 할증료가 수십 억 원까지 쌓일 수 있지.
이더리움은 크고 느린, 하지만 굉장히 안전한 컴퓨터와 같다고 할 수 있네. 자네가 어떤 함수를 실행할 때, 네트워크상의 모든 개별 노드가 함수의 출력값을 검증하기 위해 그 함수를 실행해야 하지. 모든 함수의 실행을 검증하는 수천 개의 노드가 바로 이더리움을 분산화하고, 데이터를 보존하며 누군가 검열할 수 없도록 하는 요소이지.
이더리움을 만든 사람들은 누군가가 무한 반복문을 써서 네트워크를 방해하거나, 자원 소모가 큰 연산을 써서 네트워크 자원을 모두 사용하지 못하도록 만들길 원했다네. 그래서 그들은 연산 처리에 비용이 들도록 만들었고, 사용자들은 저장 공간 뿐만 아니라 연산 사용 시간에 따라서도 비용을 지불해야 한다네.
참고: 사이드체인에서는 반드시 이렇지는 않다네. 크립토좀비를 만든 사람들이 Loom Network에서 만들고 있는 것들이 좋은 예시가 되겠군. 이더리움 메인넷에서 월드 오브 워크래프트 같은 게임을 직접적으로 돌리는 것은 절대 말이 되지 않을 걸세. 가스 비용이 엄청나게 높을 것이기 때문이지. 하지만 다른 합의 알고리즘을 가진 사이드체인에서는 가능할 수 있지. 우린 다음에 나올 레슨에서 DApp을 사이드체인에 올릴지, 이더리움 메인넷에 올릴지 판단하는 방법들에 대해 더 얘기할 걸세.
솔리디티 안에서 uint8, uint16, uint32 등의 타입을 지정하는 건 별로 의미가 없다. 이유는 생성때부터 256비트의 저장공간을 잡아놓기 때문이다. 즉 uint256 == uint8
과 같은 공간을 쓴다는 뜻이다.
하지만 예외 상황이 있다. 바로 struct
안에서이다.
struct Normal {
uint a;
}
struct Mini {
uint32 a;
}
Normal normal = Normal(10);
Mini mini = Mini(10);
//이 2개는 같지만 가스를 소모하는 게 다르다.
struct Mini {
uint32 a;
uint32 b;
uint c;
}
struct Mini {
uint32 a;
uint c;
uint32 b;
}
//이 2개도 같아 보이지만 옆으로 바로 선언하는게(상위) 가스를 덜 소모한다.
솔리디티는 유닉스 타임을 쓰고 있다. now
를 사용시 현재의 유닉스 타임스탬프를 값을 가지고 올 수 있다.
솔리디티는 또한 seconds
, minutes
, hours
, days
, weeks
, years
을 제공하고 있다.
1 minutes = 60
1 hours = 3600 (60 * 60)
1 days = 86400 ( 24 * 1 hours)
그럼 사용예제를 보자.
uint lastUpdated;
function updateTimestamp() public {
lastUpdated = now //now로 설정
}
//5분이 지난 경우 true , 아닌 경우 false
function fiveMinutesHavePassed() public view returns(bool) {
return (now >= (lastUpdated + 5 minutes));
}
uint 256 를 uint32 으로 캐스팅시
uint32(uint256 변수) //이런식으로 캐스팅함
storage 구조체를 주고 받을 수 있다.
function _doStuff(Zombie storage _zombie) internal {
// _zombie 처리
}
함수를 public 으로 지정시 외부에서 인터페이스로 해당 함수를 호출할 수 있다는 걸 유의해야 한다.
그래서 위에 언급했던 onlyOwner
를 사용을 할 수 있고 그 외 private
또는 internal
로 만들 수 있다.
// 사용자의 나이를 저장하는 매핑
mapping ( uint => uint ) public age;
//사용자의 특정 나이 이상인지 확인 하는 제어자
modifier olderThan( uint _age, uint _userId ) {
require(age[_userId]) >= _age);
_;
}
function buyAlchoDrinks(uint _userId) public orderThan(16, _userId) {
//필요한 함수 내용들
}
함수가 데이터를 쓰고,수정,삭제를 하지 않고 오직 읽기만 할 경우 사용될 수 있다.
View 함수는 사용자에 의해서 외부에서 호출 시 가스를 전혀 소모하지 않는다.
즉 로컬 노드에 데이터만 조회하고 따로 어떠한 트렉젝션이 필요없다는 뜻이다.
external view //오직 읽기에만 사용가능하다.
데이터의 일부를 쓰거나 바꿀때마다 블록체인에 영구적으로 기록되기 때문에 가스비가 들수 밖에 없다.
비용이 드는 문제이다.
그래서 비용을 최소화 하기 위해 필요한 경우가 아니면 storage에 데이터를 쓰지 않는 편이 좋다.
Storage 에 쓰지 않고 배열을 만들려면 memory 키워드를 이용하면 된다.
Storage 보다 휠씬 가스비가 절약할 수 있다. 외부에서 호출하는 View 함수라는 무료이다.
function getArray() external pure returns(uint[]){
//배열의 크기는 항상 주어져야 한다. 이후에는 변경될 수 있을것이다.
uint[] memory values = new uint[](3);
values.push(1);
values.push(2);
values.push(3);
return values;
}
솔리디티에서 for문은 자바스크립트와 유사하다.
function getEvens() pure external returns(uint[]) {
uint[] memory evens = new uint[](5);
uint counter = 0;
for(uint i = 0; i <= 10; i++ ){
if(i % 2 == 0){
evens[counter] = i;
counter++;
}
}
return evens;
}
payable로 지정된 함수에선 이더리움을 받을 수 잇는 특수 함수이다.
일반적인 API로는 보낼수 없지만 payable로는 보낼수 있다.
이 함수를 실행하기 위해선 컨트렉트에 일정금액을 지불해야 한다.
Contract OnlineStore {
function buySomething() external payable {
//함수 실행에 0.001이더가 보내졌는지 체크하는 함수 (> , <로도 이용가능)
require(msg.value == 0.001 ether);
transferthing(msg.sender); //이더를 이동..
}
}
그럼 웹에서는 이 부분 호출하기 위해선
// `OnlineStore`는 자네의 이더리움 상의 컨트랙트를 가리킨다고 가정해야 한다.
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})
주의 할점은 만약 함수가 payable 제어자가 없는 경우 이더를 보낸다고 하면 함수에서 트랙젝션을 거부를 할것이다.
이더를 보낸 후 출금을 하지 않는 경우 해당 컨트렉트의 이더리움 계좌에 저장되고 갇히게 된다.
그래서 그걸 출금하는 시스템을 같이 개발해야 한다.
contract GetPaid is Ownable {
//Owner 인지 체크
function withdraw() external onlyOwner {
//계좌에 있는 금액을 트렌스퍼한다.
owner.transfer(this.balance);
}
}
그리고 초과 입금시 나머지를 돌려주는 로직을 짤려면 다음과 같다.
uint itemfee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee); //입금하는 사람에게 출금하고 있다.
만약 판매자가 있고 구매자가 물건을 구입시 이미 판매자가 storage 에 저장되었다는 가정하에
seller.transfer(msg.value) //지정된 판매자에게 금액을 출금하고 있다.
솔리디티에서 난수를 만들려면 keccak256
해시 함수를 사용하면 된다.
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNounce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNounce)) % 100;
now => 타임스탬프 값
실행시 0~99까지 난수를 만들 수 있다.
이더리움에서는 자네가 컨트랙트의 함수를 실행하면_트랜잭션(transaction)으로서 네트워크의 노드 하나 혹은 여러 노드에 실행을 알리게 되네. 그 후 네트워크의 노드들은 여러 개의 트랜잭션을 모으고, "작업 증명"으로 알려진 계산이 매우 복잡한 수학적 문제를 먼저 풀기 위한 시도를 하게 되네. 그리고서 해당 트랜잭션 그룹을 그들의 작업 증명(PoW)과 함께블록_으로 네트워크에 배포하게 되지.
한 노드가 어떤 PoW를 풀면, 다른 노드들은 그 PoW를 풀려는 시도를 멈추고 해당 노드가 보낸 트랜잭션 목록이 유효한 것인지 검증하네. 유효하다면 해당 블록을 받아들이고 다음 블록을 풀기 시작하지.
이것이 우리의 난수 함수를 취약하게 만드네.
우리가 동전 던지기 컨트랙트를 사용한다고 해보지 - 앞면이 나오면 돈이 두 배가 되고, 뒷면이 나오면 모두 다 잃는 것이네. 앞뒷면을 결정할 때 위에서 본 난수 함수를 사용한다고 가정해보세. (random >= 50
은 앞면,random < 50
은 뒷면이네).
내가 만약 노드를 실행하고 있다면, 나는오직 나의 노드에만트랜잭션을 알리고 이것을 공유하지 않을 수 있네. 그 후 내가 이기는지 확인하기 위해 동전 던지기 함수를 실행할 수 있지 - 그리고 만약 내가 진다면, 내가 풀고 있는 다음 블록에 해당 트랜잭션을 포함하지 않는 것을 선택하지. 난 이것을 내가 결국 동전 던지기에서 이기고 다음 블록을 풀 때까지 무한대로 반복할 수 있고, 이득을 볼 수 있네.
블록체인의 전체 내용은 모든 참여자에게 공개되므로, 이건 풀기 어려운 문제이고 그 해답은 이 튜토리얼에를 벗어나네. 해결 방법들에 대해 궁금하다면이 StackOverflow 글을 읽어보게. 하나의 방법은 이더리움 블록체인 외부의 난수 함수에 접근할 수 있도록_오라클_을 사용하는 것이네.
물론, 네트워크 상의 수만 개의 이더리움 노드들이 다음 블록을 풀기 위해 경쟁하고 있으니, 내가 다음 블록을 풀 확률은 매우 낮을 것이네. 위에서 말한 부당한 방법을 쓰는 것은 많은 시간과 연산 자원을 필요로 할 것이야 - 하지만 보상이 충분히 크다면(내가 천억 원을 걸 수 있다든지?), 공격할 만한 가치가 있을 것이네.
그러니 이런 난수 생성은 이더리움 상에서 안전하지는 않지만, 실제로는 난수 함수가 즉시 큰 돈이 되지 않는 한, 자네 게임의 사용자들은 게임을 공격할 만한 충분한 자원을 들이지 않을 것이네.
이 튜토리얼에서는 시연 목적으로 간단한 게임을 만들고 있고 바로 돈이 되는 게 없기 때문에, 우린 구현하기 간단한 난수 생성기를 사용하는 것으로 타협할 것이네. 이게 완전히 안전하지는 않다는 걸 알긴 하지만 말이야.
향후 레슨에서는, 우린_oracle_(이더리움 외부에서 데이터를 받아오는 안전한 방법 중 하나)을 사용해서 블록체인 밖에서 안전한 난수를 만드는 방법을 다룰 수도 있네.
if (zombieCoins[msg.sender] > 100000000) {
}else{
}
기본적으로 ERC20 토큰이 있어서 이걸 거래가 가능하다. 이더리움 토큰을 생각해보면 된다.
하지만 게임상에서 게임 수집물에 대한 토큰으로 표기하기에는 한계가 있다..
좀비하나마다 아이디를 부여해야 하기 때문이다.
그래서 ERC721 토큰을 제공한다.
참고 : ERC721 같은 표준을 사용하는 장점은 경매와 플레이어가 좀비를 거래 / 판매하는 방식을 결정하는 에스크로 로직을 계약에서 구현할 필요가 없어진다는 점입니다.
사양을 준수하면 다른 사람이 수집 가능한 ERC721 스크립트 자산의 교환 플랫폼을 만들 수 우리의 ERC721 좀비는 그 플랫폼에서 사용할 수 있습니다.
따라서 자신의 거래 로직을 전개하는 대신 토큰 규격을 사용하는 것은 분명히 장점이 있습니다.
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
}
토큰 계약 구현시 먼저 할껀 Import 를 해서 가져오는 것이다.
import "./erc721.sol"
//참고로 계약은 다중상속을 허용하고 있다.
contrat A is B,C {
}
function balanceOf(address _owner) public view returns (uint256 _balance);
//Address 입력시 그 계정에 balance가 얼마나 있는지 조회할 수 있다.
function ownerOf(uint256 _tokenId) public view returns (address _owner);
//해당 토큰 아이디에 address를 리턴해준다.
연산시 만약 uinit8 을 지정한 상태에서 8비트만 가질수 있다. 즉 최대수는 2^8 -1 = 255
이다.
하지만 만약
uint number = 255;
number++; //이 경우 오버플로우 발생함
오버플로우 반대로 음수로 가는 경우 발생될 수 있다.
이를 방지하기 위해 OpenZepplin
은 SafeMath
라는 라이버러리를 제공한다.
기본적으로 함수는 4개의 기능을 제공한다.
add, sub, mul, div
using SafeMath for uint256; //라이버러리를 사용하겠다고 정의
uint256 a = 5;
uint256 b = a.add(3);
uint256 c = a.mul(2);
내부 소스를 들어다 보면
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
library 라는 키워드 확인해볼 필요있다.
library 타입으로 지정시 다른 타입을 앞으로 끌어들일 수 있다.
using SafeMath for uint;
uint test = 2;
test = test.mul(3); //인자가 2개가 필요하지만 자동으로 앞 변수(test)를 입력받게 된다..
//num++ 이 방법을 쓰지 말고
num = num.add(1); //이렇게 대체하면 됩니다.
사용법은 **require **와 비슷하다. 값이 false 일때 반환한다.
그러면 require 와 차이점은 무엇일까.
**require **실패시 가스비가 반환된다. 하지만 **assert **사용시 가스비는 반환되지 않고 false 로 끝난다.
그래서 극단적인 오류의 경우에만 사용해야 한다. 예를 들어 오버플로우나 언더 플로우등..
기본적으로 SafeMath 타입은 uint256 이다. 그래서 추가적으로 2개의 라이버러리를 더 가져와야 한다.
SafeMath16, SafeMath32
내부적으로 같지만 타입만 변경되었다.
솔리디티에서 주석은 자바스크립트 과 동일하다.
주석은 **natspec 을 따른다. **
/// @title A contract for basic math operations
/// @author H4XF13LD MORRIS 💯💯😎💯💯
/// @notice For now, this contract just adds a multiply function
contract Math {
/// @notice Multiplies 2 numbers together
/// @param x the first uint.
/// @param y the second uint.
/// @return z the product of (x * y)
/// @dev This function does not currently check for overflows
function multiply(uint x, uint y) returns (uint z) {
// This is just a normal comment, and won't get picked up by natspec
z = x * y;
}
}
@notice 는 이 함수가 하는 일을 설명한다.
@dev 는 추가적으로 개발자에게 할 말을 적는다.
이더리움 네트워크내에서 통신을 하기위해선 여러가지 방법이 있지만 주로 JSON-RPC를 통한다. 하지만 형태는 직접 보기가 불편한 점이 있다. 그래서 그걸 인터페이스로 쉽게 구현하는 라이버러리가 있다. Web3.js 를 뜻한다.
JSON-RPC
형태는 다음과 같다.
{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","gas":"0x76c0","gasPrice":"0x9184e72a000","value":"0x9184e72a","data":"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}],"id":1}
하지만 Web3.js 에서 계약 내 함수 호출시 다음과 같은 형태로 가능하다.
CryptoZombies.methods.createRandomZombie("Vitalik Nakamoto 🤔")
.send({ from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", gas: "3000000" })
Web3 라이버러리 주입
web3 설치시 여러가지 방법이 있을 수 있다.
// Using NPM
npm install web3
// Using Yarn
yarn add web3
// Using Bower
bower install web3
추가적으로 스크립트로 가져오는 방법인
<script language="javascript" type="text/javascript" src="web3.min.js"></script>
Web3 Provider 은 이더리움 서버를 어떤 걸로 할지 설정해준다. 자신의 네트워크로 지정해도 상관은 없다. 그리고 메인이나 테스트넷등에 접속시 쉽게 하기 위해서 Infura 라는 서비스도 제공되어 진다.
Infura 를 공급자(Provider)로 사용하는 경우 자신의 노드를 설정하고 유지할 필요없이 Ethereum 블록 체인으로 메세지를 보내고 받을 수 있다.
var web3 = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));
DAPP의 경우 읽는 것 뿐아니라 블록체인에 Write 할 것이다. 그럼 이 사용자들이 개인 키로 트랙잭션에 서명 할 수 있는 방법이 필요하다.
그렬경우 대표적으로 인기가 많은 것은 MetaMask
이다.
하지만 만약 Metamask 를 설치를 하지 않을 경우에도 오류 메세지를 보여주는 편이 좋다.
window.addEventListener('load', function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider
web3js = new Web3(web3.currentProvider);
} else {
// Handle the case where the user doesn't have web3. Probably
// show them a message telling them to install Metamask in
// order to use our app.
}
// Now you can start your app & access web3js freely:
startApp()
})
Web3.js 은 두가지가 필수로 주어져야 한다.
// Instantiate myContract
var myContract = new web3js.eth.Contract(myABI, myContractAddress);
**Web3.js 는 작성한 스마트 계약과의 연동시 **
주소
와ABI
가 필요합니다.
스마트 계약 작성후 그 계약을 컴파일 한 후 이더리움에 배포를 하는 과정을 거칩니다. 배포를 하게 되면 이더리움 해당 되는 넷에 그 계약이 머물고 있는 주소를 얻을수 있다.
배포 전 컴파일하게 되면 web3.js 가 이해할 수 있는 JSON 향태의 파일을 제공해준다. 그럼 이 걸 주소와 같이 저장해서 사용을 하면 된다.
이 2개가 준비되었다고 가정하면 Web3 에서는 인스턴스화 할 수 있다.
var myContract = new Web3js.eth.Contract(myABI, myContractAddress);
자 이제 모든 준비는 다 된 상태이다. 그럼 함수들을 호출을 해보자. 함수 호출시 call
과 send
를 사용한다.
Call
: View, Pure 함수로 주로 사용된다. 이건 로컬 노드에서 실행되기 때문에 블록체인에서 transaction 을 실행하지 않는다.
View,Pure 함수는 가스소모가 없다.
myContract.methods.myMethod(123).call()
send
: 트랙잭션이 발생되며 블록체인내에서 변화가 이루어진다. View,Pure 함수를 제외하고 함수들이 send로 불리어진다.send
함수를 실행시 가스가 소모되며 만약 메타메스크를 실행한 상태라면 그 메타메스크가 열리면서 함수를 실행을 할 수 있게 된다. transaction
에는 submit 도 가능하다.
myContract.methods.myMethod(123).send()
MetaMask 를 통해 Web3 은 유저정보를 실시간으로 받아올 수 있다.
var userAccount = web3.eth.accounts[0]
만약 유저 계정을 바뀌는 경우라면 어떻게 되는 것일까.. 크롬에서 metamask를 켜고 유저정보를 실시간으로 변경시 그 정보를 업데이트를 해줘야 한다.
그걸 하기 위해선 매초마다 체크를 해서 업데이트를 해주면 된다.
var accountInterval = setInterval(function() {
//변경이 된 경우라면
if(web3.eth.accounts[0] !== userAccount) {
userAccount = web3.eth.accounts[0];
updateInterface();
}
}, 100);
getZombieDetails(id)
.then(function(zombie) {
// Using ES6's "template literals" to inject variables into the HTML.
// Append each one to our #zombies div
$("#zombies").append(`<div class="zombie">
<ul>
<li>Name: ${zombie.name}</li>
<li>DNA: ${zombie.dna}</li>
<li>Level: ${zombie.level}</li>
<li>Wins: ${zombie.winCount}</li>
<li>Losses: ${zombie.lossCount}</li>
<li>Ready Time: ${zombie.readyTime}</li>
</ul>
</div>`);
});
send
트랙잭션을 수행하면 from
해당되는 기능을 호출하는 사람의 주소가 필요하다. (msg.sender
) . 그 후에 MetaMask 가 팝업으로 나와서 submit(전송확인) 할 것인지 물어본다.send
시 거래 비용으로 가스가 든다.send
를 하는 순간 블록에 노드를 업데이트 하므로 최소 15이상의 시간
이 걸릴 수 있으므로 그에 대한 UI 를 고려 해야 할 것이다. 즉 비동기 함수가 필요할 것이다.... Solidity 코드
function createRandomZombie(string _name) public {
require(ownerZombieCount[msg.sender] == 0);
uint randDna = _generateRandomDna(_name);
randDna = randDna - randDna % 100;
_createZombie(_name, randDna);
}
.... 비동기로 처리 되는 부분을 눈여겨 봐야 한다.
.... Web3.js 코드
function createRandomZombie(name) {
// This is going to take a while, so update the UI to let the user know
// the transaction has been sent
$("#txStatus").text("Creating new zombie on the blockchain. This may take a while...");
// Send the tx to our contract:
return CryptoZombies.methods.createRandomZombie(name)
.send({ from: userAccount })
.on("receipt", function(receipt) {
$("#txStatus").text("Successfully created " + name + "!");
// Transaction was accepted into the blockchain, let's redraw the UI
getZombiesByOwner(userAccount).then(displayZombies);
})
.on("error", function(error) {
// Do something to alert the user their transaction has failed
$("#txStatus").text(error);
});
}
여기에서 이벤트 리스너인 receipt 와 error 부분이 나온다.
그리고 전송시 가스를 코드에서 지정하거나
MetaMask
.에서 지정하는 방법이 있을 수 있다..send({ from: userAccount, gas: 3000000 }) // 이렇게 코드에서 지정하거나 MetaMask 로 사용자가 가스비를 설정하게끔 할수 있다.
// zombieHelper 에 함수를 호출할 수 있다.
function levelUp(uint _zombieId) external payable {
require(msg.value == levelUpFee);
zombies[_zombieId].level++;
}
Wei
란?
1 ether = 10^18 wei 로 계산된다.
그래서 web3에선 편하게 1이더를 wei 로 쉽게 변환해준다.
web3js.utils.toWei("1", "ether");
levelUpFee 가 0.001 이더로 설정되어있기 때문에 우리는 0.001 이더를 보낼 수 있다.
CryptoZombies.methods.levelUp(zombieId)
.send({ from: userAccount, value: web3js.utils.toWei("0.001", "ether") })
zombieFactory.sol
에서 새로운 좀비 호출시 NewZombie
라는 이벤트를 트리거할 수 있다. web3js
에서도 이벤트 발생이 가능하다.
cryptoZombies.events.NewZombie()
.on("data", function(event) {
let zombie = event.returnValues; //이벤트 결과값을 여기로 리턴받음
console.log("A new zombie was born!", zombie.zombieId, zombie.name, zombie.dna);
}).on("error", console.error);
어떤 좀비라도 DAPP 에서 생성이 되면 로그를 보여주게 된다. 현재 유저뿐만 아니라 모든 좀비에 대해서 이벤트가 발생을 하게 된다. 하지만 만약 현재 유저에만 보여주고 싶다면?
이벤트에서 현재 유저에만 이벤트가 가도록 하고 싶다면 indexed 키워드를 지정하면 된다.
ERC721상의 이벤트 중 하나인 전송 부분에서 살펴볼 수 있다.
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
_from,_to 가 indexed 키워드가 붙여져 있다.
// Use `filter` to only fire this code when `_to` equals `userAccount`
cryptoZombies.events.Transfer({ filter: { _to: userAccount } })
.on("data", function(event) {
let data = event.returnValues;
// The current user just received a zombie!
// Do something here to update the UI to show it
}).on("error", console.error);
getPastEvents
를 사용하여 지난 이벤트를 조회도 가능하다. fromBlock, toBlock 로 이벤트 로그들을 살펴볼수 있다.
cryptoZombies.getPastEvents("NewZombie", { fromBlock: 0, toBlock: "latest" })
.then(function(events) {
// `events` is an array of `event` objects that we can iterate, like we did above
// This code will get us a list of every zombie that was ever created
});
현재로서는 웹소켓을 이용하여 이벤트를 구독할 수 있다. 최신 1.0 web3.js 내용이다.
아직 메타메스크는 Provider 로써 제공을 못 해주고 있다. 그래서 infura 를 사용하여 만들수 있다.
var web3Infura = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));
var czEvents = new web3Infura.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress);
Geth를 이용한 dapp 개발 #1 (1) | 2018.04.17 |
---|---|
[DAPP] truffle을 활용한 Pet-Shop 튜터리얼 분석해보자. (0) | 2018.04.01 |
[DAPP] Truffle로 스마트 계약을 테스팅하고 디버깅해보자. (0) | 2018.03.31 |
[DAPP] ERC20 토큰을 만들어서 배포까지 (7) | 2018.03.21 |
[DAPP]예제로 배우는 DAPP 개발편 - 복권편 (3) | 2018.03.19 |
이더리움에 올릴수 있는 토큰은 ERC20 규격으로 만들수 있다. 이 토큰을 가지고 ICO에 올려서 서로간의 거래를 할 수 있습니다.
그럼 시작해보자.
규격 인터페이스 형태는 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | contract ERC20Interface { //총 갯수 function totalSupply() public constant returns (uint); //주어진 토큰오너에 대한 잔액조회 function balanceOf(address tokenOwner) public constant returns (uint balance); //소유자가 승인 할 수 는 토큰 양을 반환합니다. function allowance(address tokenOwner, address spender) public constant returns (uint remaining); //to 에게 토큰을 전송한다. function transfer(address to, uint tokens) public returns (bool success); //트큰을 원래주인으로부터 다른사람에게 전송함을 승인한다. function approve(address spender, uint tokens) public returns (bool success); //from -> to 을 토큰을 전송 : 성공/실패 function transferFrom(address from, address to, uint tokens) public returns (bool success); //전송 이벤트 event Transfer(address indexed from, address indexed to, uint tokens); //승인 이벤트 event Approval(address indexed tokenOwner, address indexed spender, uint tokens); } | cs |
필요한 지식은 다음과 같다..
여기에선 Truffle 이더리움 프레임워크를 이용해서 쉽게 만들어볼것이다.
우선 설치를 해보자.
npm install -g truffle
그리고 원하는 위치에 폴더를 만들고 초기화를 진행한다.
1 2 3 | mkdir sugi-coin cd sugi-coin truffle init | cs |
처음 트리 구조는 위와 같다.
그리고 오픈재플린( OpenZepplin ) 을 라이버러리를 설치를 해보자.
유용한 계약들을 손쉽게 쓸수 있도록 제공해주는 DAPP 개발시 필수 라이버러리이다.
토큰 규약 말고도 여러가지가 미리 포함되어 있다.
1 | npm install zeppelin-solidity -save | cs |
그럼 contract 를 작성해보자.
contract 폴더내에 sujicoin.sol 을 작성한다. StandardToken 을 상속받는다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | pragma solidity ^0.4.18; import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; contract Sujicoin is StandardToken { string public name = "Sujicoin"; string public symbol = "SUJI"; //통화단위 uint public decimals = 2; //자리수 uint public INITIAL_SUPPLY = 10000 * (10 ** decimals); //초기 공급량 //생성자 function Sujicoin() public { balances[msg.sender] = INITIAL_SUPPLY; } } | cs |
그리고 배포를 위해 migrations 폴더에 파일을 새로 만든다.
1 | 2_deploy_sujicoin.js | cs |
소스는 아래와 같이 작성한다.
1 2 3 4 5 | var SujiCoin = artifacts.require("./Sujicoin.sol"); module.exports = function(deployer) { deployer.deploy(SujiCoin); }; | cs |
그리고 루트 폴더에 보면 truffle.js 파일이 있을 것이다. 아래와 같은 소스를 넣어준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | require('dotenv').config(); const Web3 = require("web3"); const web3 = new Web3(); const WalletProvider = require("truffle-wallet-provider"); const Wallet = require('ethereumjs-wallet'); var rinkebyPrivateKey = new Buffer("private key", "hex") var rinkebyWallet = Wallet.fromPrivateKey(rinkebyPrivateKey); var rinkebyProvider = new WalletProvider(rinkebyWallet, "https://rinkeby.infura.io/"); module.exports = { networks: { development: { host: "localhost", port: 8545, network_id: "*" // Match any network id }, rinkeby: { provider: rinkebyProvider, gas: 4600000, gasPrice: web3.toWei("20", "gwei"), network_id: "3", } } }; | cs |
[ RINKEBY_PRIVATE_KEY ] 에 지갑에서 private key를 넣어주자.
여기에서는 Metamask 를 이용한다.
Private key 추출은 Metamask 를 통해서 할수 있다.
서버는 Rinkeby 테스트 서버를 이용한다.
그리고 배포를 진행해본다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | >> truffle deploy --network rinkeby Compiling .\contracts\SujiCoin.sol... Compiling zeppelin-solidity/contracts/token/ERC20/StandardToken.sol... Compiling zeppelin-solidity\contracts\math\SafeMath.sol... Compiling zeppelin-solidity\contracts\token\ERC20\BasicToken.sol... Compiling zeppelin-solidity\contracts\token\ERC20\ERC20.sol... Compiling zeppelin-solidity\contracts\token\ERC20\ERC20Basic.sol... Writing artifacts to .\build\contracts Using network 'rinkeby'. Running migration: 1_initial_migration.js Replacing Migrations... ... 0x2ce47755f6112ab384a14910656eddb18fa94061cc1dc0349f7b899fb3ba9f84 Migrations: 0x5903b751a054c281f12a354a8d9bb2304a584447 Saving successful migration to network... ... 0x6a7b74a8d897593042da8b7f349a39e5238f5ed2dce302965ef50ccc58b0260c Saving artifacts... Running migration: 2_deploy_hamburgercoin.js Deploying SujiCoin... ... 0x1d52f1e2805d04640d70a8f109f07292bdde2469dd377ac3315964956563ef53 SujiCoin: 0x2e7b9c9c7c74bac74d069e57128dc66898ff41a9 Saving artifacts... | cs |
최종적으로 나온 SujiCoin: 0x2e7b9c9c7c74bac74d069e57128dc66898ff41a9
0x.... 이 부분이 현재 올라간 계약 부분이다. 그럼 잘 올라갔는지 확인해보자.
여기에서는 Rinkeby test net을 기본으로 한다.
https://rinkeby.etherscan.io/address/0x2e7b9c9c7c74bac74d069e57128dc66898ff41a9
이 주소로 가서 테스트 서버에 정상적으로 올라갔는지 확인 해본다.
성공적으로 올라간 것을 볼 수 있다. 그러면 이걸 다시 제대로 등록 되는 지 확인해보자.
Address( 0x2e7b9c9c7c74bac74d069e57128dc66898ff41a9 ) 을 복사를 해서 Metamask 토큰에 넣어보자.
위와 같이 SUJI 토큰이 등록된 걸 볼 수 있다.
그럼 전송을 서로간에 해보자.
우선 https://www.myetherwallet.com/ 으로 가보자.
오른쪽에 보면 Metamask 연동하는 부분이 보인다. 클릭하자.
Geth를 이용한 dapp 개발 #1 (1) | 2018.04.17 |
---|---|
[DAPP] truffle을 활용한 Pet-Shop 튜터리얼 분석해보자. (0) | 2018.04.01 |
[DAPP] Truffle로 스마트 계약을 테스팅하고 디버깅해보자. (0) | 2018.03.31 |
[DAPP] 크립토좀비 요약 내용[1 ~ 6장] (2) | 2018.03.25 |
[DAPP]예제로 배우는 DAPP 개발편 - 복권편 (3) | 2018.03.19 |
내용은 최근 세미나 한 자료를 기준으로 진행한다.
예제 자료는 다음의 위치에서 볼 수 있다.
https://github.com/bear2u/lottery_exam1
결과물은 오른쪽과 같다.
지갑을 우선 설치를 해보자.
https://metamask.io/ 에서 크롬 확장 프로그램인 설치를 하자.
로그인을 비밀번호를 입력 후 하면 되는데 문자열이 모니크 키값으로 주어지는데 꼭 다른 곳에 저장을 해놓자.
로그인시 보유한 이더와 서버주소를 확인 할 수 있다. 테스트 서버를 우선 설정해보자.
왼쪽 하단 서버 선택을 Rinkeby Test Net 으로 지정하자.
개발을 해서 테스트를 할려면 이더(코인)가 필요하다.
무료 이더를 받을 수 있는 방법은 2가지가 있다.
http://rinkeby-faucet.com : 지갑 계정을 복사를 해서 해당 사이트에 넣으면 0.001 이더를 보내준다. (테스트 서버에 한해)
https://faucet.rinkeby.io/ : 소셜을 통해서 약 18이더정도를 받을 수 있다. 사용 방법은 피드에 내 지갑 주소를 올려서 그 해당 링크를 넣으면 된다.
일반적인 아키텍처와 비교해서 차이점은 이더리움 블록체인 서버에 연결하기 위해 web3 라이버러리를 이용할 수 있다.
web3js 라이버러리 바로가기
오타 : Proof of Work (POW)
http://remix.ethereum.org/ 에서 온라인 컴파일 및 테스팅을 진행 할 수 있다.
테스팅 환경을 선택을 가능하다.
우선 로컬로 지정해서 시작한다.
이 후엔 서버를 선택해서 진행가능하다.
실행환경 지정
생성 함수를 어떤 식으로 지정 할 수 있는지 지정
각각의 함수를 테스팅 할 수 있다.
Account를 기본이나 다른 걸로 설정한다. JVM 설정시 기본 5개는 주어진다.
1이더 이상 값을 설정한다.
그리고 하단에 Enter를 통해서 베팅한다.
Account를 다른 걸로 선택해서 Enter를 해서 여러명을 입장한다.
여러명을 베팅을 시켜서 돈이 다 나갔는지 확인해본다.
현재 4명이 들어간 상태이다.
유저가 제대로 들어왔는지 확인해본다. 총 4명인걸 확인한다.
그리고 매니저를(처음 만든 계정) 통해서 pickWinner 클릭한다.
그럼 랜덤으로 이긴 사람에게 돈이 들어간걸 볼 수 있다. 102.xxxxx 이더로 들어갔다.
콘솔창으로 오류 및 진행사항들을 확인 할 수 있다.
그리고 이 작성된 솔리디티 스크립트를 함수가 제대로 실행되는지 테스팅 해보자. mocha 로 진행한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | const assert = require('assert'); const ganache = require('ganache-cli'); const Web3 = require('web3'); const provider = ganache.provider(); const web3 = new Web3(provider); const { interface, bytecode } = require('../compile'); let lottery; let accounts; beforeEach(async () => { accounts = await web3.eth.getAccounts(); lottery = await new web3.eth.Contract(JSON.parse(interface)) .deploy({ data: bytecode }) .send({ from: accounts[0], gas: '1000000'}); lottery.setProvider(provider); }); describe('Lottery Contract', () => { it('deploys a contract', () => { assert.ok(lottery.options.address); }); it('allows one account to enter', async () => { await lottery.methods.enter().send({ from: accounts[0], value: web3.utils.toWei('0.02','ether') //'2000000000000000' }); const players = await lottery.methods.getPlayers().call({ from: accounts[0] }); //저장된 사람이 계정에 있는지 체크 assert.equal(accounts[0], players[0]); //계정 사이즈 체크 assert.equal(1, players.length); }); it('allows multiple accounts to enter', async () => { await lottery.methods.enter().send({ from: accounts[0], value: web3.utils.toWei('0.02','ether') //'2000000000000000' }); await lottery.methods.enter().send({ from: accounts[1], value: web3.utils.toWei('0.02','ether') //'2000000000000000' }); await lottery.methods.enter().send({ from: accounts[2], value: web3.utils.toWei('0.02','ether') //'2000000000000000' }); const players = await lottery.methods.getPlayers().call({ from: accounts[0] }); //저장된 사람이 계정에 있는지 체크 assert.equal(accounts[0], players[0]); assert.equal(accounts[1], players[1]); assert.equal(accounts[2], players[2]); //계정 사이즈 체크 assert.equal(3, players.length); }); it('requires a minimum amount of ether to enter', async () => { try{ await lottery.methods.enter().send({ from: accounts[0], value: 0 }); assert(false); } catch (err) { assert.ok(err); } }); it('only manager can call pickWinner' , async () => { try{ await lottery.methods.pickWinner().send({ from: accounts[1] }); assert(false); }catch(err) { assert(err); } }); if('sends money to the winnder and resets the players array', async () => { await lottery.methods.enter().send({ from: accounts[0], value: web3.utils.toWei('2', 'ether') }); const initialBalance = await Web3.eth.getBalance(accounts[0]); await lottery.methods.pickWinner().send({ from: accounts[0] }); const finalBalance = await web3.eth.getBalance(accounts[0]); const difference = finalBalance - initialBalance; console.log(finalBalance - initialBalance); assert(difference > web3.utils.toWei('1.8', 'ether')); }); }); | cs |
이제 컴파일 해보자.
1 2 3 4 5 6 7 8 9 | const path = require('path'); const fs = require('fs'); const solc = require('solc'); const lotteryPath = path.resolve(__dirname, 'contracts', 'Lottery.sol'); const source = fs.readFileSync(lotteryPath, 'utf8'); module.exports = solc.compile(source, 1).contracts[':Lottery']; | cs |
그리고 배포를 해보자. 배포를 하면 시간이 다소 걸린다. 실제 서버로 올리기 때문이다.
ABI 내용도 나오는 걸 볼 수 있다.
1 2 3 4 5 6 7 8 | node compile node deploy //결과 Attempting to deploy from account 0xBC9aAd0B1598a9388f3535272376006eeaF1eea2 // contract address [{"constant":true,"inputs":[],"name":"manager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pickWinner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getPlayers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"enter","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"players","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] Contract deployed to 0x43eD67e834c9588bd8552BbC54Bc1d565C6C8Cf7 | cs |
그리고 잘 배포가 되었는지 확인해보자.
에서 계약 주소를 넣어서 확인 할 수 있다. 물론 현재 서버는 RINKEBY 테스트 서버이다.
이제 웹 클라이언트 화면을 만들 차례이다.
현재 지갑을 가져온다.
1 2 3 4 5 6 7 | import Web3 from 'web3'; const web3 = new Web3(window.web3.currentProvider); export default web3; | cs |
여기에서 지갑 가져오는 위치를 다른 곳으로 바꿀 수도 있다.
1 2 3 4 5 6 7 8 | if (typeof web3 !== 'undefined') { web3 = new Web3(web3.currentProvider); } else { // set the provider you want from Web3.providers web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); } | cs |
그리고 lottery.js 을 통해 현재 솔리디티로 작성된 내용을 가져온다. 계약 주소와 abi 내용을 입력하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | import web3 from './web3'; const address = '0x6D02E4aD1E1870E96E41E5735A2ddb02165C213b'; const abi = [ { "constant": true, "inputs": [ ], "name": "manager", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ ], "name": "pickWinner", "outputs": [ ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ ], "name": "getPlayers", "outputs": [ { "name": "", "type": "address[]" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ ], "name": "enter", "outputs": [ ], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "players", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "inputs": [ ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" } ]; export default new web3.eth.Contract(abi, address); | cs |
Geth를 이용한 dapp 개발 #1 (1) | 2018.04.17 |
---|---|
[DAPP] truffle을 활용한 Pet-Shop 튜터리얼 분석해보자. (0) | 2018.04.01 |
[DAPP] Truffle로 스마트 계약을 테스팅하고 디버깅해보자. (0) | 2018.03.31 |
[DAPP] 크립토좀비 요약 내용[1 ~ 6장] (2) | 2018.03.25 |
[DAPP] ERC20 토큰을 만들어서 배포까지 (7) | 2018.03.21 |
********** 카카오 **********
https://developers.kakao.com/docs/android/user-management#사용자-정보-요청
Name Description
kaccount_email 사용자 카카오계정의 이메일
kaccount_email_verified 인증받은 카카오계정 이메일인지 여부
카카오계정 이메일은 변경될 수 있습니다.
nickname 카카오톡 또는 카카오스토리의 닉네임 정보
profile_image 640px * 640px 크기의 카카오톡 또는 카카오스토리의 프로필 이미지 URL (2017/08/22 이전 가입자에 대해서는 480px * 480px ~ 1024px * 1024px 크기를 가질 수 있음)
thumbnail_image 110px * 110px 크기의 카카오톡 또는 카카오스토리의 썸네일 프로필 이미지 URL (2017/08/22 이전 가입자에 대해서는 160px * 213px 크기를 가질 수 있음)
********** 네이버 **********
https://developers.naver.com/docs/login/devguide/
3.3.1 네아로 회원의 프로필 정보
네아로 연동을 성공적으로 완료하면 연동 결과로 접근 토큰을 얻을 수 있습니다.
접근 토큰을 이용하여 사용자 프로필 정보를 조회할 수 있습니다.
제공가능한 프로필 정보
이용자 고유 식별자 (네이버 ID가 아닌 고유 식별자)
이름
닉네임 (네이버 별명)
프로필 이미지 (네이버 내정보 프로필 이미지 URL)
이메일 (네이버 내정보 이메일)
생일
연령대
성별
각각의 프로필 정보의 규격은 다음과 같습니다.
이용자 고유 식별자 : INT64 규격의 숫자
이름 : 10자 이내로 구성된 문자열
닉네임 : 20자 이내로 구성된 문자열
프로필 이미지 : 255자 이내로 구성된 URL 형태의 문자열
이메일 : 이메일 규격의 문자열
생일 : 월-일 (MM-DD) 형태의 문자열
연령대 : 연령 구간에 따라 0-9 / 10-19 / 20-29 / 30-39 / 40-49 / 50-59 / 60- 으로 표현된 문자열
성별 : M/F (남성/여성) 으로 표된 문자
프로필 정보 예시
이용자 고유 식별자 : 120381022
이름 : 네이버
닉네임 : 네이버닉네임
프로필 이미지 : https://phinf.pstatic.net/.../image.jpg
이메일 : naveridlogin@naver.com
생일 : 08-15
연령대 : 20-29
성별 : F
********** 페이스북 **********
https://developers.facebook.com/docs/facebook-login/overview?locale=ko_KR
검수 절차를 받지 않은 앱에서 사용자에게 요청할 수 있는 권한은 다음과 같습니다.
public profile
user_friends
상세정보(http://superwony.tistory.com/13 참조함)
id
cover
name
first_name
last_name
age_range
link
gender
locale
picture
timezone
updated_time
verified
안녕하세요. 수지아빠입니다.
최근 온라인 모임을 몇개 참여하느라 아주 바쁜 하루를 보내고 있습니다.
그 중에서 100일달성을 한 모임이 있습니다.
바로 독서하기 모임입니다.
이 독서하기 모임은 피터팬님이 운영하는 매일 15분 이상 책을 읽고 읽은 내용을 카카오톡에 인증하는 것입니다.
처음에는 15분정도야 매일 할 수 있다는 생각으로 쉽게 참여를 하였습니다.
하지만 하루하루가 지나고 바쁜 일도 있을테고 피곤하고 자고싶은 유혹들이 몰려와 겨우 15분이라는 시간을 만들어서 꾸준하게 독서하는게 많이 힘들더군요.
그래도 한번 해보자는 생각에 1,2,3..10일..이상 연속 읽기 시작했습니다.
1년에 책을 한권을 잘 안 읽은 사람으로써 작은 변화들이 오기 시작했습니다.
그 변화 중 첫번째는 시간을 잘 활용을 하게 됩니다. 업무 시간외에 아이들과 함께 노는 시간을 제외하고 시간을 만들어야 하므로 시간을 1분도 허투로 사용하기엔 아까워서 좀 더 잘 활용하게 됩니다.
두번째는 늘 책을 보고 있습니다. 저도 이 부분은 꽤 놀랐는데 와이프가 말해주더군요. 항상 책을 보고 있고 휴대폰을 보더라도 이북을 보고 있어서 놀랍다고 하네요.
세번째는 생각이 좀 더 깊어집니다. 겨우 100일가지고 그런 생각하는게 우습긴 하지만 조금은 현재 그리고 미래의 시간들에 대해서 생각하게 되는 것 같습니다.
책을 완독한 수가 늘어나고 세상엔 정말 배울게 많구나 라는 생각을 하게 되었습니다. 그리고 많은 분들에게 독서의 참재미를 느끼는 데 도움을 많이 받기도 했습니다.
그중에서 가장 인상에 깊은 책은 플랫폼 레볼레션인 것 같습니다.
비록 후반쪽은 집중하기가 힘들었지만 처음 150페이지까지는 정말 탄식을 나오는 내용들을 읽게 되었습니다.
플랫폼이 어떻게 만들어지고 무엇을 통해 입소문을 낼 수가 있으며 살아남는 방법에 대해 분석해주었습니다.
그 밖에도 여러가지 책들을 읽게 되었습니다.
100일간 읽은 책이 약 20권에 달하게 되었습니다.
클린 소프트웨어 |
마인드 셋 |
플렛폼 레볼루션 |
신경끄기의 기술 |
데이터는 답을 알고 있다 |
근데 영화 한편 씹어봤니? |
그로스 해킹 |
쏟아지는일 완벽하게 해내는 법 |
골목의 전쟁 |
히트메이커스 |
나는 4시간만 일한다 |
거절당하기 연습 |
인플루언서 마케팅 |
소프트웨어 장인 |
헬로우 데이터과학 |
장병규의 스타트업한국 |
코틀린인액션 |
아마존 세상의 모든것을 팝니다 |
경매통장 |
나는 돈이 없어도 사업을 한다 |
이밖에도 기획의 정석,블록체인 관련 책들을 읽었습니다.
그리고 덕분에 내가 할수 있는 자신감을 찾은 것 같다. 꾸준하게 할 수 있다는 자신감.!!
현재 120일정도 지난 시점에 글을 쓰게 된 이유는 새롭게 글쓰기 모임에 가입해서 뭘 쓸까 고민하다가 꼭 써보고 싶었던 후기를 결정하게 되었습니다.
글쓰는 건 자신이 없지만 처음부터 잘 쓰는 사람은 없으므로 계속 혼자보더라도 써볼 예정입니다. ^^
Emulator: Process finished with exit code -1073741819 (0) | 2018.12.21 |
---|---|
android studio에 sign keysotre 저장 방법 (0) | 2018.02.06 |
Android Unit Testing with Mockito (0) | 2018.02.01 |
Bottom sheet 정복해보자 (0) | 2018.01.29 |
Room 에서 리스트 저장 방법 (0) | 2018.01.26 |
영문
https://media.readthedocs.org/pdf/solidity/latest/solidity.pdf
http://solidity.readthedocs.io/en/develop/
한글
[GETH] GETH 명령어 모음 (0) | 2018.05.10 |
---|---|
크립토 키티 소스 (0) | 2018.04.30 |
블록체인 공부 모음 링크 (0) | 2018.04.13 |
SMT 백서 (0) | 2018.04.12 |
Emulator: Process finished with exit code -1073741819 (0) | 2018.12.21 |
---|---|
android clean archtecture mvvm link 모음 (0) | 2018.02.26 |
Android Unit Testing with Mockito (0) | 2018.02.01 |
Bottom sheet 정복해보자 (0) | 2018.01.29 |
Room 에서 리스트 저장 방법 (0) | 2018.01.26 |
RxJava
에서 자주 쓰이는 함수중 하나인 Collection
형태를 알아보자.
우선 toList()
이다.
여러개의 배출을 하나의 리스트로 만들어주는 것이다. 주의점은 리턴값이 Single<T>
인것을 유념하자.
아주 간단한 예제를 보자.
ToList
fun testToList(){
Observable.just("Alpha" , "Beta" , "Gamma" , "Delta" , "Epslion")
.toList() //Single로 배출
.subscribeBy(
onSuccess = {
println(it)
}
)
}
====================================
[Alpha, Beta, Gamma, Delta, Epslion]
정렬을 원할땐 ToSortedList
를 사용하면 된다.
ToSortedList
Observable.just(3,1,5,20,2,7)
.toSortedList()
.subscribeBy(
onSuccess = {
println(it)
}
)
====================================
[1, 2, 3, 5, 7, 20]
Key + Value 로 묶을수 있는 Map 도 제공한다.
ToMap
Observable.just("Alpha" , "Beta" , "Gamma" , "Delta" , "Epslion")
.toMap { it.toCharArray()[0] }
.subscribeBy(
onSuccess = {
println(it)
}
)
// {A=Alpha, B=Beta, D=Delta, E=Epslion, G=Gamma}
ToMap 인자를 다르게
Observable.just("Alpha" , "Beta" , "Gamma" , "Delta" , "Epslion")
.toMap({
it.toCharArray()[0]
},
String::length,{
ConcurrentHashMap()
})
.subscribeBy(
onSuccess = {
println(it)
}
)
//{A=5, B=4, D=5, E=7, G=5}
만약 같은 키에 여러개의 중복된 값이 있는 경우 마지막 아이템이 나온다.
Observable.just("Alpha" , "Beta" , "Gamma" , "Delta" , "Epslion")
.toMap(String::length)
.subscribeBy(
onSuccess = {
println(it)
}
)
// {4=Beta, 5=Delta, 7=Epslion}
그래서 이 부분을 수정하면 toMultiMap 이란것도 제공한다.
ToMultiMap
Observable.just("Alpha" , "Beta" , "Gamma" , "Delta" , "Epslion")
.toMultimap(String::length)
.subscribeBy(::println)
// {4=[Beta], 5=[Alpha, Gamma, Delta], 7=[Epslion]}
[RxKotlin] 예제로 배워보는 그룹핑 방법 (0) | 2018.01.24 |
---|---|
[RxKotlin] 두개의 리스트를 비교시 - Combinlatest 사용 (0) | 2018.01.23 |
[RxKotlin] Lift 연산자에 대해서 알아보자 (0) | 2018.01.22 |
RxKotlin Using 사용해보자 (0) | 2018.01.19 |
RxKotlin 정리 (0) | 2017.11.06 |
android clean archtecture mvvm link 모음 (0) | 2018.02.26 |
---|---|
android studio에 sign keysotre 저장 방법 (0) | 2018.02.06 |
Bottom sheet 정복해보자 (0) | 2018.01.29 |
Room 에서 리스트 저장 방법 (0) | 2018.01.26 |
tool 을 사용해서 더미 데이터들을 보여주자. (0) | 2018.01.22 |
android studio에 sign keysotre 저장 방법 (0) | 2018.02.06 |
---|---|
Android Unit Testing with Mockito (0) | 2018.02.01 |
Room 에서 리스트 저장 방법 (0) | 2018.01.26 |
tool 을 사용해서 더미 데이터들을 보여주자. (0) | 2018.01.22 |
gradle에서 빌드 정보를 삭제하자 (0) | 2018.01.18 |
노마드 최신뉴스 모음 (2월 ~ ) (0) | 2018.05.08 |
---|---|
영어공부 팁 모음 (0) | 2017.11.25 |
영어 저작권 없는 자료 링크 (0) | 2017.11.24 |
카카오톡 개인정보 약관 저장 (0) | 2017.11.21 |
소스 검색 방법 펌 (0) | 2017.11.18 |
var fs = require('fs');
var array = fs.readFileSync('file.txt').toString().split("\n");
for(i in array) {
console.log(array[i]);
}
var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
if(err) throw err;
var array = data.toString().split("\n");
for(i in array) {
console.log(array[i]);
}
});
이름있는 배열을 JSON으로 변경시 처리 (0) | 2018.03.13 |
---|---|
request 시 한글깨짐 문제 [펌] (0) | 2018.02.08 |
새로나온 룸 에서는 리스트형태가 저장이 안된다.
그래서 json string으로 변환 후에 저장했다가 꺼낼때에 다시 변환해야 합니다.
Android Unit Testing with Mockito (0) | 2018.02.01 |
---|---|
Bottom sheet 정복해보자 (0) | 2018.01.29 |
tool 을 사용해서 더미 데이터들을 보여주자. (0) | 2018.01.22 |
gradle에서 빌드 정보를 삭제하자 (0) | 2018.01.18 |
코틀린으로 짜는 안드로이드 구글맵 가이드 (0) | 2018.01.18 |
하나의 Observable을 여러개의 옵저버로 패턴에 맞게 분리하는 방법에 대해서 알아보자.
4개의 문자 배열이 있다.
"Alpha" , "Beta" , "Delat" , "Epsilon"
이걸 문자 길이에 맞게끔 분리를 해보자.
val source = Observable.just("Alpha" , "Beta" , "Delat" , "Epsilon")
val lengthGroupObservable = source.groupBy { it.length } //groupBy 가 중요함
lengthGroupObservable.flatMapSingle { it.toList() }
.subscribeBy(
onNext = {
println(it)
}
)
======================
[Beta]
[Alpha, Delat]
[Epsilon]
보는 것과 같이 문자열 수에 따라 나뉘어 진다.
반환형태는 GroupedObservable
으로 나온다.
그리고 정렬시 키값을 가져올 수 있다.
여기서 말하는 키값은 length 가 될것이다.
val source = Observable.just("Alpha" , "Beta" , "Delat" , "Epsilon" , "Te")
val lengthGroupObservable = source.groupBy { it.length }
lengthGroupObservable.flatMapSingle {grp ->
grp.reduce("" , { x,y ->
if(x.isEmpty()){
y
}else{
"$x,$y"
}
}).map {
"${grp.key} : $it"
}
}
.subscribeBy(
onNext = {
println(it)
}
)
===================================
2 : Te
4 : Beta
5 : Alpha,Delat
7 : Epsilon
혹시 reduce
관련 해서 기억이 안날수 있으니 다시 설명하자면
발행한 데이터를 모두 사용하여 어떤 최종적인 결과 데이터를 합성할 때 활용가능하다.
즉 보통 Observable에 입력된 데이터를 필요한 map으로 매핑하고 , 원하는 데이터만 추출할 때 는 불필요한 데이터를 걸러내는 filter() 함수를 호출한다. 또는 상황에 따라 발행된 데이터를 취합하여 어떤 결과를 만들어낼 때는 reduce 계열의 함수를 사용한다.
val sources = listOf("1","2","3")
Observable.fromIterable(sources).reduce{ value1 , value2 ->
"$value2 ( $value1 )"
}.subscribe( ::println )
============================
3 ( 2 ( 1 ) )
[RxKotlin] Collection 함수 기본 알아보기 (0) | 2018.02.01 |
---|---|
[RxKotlin] 두개의 리스트를 비교시 - Combinlatest 사용 (0) | 2018.01.23 |
[RxKotlin] Lift 연산자에 대해서 알아보자 (0) | 2018.01.22 |
RxKotlin Using 사용해보자 (0) | 2018.01.19 |
RxKotlin 정리 (0) | 2017.11.06 |
매일 15분 전공 공부팀에서 2기 팀을 모집합니다.
1기는 무사히 100일 완성을 하신분들이 있으십니다. 멋집니다. ^^
회사일에 치이고 집안일에 치이는 바쁘게 보낸 작년 한해를 돌아보면서 과연 자신에 대한 투자는 얼마나 했는지 반성을 많이 하게 됩니다.
여러분들은 어떠신가요?
그래서 이번 년도에서는 자신의 스킬업을 위해 매일 최소한 15분이상 투자를 하기로 했습니다.
자신의 전공(or 업무)에 대해 개인적으로 짬을 내서 15분이상 책을 읽거나 코드를 따라 쳐도 되고 어떠한 공부를 하든지 인증만 하면 됩니다.
혼자가면 지루하고 포기하기 쉽지만 같이 가면 자극도 되고 힘을 내서 같이 끝까지 갈수 있을 거라 생각합니다.
링크 주소 클릭시 입장 가능합니다.
인증방식은 다음과 같습니다. ( 개발 관련이면 됩니다. )
예제 ) 이름/연속일자/공부내용/공부시간/링크주소등
- 수지아빠/10/Android 테스팅공부/15/http://google.com
- 홍길동/2/아이폰개발/60
- 아이뻐/10/개인앱개발/120
만약 바빠서 인증이 힘든 하루이면 찬스를 사용하셔도 됩니다.
올해는 꼭 많은 공부를 해서 좀 더 나은 개발자로 거듭납시다.
링크 주소 클릭시 입장 가능합니다.
Combinelatest
두개의 리스트를 비교하는 걸 구현한다고 가정했을때 여러 가지 방법이 있을테지만
그중에 conbinelatest 를 사용해서 리스트와 각각의 값을 비교하는 방법으로 진행해보자.
다이어그램은 다음과 같다.
val observable1 = Observable.fromArray("A" , "B" , "C" , "D")
val observable2 = Observable.fromArray("E" , "C" , "B" , "G","F")
Observables.combineLatest( observable1.toList().toObservable() , observable2 ){ list , value ->
println("$list <> $value")
if(list.contains(value)) value else ""
}
.filter { !it.isEmpty() }
.subscribeBy(
onNext = {
println("value -> $it")
}
)
====================
[A, B, C, D] <> E
[A, B, C, D] <> C
value -> C
[A, B, C, D] <> B
value -> B
[A, B, C, D] <> G
[A, B, C, D] <> F
우선 zip 과 combinelatest 성격은 합쳐주는데 있지만 조합하는 게 좀 다르다.
우선 zip을 보자,
순서대로 묶여서 나오는걸 볼 수 있다.
val observable1= Observable.interval(100 , TimeUnit.MILLISECONDS)
val observable2 = Observable.interval( 250 , TimeUnit.MILLISECONDS)
Observable.zip(observable1 , observable2 , BiFunction{ t1 : Long , t2 : Long ->
"t1 : $t1 , t2 : $t2"
}).subscribe{
println("값 -> $it")
}
Thread.sleep(1000)
=======================
값 -> t1 : 0 , t2 : 0
값 -> t1 : 1 , t2 : 1
값 -> t1 : 2 , t2 : 2
값 -> t1 : 3 , t2 : 3
그럼 combinelatest를 보자.
val observable1= Observable.interval(100 , TimeUnit.MILLISECONDS)
val observable2 = Observable.interval( 250 , TimeUnit.MILLISECONDS)
Observable.combineLatest(observable1 , observable2 , BiFunction{ t1 : Long , t2 : Long ->
"t1 : $t1 , t2 : $t2"
}).subscribe{
println("값 -> $it")
}
Thread.sleep(1000)
======================
값 -> t1 : 1 , t2 : 0
값 -> t1 : 2 , t2 : 0 <- 먼저 나온것과 이전 나온것을 묶여서 보여주는 걸 볼 수 있다.
값 -> t1 : 3 , t2 : 0
값 -> t1 : 4 , t2 : 0
값 -> t1 : 4 , t2 : 1
값 -> t1 : 5 , t2 : 1
값 -> t1 : 6 , t2 : 1
값 -> t1 : 6 , t2 : 2
값 -> t1 : 7 , t2 : 2
값 -> t1 : 8 , t2 : 2
[RxKotlin] Collection 함수 기본 알아보기 (0) | 2018.02.01 |
---|---|
[RxKotlin] 예제로 배워보는 그룹핑 방법 (0) | 2018.01.24 |
[RxKotlin] Lift 연산자에 대해서 알아보자 (0) | 2018.01.22 |
RxKotlin Using 사용해보자 (0) | 2018.01.19 |
RxKotlin 정리 (0) | 2017.11.06 |
Bottom sheet 정복해보자 (0) | 2018.01.29 |
---|---|
Room 에서 리스트 저장 방법 (0) | 2018.01.26 |
gradle에서 빌드 정보를 삭제하자 (0) | 2018.01.18 |
코틀린으로 짜는 안드로이드 구글맵 가이드 (0) | 2018.01.18 |
Android mimeType 정리 (0) | 2018.01.15 |
Lift 연산자
연산자 오버로딩 하는 방법 줌 RxJava에서는 compose 와 lift 가 대표적으로 사용된다.
compose 는 전 내용에서 설명해놓은 내용이 있다.
그럼 lift는 차이점이 무엇인가.
Lift는 연산자를 오버로딩해서 새로 만드는 걸 목적으로 하며
compose는 여러 연산자를 하나의 연산자로 만드는게 주 목적이라고 생각된다.
우선 lift 구현 동작 부터 확인해보자.
interface ObservableOperator<Downstream, Upstream> { /** * Applies a function to the child Observer and returns a new parent Observer. * @PARAM observer the child Observer instance * @return the parent Observer instance * @THROWS Exception on failure */ @NonNull @Throws(Exception::class) fun apply(@NonNull observer: Observer<in Downstream>): Observer<in Upstream>; }
그럼 Lift 실제 사용법은 어떤지 확인해보자.
우선 예제 목적은 각각의 수에 앞에 번호를 붙어는 간단한 예제이다.
//T 형태의 내용을 받아서 Pair 로 변경해서 보내는 예제이다. class AddSerialNumber<T> : ObservableOperator<Pair<Int , T>, T> { override fun apply(observer: Observer<in Pair<Int, T>>): Observer<in T> { val counter = AtomicInteger()
return object : Observer<T> { override fun onSubscribe(d: Disposable) { println("onSubscribe") observer.onSubscribe(d) }
override fun onNext(t: T) { println("onNext") observer.onNext( counter.incrementAndGet() to t) }
override fun onComplete() { println("onComplete #1") observer.onComplete() }
override fun onError(e: Throwable) { println("onError #1") observer.onError(e) }
} }}
Observable.range(10,20) .lift(AddSerialNumber<Int>()) //<-- 리프트 연산자 추가 .subscribeBy( onNext = { println("Next $it") }, onError = { it.printStackTrace() }, onComplete = { println("Completed") } )
==============================================onSubscribeonNextNext (1, 10)onNextNext (2, 11)onNextNext (3, 12)onNextNext (4, 13)onNextNext (5, 14)onNextNext (6, 15)onNextNext (7, 16)onNextNext (8, 17)onNextNext (9, 18)onNextNext (10, 19)onNextNext (11, 20)onNextNext (12, 21)onNextNext (13, 22)onNextNext (14, 23)onNextNext (15, 24)onNextNext (16, 25)onNextNext (17, 26)onNextNext (18, 27)onNextNext (19, 28)onNextNext (20, 29)onComplete #1Completed
이런식으로 앞에 번호를 붙일수가 있다.
[RxKotlin] 예제로 배워보는 그룹핑 방법 (0) | 2018.01.24 |
---|---|
[RxKotlin] 두개의 리스트를 비교시 - Combinlatest 사용 (0) | 2018.01.23 |
RxKotlin Using 사용해보자 (0) | 2018.01.19 |
RxKotlin 정리 (0) | 2017.11.06 |
[RxJava2]동시성과 병렬화 테스트 (0) | 2017.10.22 |
AWS공식 문서들
아래 링크들은 어떠한 내용이 있는지 정도만이라도 한번 훑어 보셔도 많은 도움이 됩니다.
AWS Well-Architected 프레임워크(한글화된 문서와 강연영상)
https://aws.amazon.com/ko/blogs/korea/aws-well-architected-framework-in-korean/
AWS 한국어 설명서 목록: AWS학습과 사용을 위해서 꼭 한번씩은 읽어 보셔야 하는 문서들 입니다.
https://aws.amazon.com/ko/blogs/korea/ko-documentation/
AWS한국어 기술 백서 목록: AWS 기술 백서는 아키텍처, 보안, 경제성과 같은 다양한 주제를 포괄적으로 다루고 있습니다.
https://aws.amazon.com/ko/blogs/korea/ko-whitepapers/
AWS 한국 블로그 전체 목록 : 유용한 AWS최신 기술 문서들과 다양한 이벤트 소식을 만나보실 수 있습니다.
https://aws.amazon.com/ko/blogs/korea/index/
AWS 한국 블로그 기술 문서: AWS 한국 블로그의 글 중에서 기술적 사항에 관련된 문서들 입니다.
https://aws.amazon.com/ko/blogs/korea/category/korea-techtips/
AWS기반 빅데이터 솔루션 안내
https://aws.amazon.com/ko/big-data/
AWS 학습 링크집
각각의 목적에 맞추어 작성한 링크집 입니다.
AWS 보안 관련 컨텐츠 모음집
비공식 AWS 공인 솔루션스 아키텍트 - 어소시에이트 수험 가이드
비공식 AWS 공인 개발자 - 어소시에이트 수험 가이드
실습사이트
QwikLABS: 수업때 사용했던 실습시스템과 비슷하지만 별도로 가입을 하셔야만 합니다.
백문이 불여일런(Run) 이라는 말 도 있듯이 AWS를 이해하고 사용하는데엔 실습이 큰 역할을 합니다.
강의는 1크래딧이 1달러이고 무료해 보실 수 있는 실습도 많이 있으니 내용 꼭 확인해 보시기 바랍니다.
실습용 CloudFormation 템플릿: 각자 계정으로 실습을 진행할 수 있는 CloudFormation 템플릿입니다. 실습에 따라 과금이 될 수도 있으므로 실습을 마친 후에는 반드시 해당 스택을 삭제해 주시기 바랍니다.
아키텍처 다이어그램용 AWS 심플 아이콘
https://aws.amazon.com/ko/architecture/icons/
아키텍처 다이어그램 제작 서비스
환경 자동구축 툴
필요한 자료들을 손쉽게 찾는 방법
구글링 : 가장 효율적인 방법입니다. 검색키워드는 AWS + 찾고자 하시는 사항의 키워드 로 쉽게 검색이 가능합니다.
유튜브: 서비스나 특정 기술에 대한 개념을 이해하기 위해서는 문서를 읽는것 보다 시각화된 설명을보고 듣는것이 빠를때가 많습니다. 유튜브에는 2분에서 5분사이로 AWS의 서비스들에 대해서 소개하는 영상들이 많이 올라와 있으니 서비스에 대한 개념을 잡아 나가실때 유용하게 사용하시기 바랍니다.
자격증관련
AWS 공인 솔루션 아키텍트 자격증 관련 정보 링크 모음집
AWS공인 개발자 자격증 관련 정보 링크 모음집
AWS 자격증 안내 페이지
https://aws.amazon.com/ko/certification/
AWS 공인 솔루션스 아키텍트 – 어소시에이트 자격증 안내
https://aws.amazon.com/ko/certification/certified-solutions-architect-associate/
AWS 공인 솔루션스 아키텍트 – 어소시에이트 샘플 문제
영문 시험 시간 추가 요청 방법(시험 신청 전에 요청하셔야 합니다!)
http://edu.supertrack.co.kr/notice/news.php?ptype=view&idx=5177&page=1&code=news
한국 AWS사용자 모임에서는 사용레벨별(초급자,중급자,고급자) , 관심사별(엔터프라이즈, IoT, Serverless), 지역별(강남,판교) 그리고 자격증취득 준비 모임등 다양한 관심사별로 소모임을 운영하고 있으며 소모임에서는 실습 서비스인 QwikLab의 쿠폰이나 AWS크래딧을 받으실 수 있습니다.
https://www.facebook.com/groups/awskrug/
그 밖의 읽을거리
12 Factor Apps : 클라우드 개발을 위한 12가지 모범사례
리액티브 선언문
http://www.reactivemanifesto.org/ko
도메인 주도 설계