Skip to content

Commit cf29ed0

Browse files
committed
Add basic IRC2 token implementation
1 parent 1fef4d3 commit cf29ed0

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed

tokens/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ java {
66
sourceCompatibility = JavaVersion.VERSION_11
77
targetCompatibility = JavaVersion.VERSION_11
88
}
9+
10+
dependencies {
11+
compileOnly 'foundation.icon:javaee-api:0.8.6'
12+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2020 ICONLOOP Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.iconloop.score.token.irc2;
18+
19+
import score.Address;
20+
import score.annotation.Optional;
21+
22+
import java.math.BigInteger;
23+
24+
public interface IRC2 {
25+
/**
26+
* Returns the name of the token. (e.g. "MySampleToken")
27+
*/
28+
String name();
29+
30+
/**
31+
* Returns the symbol of the token. (e.g. "MST")
32+
*/
33+
String symbol();
34+
35+
/**
36+
* Returns the number of decimals the token uses. (e.g. 18)
37+
*/
38+
int decimals();
39+
40+
/**
41+
* Returns the total token supply.
42+
*/
43+
BigInteger totalSupply();
44+
45+
/**
46+
* Returns the account balance of another account with address {@code _owner}.
47+
*/
48+
BigInteger balanceOf(Address _owner);
49+
50+
/**
51+
* Transfers {@code _value} amount of tokens to address {@code _to}, and MUST fire the {@code Transfer} event.
52+
* This function SHOULD throw if the caller account balance does not have enough tokens to spend.
53+
* If {@code _to} is a contract, this function MUST invoke the function {@code tokenFallback(Address, int, bytes)}
54+
* in {@code _to}. If the {@code tokenFallback} function is not implemented in {@code _to} (receiver contract),
55+
* then the transaction must fail and the transfer of tokens should not occur.
56+
* If {@code _to} is an externally owned address, then the transaction must be sent without trying to execute
57+
* {@code tokenFallback} in {@code _to}. {@code _data} can be attached to this token transaction.
58+
* {@code _data} can be empty.
59+
*/
60+
void transfer(Address _to, BigInteger _value, @Optional byte[] _data);
61+
62+
/**
63+
* (EventLog) Must trigger on any successful token transfers.
64+
*/
65+
void Transfer(Address _from, Address _to, BigInteger _value, byte[] _data);
66+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2020 ICONLOOP Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.iconloop.score.token.irc2;
18+
19+
import score.Address;
20+
import score.Context;
21+
import score.DictDB;
22+
import score.annotation.EventLog;
23+
import score.annotation.External;
24+
import score.annotation.Optional;
25+
26+
import java.math.BigInteger;
27+
28+
public abstract class IRC2Basic implements IRC2 {
29+
private final String name;
30+
private final String symbol;
31+
private final int decimals;
32+
private final BigInteger totalSupply;
33+
private final DictDB<Address, BigInteger> balances;
34+
35+
public IRC2Basic(String _name, String _symbol, BigInteger _decimals, BigInteger _initialSupply) {
36+
this.name = _name;
37+
this.symbol = _symbol;
38+
this.decimals = _decimals.intValue();
39+
40+
// decimals must be larger than 0 and less than 21
41+
Context.require(this.decimals >= 0);
42+
Context.require(this.decimals <= 21);
43+
44+
// initialSupply must be larger than 0
45+
Context.require(_initialSupply.compareTo(BigInteger.ZERO) >= 0);
46+
47+
// calculate totalSupply
48+
if (_initialSupply.compareTo(BigInteger.ZERO) > 0) {
49+
BigInteger oneToken = pow10(this.decimals);
50+
this.totalSupply = oneToken.multiply(_initialSupply);
51+
} else {
52+
this.totalSupply = BigInteger.ZERO;
53+
}
54+
55+
// set the initial balance of the owner
56+
this.balances = Context.newDictDB("balances", BigInteger.class);
57+
this.balances.set(Context.getOrigin(), this.totalSupply);
58+
}
59+
60+
// BigInteger#pow() is not implemented in the shadow BigInteger.
61+
// we need to use our implementation for that.
62+
private static BigInteger pow10(int exponent) {
63+
BigInteger result = BigInteger.ONE;
64+
for (int i = 0; i < exponent; i++) {
65+
result = result.multiply(BigInteger.TEN);
66+
}
67+
return result;
68+
}
69+
70+
@External(readonly=true)
71+
public String name() {
72+
return name;
73+
}
74+
75+
@External(readonly=true)
76+
public String symbol() {
77+
return symbol;
78+
}
79+
80+
@External(readonly=true)
81+
public int decimals() {
82+
return decimals;
83+
}
84+
85+
@External(readonly=true)
86+
public BigInteger totalSupply() {
87+
return totalSupply;
88+
}
89+
90+
@External(readonly=true)
91+
public BigInteger balanceOf(Address _owner) {
92+
return safeGetBalance(_owner);
93+
}
94+
95+
@External
96+
public void transfer(Address _to, BigInteger _value, @Optional byte[] _data) {
97+
Address _from = Context.getCaller();
98+
99+
// check some basic requirements
100+
Context.require(_value.compareTo(BigInteger.ZERO) >= 0);
101+
Context.require(safeGetBalance(_from).compareTo(_value) >= 0);
102+
103+
// adjust the balances
104+
safeSetBalance(_from, safeGetBalance(_from).subtract(_value));
105+
safeSetBalance(_to, safeGetBalance(_to).add(_value));
106+
107+
// if the recipient is SCORE, call 'tokenFallback' to handle further operation
108+
byte[] dataBytes = (_data == null) ? new byte[0] : _data;
109+
if (_to.isContract()) {
110+
Context.call(_to, "tokenFallback", _from, _value, dataBytes);
111+
}
112+
113+
// emit Transfer event
114+
Transfer(_from, _to, _value, dataBytes);
115+
}
116+
117+
protected void _burn(Address owner, BigInteger amount) {
118+
// TODO: burn
119+
}
120+
121+
private BigInteger safeGetBalance(Address owner) {
122+
return balances.getOrDefault(owner, BigInteger.ZERO);
123+
}
124+
125+
private void safeSetBalance(Address owner, BigInteger amount) {
126+
balances.set(owner, amount);
127+
}
128+
129+
@EventLog(indexed=3)
130+
public void Transfer(Address _from, Address _to, BigInteger _value, byte[] _data) {}
131+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2020 ICONLOOP Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.iconloop.score.token.irc2;
18+
19+
import score.Context;
20+
import score.annotation.External;
21+
22+
import java.math.BigInteger;
23+
24+
public abstract class IRC2Burnable extends IRC2Basic {
25+
public IRC2Burnable(String _name, String _symbol, BigInteger _decimals, BigInteger _initialSupply) {
26+
super(_name, _symbol, _decimals, _initialSupply);
27+
}
28+
29+
@External
30+
public void burn(BigInteger _amount) {
31+
_burn(Context.getCaller(), _amount);
32+
}
33+
}

0 commit comments

Comments
 (0)