Skip to content

Commit

Permalink
nvm: add NRC721BasicToken smart contract.
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonLi000 committed May 16, 2018
1 parent ffba9fe commit 626eff5
Show file tree
Hide file tree
Showing 2 changed files with 324 additions and 0 deletions.
100 changes: 100 additions & 0 deletions nf/nvm/engine_v8_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,106 @@ func TestNRC20ContractMultitimes(t *testing.T) {
}
}

func TestNRC721Contract(t *testing.T) {

tests := []struct {
test string
contractPath string
sourceType string
name string
symbol string
from string
to string
tokenID string
}{
{"nrc721", "./test/NRC721BasicToken.js", "js", "721标准代币", "ST",
"n1FkntVUMPAsESuCAAPK711omQk19JotBjM", "n1Kjom3J4KPsHKKzZ2xtt8Lc9W5pRDjeLcW", "1001",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := ioutil.ReadFile(tt.contractPath)
assert.Nil(t, err, "contract path read error")

mem, _ := storage.NewMemoryStorage()
context, _ := state.NewWorldState(dpos.NewDpos(), mem)
assert.Nil(t, err)

// prepare the contract.
contract, _ := context.CreateContractAccount([]byte("account2"), nil)
contract.AddBalance(newUint128FromIntWrapper(5))

// parepare env, block & transactions.
tx := mockNormalTransaction(tt.from, "n1TV3sU6jyzR4rJ1D7jCAmtVGSntJagXZHC", "0")
ctx, err := NewContext(mockBlock(), tx, contract, context)

// execute.
engine := NewV8Engine(ctx)
engine.SetExecutionLimits(10000, 100000000)
args := fmt.Sprintf("[\"%s\", \"%s\"]", tt.name, tt.symbol)
_, err = engine.DeployAndInit(string(data), tt.sourceType, args)
assert.Nil(t, err)
engine.Dispose()

// call name.
engine = NewV8Engine(ctx)
engine.SetExecutionLimits(10000, 100000000)
name, err := engine.Call(string(data), tt.sourceType, "name", "")
assert.Nil(t, err)
var nameStr string
err = json.Unmarshal([]byte(name), &nameStr)
assert.Nil(t, err)
assert.Equal(t, tt.name, nameStr)
engine.Dispose()

// call symbol.
engine = NewV8Engine(ctx)
engine.SetExecutionLimits(10000, 100000000)
symbol, err := engine.Call(string(data), tt.sourceType, "symbol", "")
assert.Nil(t, err)
var symbolStr string
err = json.Unmarshal([]byte(symbol), &symbolStr)
assert.Nil(t, err)
assert.Equal(t, tt.symbol, symbolStr)
assert.Nil(t, err)
engine.Dispose()

// call mint.
engine = NewV8Engine(ctx)
engine.SetExecutionLimits(10000, 100000000)
mintArgs := fmt.Sprintf("[\"%s\", \"%s\"]", tt.from, tt.tokenID)
result, err := engine.Call(string(data), tt.sourceType, "mint", mintArgs)
assert.Nil(t, err)
assert.Equal(t, "\"\"", result)
engine.Dispose()

// call approve.
engine = NewV8Engine(ctx)
engine.SetExecutionLimits(10000, 100000000)
approveArgs := fmt.Sprintf("[\"%s\", \"%s\"]", tt.to, tt.tokenID)
result, err = engine.Call(string(data), tt.sourceType, "approve", approveArgs)
assert.Nil(t, err)
assert.Equal(t, "\"\"", result)
engine.Dispose()

// parepare env, block & transactions.
tx = mockNormalTransaction(tt.to, "n1TV3sU6jyzR4rJ1D7jCAmtVGSntJagXZHC", "0")
ctx, err = NewContext(mockBlock(), tx, contract, context)

// call transferFrom.
engine = NewV8Engine(ctx)
engine.SetExecutionLimits(10000, 100000000)
transferFromArgs := fmt.Sprintf("[\"%s\", \"%s\", \"%s\"]", tt.from, tt.to, tt.tokenID)
result, err = engine.Call(string(data), tt.sourceType, "transferFrom", transferFromArgs)
assert.Nil(t, err)
assert.Equal(t, "\"\"", result)
engine.Dispose()

})
}
}

func TestNebulasContract(t *testing.T) {
tests := []struct {
name string
Expand Down
224 changes: 224 additions & 0 deletions nf/nvm/test/NRC721BasicToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Copyright (C) 2017 go-nebulas authors
//
// This file is part of the go-nebulas library.
//
// the go-nebulas library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// the go-nebulas library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>.
//

'use strict';

var Operator = function (obj) {
this.operator = {};
this.parse(obj);
};

Operator.prototype = {
toString: function () {
return JSON.stringify(this.operator);
},

parse: function (obj) {
if (typeof obj != "undefined") {
var data = JSON.parse(obj);
for (var key in data) {
this.operator[key] = data[key];
}
}
},

get: function (key) {
return this.operator[key];
},

set: function (key, value) {
this.operator[key] = value;
}
};

var StandardToken = function () {
LocalContractStorage.defineProperties(this, {
_name: null,
_symbol: null,
});

LocalContractStorage.defineMapProperties(this, {
"tokenOwner": null,
"ownedTokensCount": {
parse: function (value) {
return new BigNumber(value);
},
stringify: function (o) {
return o.toString(10);
}
},
"tokenApprovals": null,
"operatorApprovals": {
parse: function (value) {
return new Operator(value);
},
stringify: function (o) {
return o.toString();
}
},

});
};

StandardToken.prototype = {
init: function (name, symbol) {
this._name = name;
this._symbol = symbol;
},

name: function () {
return this._name;
},

symbol: function () {
return this._symbol;
},

balanceOf: function (_owner) {
var balance = this.ownedTokensCount.get(_owner);
if (balance instanceof BigNumber) {
return balance.toString(10);
} else {
return "0";
}
},

ownerOf: function (_tokenId) {
return this.tokenOwner.get(_tokenId);
},

approve: function (_to, _tokenId) {
var from = Blockchain.transaction.from;

var owner = this.ownerOf(_tokenId);
if (_to == owner) {
throw new Error("invalid address in approve.");
}
// msg.sender == owner || isApprovedForAll(owner, msg.sender)
if (owner == from || this.isApprovedForAll(owner, from)) {
this.tokenApprovals.set(_tokenId, _to);
this.approveEvent(true, owner, _to, _tokenId);
} else {
throw new Error("permission denied in approve.");
}
},

getApproved: function (_tokenId) {
return this.tokenApprovals.get(_tokenId);
},

setApprovalForAll: function(_to, _approved) {
var from = Blockchain.transaction.from;
if (from == _to) {
throw new Error("invalid address in setApprovalForAll.");
}
var operator = this.operatorApprovals.get(from) || new Operator();
operator.set(_to, _approved);
this.operatorApprovals.set(from, operator);
},

isApprovedForAll: function(_owner, _operator) {
var operator = this.operatorApprovals.get(_owner);
if (operator != null) {
if (operator.get(_operator) === "true") {
return true;
} else {
return false;
}
}
},

isApprovedOrOwner: function(_spender, _tokenId) {
var owner = this.ownerOf(_tokenId);
return _spender == owner || this.getApproved(_tokenId) == _spender || this.isApprovedForAll(owner, _spender);
},

transferFrom: function (_from, _to, _tokenId) {
var from = Blockchain.transaction.from;
if (this.isApprovedOrOwner(from, _tokenId)) {
this.clearApproval(_from, _tokenId);
this.removeTokenFrom(_from, _tokenId);
this.addTokenTo(_to, _tokenId);
this.transferEvent(true, _from, _to, _tokenId);
} else {
throw new Error("permission denied in transferFrom.");
}

},

clearApproval: function (_owner, _tokenId) {
var owner = this.ownerOf(_tokenId);
if (_owner != owner) {
throw new Error("permission denied in clearApproval.");
}
this.tokenApprovals.del(_tokenId);
},

removeTokenFrom: function(_from, _tokenId) {
if (_from != this.ownerOf(_tokenId)) {
throw new Error("permission denied in removeTokenFrom.");
}
var tokenCount = this.ownedTokensCount.get(_from);
if (tokenCount.lt(1)) {
throw new Error("Insufficient account balance in removeTokenFrom.");
}
this.ownedTokensCount.set(_from, tokenCount-1);
},

addTokenTo: function(_to, _tokenId) {
this.tokenOwner.set(_tokenId, _to);
var tokenCount = this.ownedTokensCount.get(_to) || new BigNumber(0);
this.ownedTokensCount.set(_to, tokenCount+1);
},

mint: function(_to, _tokenId) {
this.addTokenTo(_to, _tokenId);
this.transferEvent(true, "", _to, _tokenId);
},

burn: function(_owner, _tokenId) {
this.clearApproval(_owner, _tokenId);
this.removeTokenFrom(_owner, _tokenId);
this.transferEvent(true, _owner, "", _tokenId);
},

transferEvent: function (status, _from, _to, _tokenId) {
Event.Trigger(this.name(), {
Status: status,
Transfer: {
from: _from,
to: _to,
tokenId: _tokenId
}
});
},

approveEvent: function (status, _owner, _spender, _tokenId) {
Event.Trigger(this.name(), {
Status: status,
Approve: {
owner: _owner,
spender: _spender,
tokenId: _tokenId
}
});
}

};

module.exports = StandardToken;

0 comments on commit 626eff5

Please sign in to comment.