-
Notifications
You must be signed in to change notification settings - Fork 77
/
Copy pathMRC20.ts
307 lines (260 loc) · 9.03 KB
/
MRC20.ts
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
import {
Address,
Context,
generateEvent,
Storage,
isDeployingContract,
} from '@massalabs/massa-as-sdk';
import { Args, stringToBytes, u256ToBytes } from '@massalabs/as-types';
import { _balance, _setBalance, _approve, _allowance } from './MRC20-internals';
import { setOwner } from '../utils/ownership';
import { u256 } from 'as-bignum/assembly';
export const VERSION = stringToBytes('0.0.1');
const TRANSFER_EVENT_NAME = 'TRANSFER SUCCESS';
const APPROVAL_EVENT_NAME = 'APPROVAL SUCCESS';
export const NAME_KEY = stringToBytes('NAME');
export const SYMBOL_KEY = stringToBytes('SYMBOL');
export const TOTAL_SUPPLY_KEY = stringToBytes('TOTAL_SUPPLY');
export const DECIMALS_KEY = stringToBytes('DECIMALS');
/**
* Initialize the ERC20 contract
*
* @remarks You must call this function in your contract's constructor or re-write it to fit your needs !
*
* @param name - the name of the token
* @param symbol - the symbol of the token
* @param decimals - the number of decimals
* @param totalSupply - the total supply of the token
*/
export function mrc20Constructor(
name: string,
symbol: string,
decimals: u8,
totalSupply: u256,
): void {
assert(isDeployingContract());
Storage.set(NAME_KEY, stringToBytes(name));
Storage.set(SYMBOL_KEY, stringToBytes(symbol));
Storage.set(DECIMALS_KEY, [decimals]);
Storage.set(TOTAL_SUPPLY_KEY, u256ToBytes(totalSupply));
setOwner(new Args().add(Context.caller().toString()).serialize());
_setBalance(Context.caller(), totalSupply);
}
/**
* Returns the version of this smart contract.
* This versioning is following the best practices defined in https://semver.org/.
*
* @param _ - unused see https://github.com/massalabs/massa-sc-std/issues/18
* @returns token version
*/
export function version(_: StaticArray<u8>): StaticArray<u8> {
return VERSION;
}
// ======================================================== //
// ==== TOKEN ATTRIBUTES ==== //
// ======================================================== //
/**
* Returns the name of the token.
*
* @param _ - unused see https://github.com/massalabs/massa-sc-std/issues/18
* @returns token name.
*/
export function name(_: StaticArray<u8>): StaticArray<u8> {
return Storage.get(NAME_KEY);
}
/** Returns the symbol of the token.
*
* @param _ - unused see https://github.com/massalabs/massa-sc-std/issues/18
* @returns token symbol.
*/
export function symbol(_: StaticArray<u8>): StaticArray<u8> {
return Storage.get(SYMBOL_KEY);
}
/**
* Returns the total token supply.
*
* The number of tokens that were initially minted.
*
* @param _ - unused see https://github.com/massalabs/massa-sc-std/issues/18
* @returns u256
*/
export function totalSupply(_: StaticArray<u8>): StaticArray<u8> {
return Storage.get(TOTAL_SUPPLY_KEY);
}
/**
* Returns the maximum number of digits being part of the fractional part
* of the token when using a decimal representation.
*
* @param _ - unused see https://github.com/massalabs/massa-sc-std/issues/18
* @returns
*/
export function decimals(_: StaticArray<u8>): StaticArray<u8> {
return Storage.get(DECIMALS_KEY);
}
// ==================================================== //
// ==== BALANCE ==== //
// ==================================================== //
/**
* Returns the balance of an account.
*
* @param binaryArgs - Args object serialized as a string containing an owner's account (Address).
*/
export function balanceOf(binaryArgs: StaticArray<u8>): StaticArray<u8> {
const args = new Args(binaryArgs);
const addr = new Address(
args.nextString().expect('Address argument is missing or invalid'),
);
return u256ToBytes(_balance(addr));
}
// ==================================================== //
// ==== TRANSFER ==== //
// ==================================================== //
/**
* Transfers tokens from the caller's account to the recipient's account.
*
* @param binaryArgs - Args object serialized as a string containing:
* - the recipient's account (address)
* - the number of tokens (u256).
*/
export function transfer(binaryArgs: StaticArray<u8>): void {
const owner = Context.caller();
const args = new Args(binaryArgs);
const toAddress = new Address(
args.nextString().expect('receiverAddress argument is missing or invalid'),
);
const amount = args
.nextU256()
.expect('amount argument is missing or invalid');
_transfer(owner, toAddress, amount);
generateEvent(TRANSFER_EVENT_NAME);
}
/**
* Transfers tokens from the caller's account to the recipient's account.
*
* @param from - sender address
* @param to - recipient address
* @param amount - number of token to transfer
*
* @returns true if the transfer is successful
*/
function _transfer(from: Address, to: Address, amount: u256): void {
assert(from != to, 'Transfer failed: cannot send tokens to own account');
const currentFromBalance = _balance(from);
const currentToBalance = _balance(to);
// @ts-ignore
const newToBalance = currentToBalance + amount;
assert(currentFromBalance >= amount, 'Transfer failed: insufficient funds');
assert(newToBalance >= currentToBalance, 'Transfer failed: overflow');
// @ts-ignore
_setBalance(from, currentFromBalance - amount);
_setBalance(to, newToBalance);
}
// ==================================================== //
// ==== ALLOWANCE ==== //
// ==================================================== //
/**
* Returns the allowance set on the owner's account for the spender.
*
* @param binaryArgs - Args object serialized as a string containing:
* - the owner's account (address)
* - the spender's account (address).
*/
export function allowance(binaryArgs: StaticArray<u8>): StaticArray<u8> {
const args = new Args(binaryArgs);
const owner = new Address(
args.nextString().expect('owner argument is missing or invalid'),
);
const spenderAddress = new Address(
args.nextString().expect('spenderAddress argument is missing or invalid'),
);
return u256ToBytes(_allowance(owner, spenderAddress));
}
/**
* Increases the allowance of the spender on the owner's account by the amount.
*
* This function can only be called by the owner.
*
* @param binaryArgs - Args object serialized as a string containing:
* - the spender's account (address);
* - the amount (u256).
*/
export function increaseAllowance(binaryArgs: StaticArray<u8>): void {
const owner = Context.caller();
const args = new Args(binaryArgs);
const spenderAddress = new Address(
args.nextString().expect('spenderAddress argument is missing or invalid'),
);
const amount = args
.nextU256()
.expect('amount argument is missing or invalid');
// @ts-ignore
let newAllowance = _allowance(owner, spenderAddress) + amount;
if (newAllowance < amount) {
newAllowance = u256.Max;
}
_approve(owner, spenderAddress, newAllowance);
generateEvent(APPROVAL_EVENT_NAME);
}
/**
* Decreases the allowance of the spender the on owner's account by the amount.
*
* This function can only be called by the owner.
*
* @param binaryArgs - Args object serialized as a string containing:
* - the spender's account (address);
* - the amount (u256).
*/
export function decreaseAllowance(binaryArgs: StaticArray<u8>): void {
const owner = Context.caller();
const args = new Args(binaryArgs);
const spenderAddress = new Address(
args.nextString().expect('spenderAddress argument is missing or invalid'),
);
const amount = args
.nextU256()
.expect('amount argument is missing or invalid');
const current = _allowance(owner, spenderAddress);
let newAllowance = u256.Zero;
if (current > amount) {
// @ts-ignore
newAllowance = current - amount;
}
_approve(owner, spenderAddress, newAllowance);
generateEvent(APPROVAL_EVENT_NAME);
}
/**
* Transfers token ownership from the owner's account to the recipient's account
* using the spender's allowance.
*
* This function can only be called by the spender.
* This function is atomic:
* - both allowance and transfer are executed if possible;
* - or if allowance or transfer is not possible, both are discarded.
*
* @param binaryArgs - Args object serialized as a string containing:
* - the owner's account (address);
* - the recipient's account (address);
* - the amount (u256).
*/
export function transferFrom(binaryArgs: StaticArray<u8>): void {
const spenderAddress = Context.caller();
const args = new Args(binaryArgs);
const owner = new Address(
args.nextString().expect('ownerAddress argument is missing or invalid'),
);
const recipient = new Address(
args.nextString().expect('recipientAddress argument is missing or invalid'),
);
const amount = args
.nextU256()
.expect('amount argument is missing or invalid');
const spenderAllowance = _allowance(owner, spenderAddress);
assert(
spenderAllowance >= amount,
'transferFrom failed: insufficient allowance',
);
_transfer(owner, recipient, amount);
// @ts-ignore
_approve(owner, spenderAddress, spenderAllowance - amount);
generateEvent(TRANSFER_EVENT_NAME);
}