forked from mradkov/aeternity-fungible-token
-
Notifications
You must be signed in to change notification settings - Fork 4
/
fungible-token.aes
126 lines (102 loc) · 4.71 KB
/
fungible-token.aes
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// ISC License
//
// Copyright (c) 2017, aeternity developers
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// THIS IS NOT SECURITY AUDITED
// DO NEVER USE THIS WITHOUT SECURITY AUDIT FIRST
@compiler >= 5
include "String.aes"
/// @title - Fungible token basic
contract FungibleToken =
// This defines the state of type record encapsulating the contract's mutable state
record state =
{ owner : address // the smart contract's owner address
, total_supply : int // total token supply
, balances : balances // balances for each account
, meta_info : meta_info } // token meta info (name, symbol, decimals)
// This is the meta-information record type
record meta_info =
{ name : string
, symbol : string
, decimals : int }
// This is a type alias for the balances map
type balances = map(address, int)
// Declaration and structure of datatype event
// and events that will be emitted on changes
datatype event =
Transfer(address, address, int)
| Mint(address, int)
// List of implemented extensions for the deployed contract
entrypoint aex9_extensions() : list(string) = []
// Create a fungible token with
// the following `name` `symbol` and `decimals`
// and set the inital smart contract state
entrypoint init(name: string, decimals : int, symbol : string, initial_owner_balance : option(int)) =
// If the `name` lenght is less than 1 symbol abort the execution
require(String.length(name) >= 1, "STRING_TOO_SHORT_NAME")
// If the `symbol` length is less than 1 symbol abort the execution
require(String.length(symbol) >= 1, "STRING_TOO_SHORT_SYMBOL")
// If the provided value for `decimals` is negative abort the execution
require_non_negative_value(decimals)
let owner = Call.caller
let (initial_supply, initial_balances) = switch(initial_owner_balance)
Some(initial_balance) =>
// If negative initial owner balance is passed, abort the execution
require_non_negative_value(initial_balance)
// Emit Mint event to signal token supply has been created
Chain.event(Mint(owner, initial_balance))
(initial_balance, { [owner] = initial_balance })
None => (0, {})
{ owner = owner,
total_supply = initial_supply,
balances = initial_balances,
meta_info = { name = name, symbol = symbol, decimals = decimals } }
// Get the token meta info
entrypoint meta_info() : meta_info =
state.meta_info
// Get the token total supply
entrypoint total_supply() : int =
state.total_supply
// Get the token owner address
entrypoint owner() : address =
state.owner
// Get the balances state
entrypoint balances() : balances =
state.balances
// Get balance for address of `owner`
// returns option(int)
// If the `owner` address haven't had any token balance
// in this smart contract the return value is None
// Otherwise Some(int) is returned with the current balance
entrypoint balance(account: address) : option(int) =
Map.lookup(account, state.balances)
/// Transfer the balance of `value` from `Call.caller` to `to_account` account
stateful entrypoint transfer(to_account: address, value: int) =
internal_transfer(Call.caller, to_account, value)
// INTERNAL FUNCTIONS
function require_owner() =
require(Call.caller == state.owner, "ONLY_OWNER_CALL_ALLOWED")
function require_non_negative_value(value : int) =
require(value >= 0, "NON_NEGATIVE_VALUE_REQUIRED")
function require_balance(account : address, value : int) =
switch(balance(account))
Some(balance) =>
require(balance >= value, "ACCOUNT_INSUFFICIENT_BALANCE")
None => abort("BALANCE_ACCOUNT_NOT_EXISTENT")
stateful function internal_transfer(from_account: address, to_account: address, value: int) =
require_non_negative_value(value)
require_balance(from_account, value)
put(state{ balances[from_account] @ b = b - value })
put(state{ balances[to_account = 0] @ b = b + value })
Chain.event(Transfer(from_account, to_account, value))