-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ERC827-like methods for ERC721Holdings #2
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "./ERC721HoldingsBasic.sol"; | ||
|
||
|
||
contract ERC721HoldingsExecuteCalls is ERC721HoldingsBasic { | ||
function approveAndCall(address _spender, uint256 _tokenId, bytes _data) | ||
public payable returns (bool); | ||
|
||
function transferFromAndCall(address _from, uint256 _to, address _toOrigin, uint256 _tokenId, bytes _data) | ||
public payable returns (bool); | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
pragma solidity ^0.4.23; | ||
|
||
import "./ERC721HoldingsBasicToken.sol"; | ||
|
||
|
||
contract ERC721HoldingsExecuteCallsToken is ERC721HoldingsBasicToken { | ||
|
||
function approveAndCall(address _spender, uint256 _tokenId, bytes _data) | ||
public payable returns (bool) | ||
{ | ||
super.approve(_spender, _tokenId); | ||
|
||
// solium-disable-next-line security/no-call-value | ||
require(_spender.call.value(msg.value)(_data)); | ||
|
||
return true; | ||
} | ||
|
||
function transferFromAndCall( | ||
address _from, | ||
uint256 _to, | ||
address _toOrigin, | ||
uint256 _tokenId, | ||
bytes _data) | ||
public payable returns (bool) | ||
{ | ||
address _holderOwner = _ownerOf(_to, _toOrigin); | ||
require(_holderOwner != address(this)); | ||
|
||
super.transferFrom(_from, _to, _toOrigin, _tokenId); | ||
|
||
// solium-disable-next-line security/no-call-value | ||
require(_holderOwner.call.value(msg.value)(_data)); | ||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
pragma solidity ^0.4.21; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's be consistent and use 'pragma solidity ^0.4.23;' |
||
|
||
|
||
contract MessageHelper { | ||
|
||
event Show(bytes32 b32, uint256 number, string text); | ||
event Buy(bytes32 b32, uint256 number, string text, uint256 value); | ||
|
||
function showMessage( bytes32 message, uint256 number, string text ) public returns (bool) { | ||
emit Show(message, number, text); | ||
return true; | ||
} | ||
|
||
function buyMessage( bytes32 message, uint256 number, string text ) public payable returns (bool) { | ||
emit Buy(message, number, text, msg.value); | ||
return true; | ||
} | ||
|
||
function fail() public pure { | ||
require(false); | ||
} | ||
|
||
function call(address to, bytes data) public returns (bool) { | ||
// solium-disable-next-line security/no-low-level-calls | ||
if (to.call(data)) | ||
return true; | ||
else | ||
return false; | ||
} | ||
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add newline to EOF |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
const Message = artifacts.require('mocks/MessageHelper.sol'); | ||
|
||
import assertRevert from './helpers/assertRevert'; | ||
const BigNumber = web3.BigNumber; | ||
|
||
require('chai') | ||
.use(require('chai-as-promised')) | ||
.use(require('chai-bignumber')(BigNumber)) | ||
.should(); | ||
|
||
export default function shouldExecuteCallsERC721HoldingsToken (accounts) { | ||
const firstTokenId = 1; | ||
const secondTokenId = 2; | ||
const unknownTokenId = 3; | ||
|
||
const firstAvatarId = 1; | ||
const secondAvatarId = 2; | ||
const thirdAvatarId = 3; | ||
|
||
const creator = accounts[0]; | ||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; | ||
|
||
describe('like an execute calls ERC721HoldingsToken', function () { | ||
beforeEach(async function () { | ||
this.message = await Message.new({ from: creator }); | ||
|
||
await this.avatars.mint(creator, firstAvatarId, { from: creator }); | ||
await this.avatars.mint(this.message.contract.address, secondAvatarId, { from: creator }); | ||
await this.avatars.mint(this.token.contract.address, thirdAvatarId, { from: creator }); | ||
|
||
await this.token.mint(firstAvatarId, this.avatars.address, firstTokenId, { from: creator }); | ||
await this.token.mint(firstAvatarId, this.avatars.address, secondTokenId, { from: creator }); | ||
}); | ||
|
||
describe('Test Execute Calls methods', function () { | ||
it('should allow payment through approve', async function () { | ||
const extraData = this.message.contract.buyMessage.getData( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've used It would look something like this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are using the web3 v0.20 API and this will change with web3 v1.0 release. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just a FYI comment, no need to change anything for now. |
||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
const transaction = await this.token.approveAndCall( | ||
this.message.contract.address, firstTokenId, extraData, { from: creator, value: 1 } | ||
); | ||
|
||
assert.equal(2, transaction.receipt.logs.length); | ||
|
||
const appproved = await this.token.getApproved(firstTokenId); | ||
this.message.contract.address.should.be.equal(appproved); | ||
|
||
const balance = await web3.eth.getBalance(this.message.contract.address); | ||
new BigNumber(1).should.be.bignumber.equal(balance); | ||
}); | ||
|
||
it('should allow payment through transferFrom', async function () { | ||
const extraData = this.message.contract.buyMessage.getData( | ||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
await this.token.approve(accounts[1], firstTokenId, { from: creator }); | ||
|
||
const appproved = await this.token.getApproved(firstTokenId); | ||
accounts[1].should.be.equal(appproved); | ||
|
||
const transaction = await this.token.transferFromAndCall( | ||
accounts[0], secondAvatarId, this.avatars.contract.address, firstTokenId, extraData, { from: accounts[1], value: 1 } | ||
); | ||
|
||
assert.equal(3, transaction.receipt.logs.length); | ||
|
||
const tokenBalance = await this.token.balanceOf(secondAvatarId, this.avatars.address); | ||
new BigNumber(1).should.be.bignumber.equal(tokenBalance); | ||
|
||
const ethBalance = await web3.eth.getBalance(this.message.contract.address); | ||
new BigNumber(1).should.be.bignumber.equal(ethBalance); | ||
}); | ||
|
||
it('should revert funds of failure inside approve (with data)', async function () { | ||
// showMessage is not payable, so it fails when called with msg.value | ||
const extraData = this.message.contract.showMessage.getData( | ||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
await this.token.approveAndCall( | ||
this.message.contract.address, firstTokenId, extraData, { from: creator, value: 1 } | ||
).should.be.rejectedWith('revert'); | ||
|
||
// approval should not have gone through so approved address is still 0x0 | ||
const appproved = await this.token.getApproved(firstTokenId); | ||
ZERO_ADDRESS.should.be.equal(appproved); | ||
|
||
const ethBalance = await web3.eth.getBalance(this.message.contract.address); | ||
new BigNumber(0).should.be.bignumber.equal(ethBalance); | ||
}); | ||
|
||
it('should revert funds of failure inside transferFrom (with data)', async function () { | ||
// showMessage is not payable, so it fails when called with msg.value | ||
const extraData = this.message.contract.showMessage.getData( | ||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
await this.token.approve(accounts[1], firstTokenId, { from: creator }); | ||
|
||
await this.token.transferFromAndCall( | ||
accounts[0], secondAvatarId, this.avatars.contract.address, firstTokenId, extraData, { from: accounts[1], value: 1 } | ||
).should.be.rejectedWith('revert');; | ||
|
||
// transferFrom should not have gone through so approved address is still accounts[1] | ||
const appproved = await this.token.getApproved(firstTokenId); | ||
accounts[1].should.be.equal(appproved); | ||
|
||
const tokenBalance = await this.token.balanceOf(secondAvatarId, this.avatars.address); | ||
new BigNumber(0).should.be.bignumber.equal(tokenBalance); | ||
|
||
const ethBalance = await web3.eth.getBalance(this.message.contract.address); | ||
new BigNumber(0).should.be.bignumber.equal(ethBalance); | ||
}); | ||
|
||
it('should return correct allowance after approve (with data) and show the event on receiver contract', async function () { | ||
const extraData = this.message.contract.showMessage.getData( | ||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
const transaction = await this.token.approveAndCall( | ||
this.message.contract.address, firstTokenId, extraData, { from: creator } | ||
); | ||
|
||
assert.equal(2, transaction.receipt.logs.length); | ||
|
||
const appproved = await this.token.getApproved(firstTokenId); | ||
this.message.contract.address.should.be.equal(appproved); | ||
}); | ||
|
||
it('should return correct balances after transferFrom (with data) and show the event on receiver contract', async function () { | ||
const extraData = this.message.contract.showMessage.getData( | ||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
await this.token.approve(accounts[1], firstTokenId, { from: accounts[0] }); | ||
|
||
const appproved = await this.token.getApproved(firstTokenId); | ||
accounts[1].should.be.equal(appproved); | ||
|
||
const transaction = await this.token.transferFromAndCall( | ||
accounts[0], secondAvatarId, this.avatars.contract.address, firstTokenId, extraData, { from: accounts[1] } | ||
); | ||
|
||
assert.equal(3, transaction.receipt.logs.length); | ||
|
||
const tokenBalance = await this.token.balanceOf(secondAvatarId, this.avatars.address); | ||
new BigNumber(1).should.be.bignumber.equal(tokenBalance); | ||
}); | ||
|
||
it('should fail inside approve (with data)', async function () { | ||
const extraData = this.message.contract.fail.getData(); | ||
|
||
await this.token.approveAndCall(this.message.contract.address, firstTokenId, extraData) | ||
.should.be.rejectedWith('revert'); | ||
|
||
// approval should not have gone through so approved is still ZERO_ADDRESS | ||
const appproved = await this.token.getApproved(firstTokenId); | ||
ZERO_ADDRESS.should.be.equal(appproved); | ||
}); | ||
|
||
it('should fail inside transferFrom (with data)', async function () { | ||
const extraData = this.message.contract.fail.getData(); | ||
|
||
await this.token.approve(accounts[1], firstAvatarId, { from: creator }); | ||
await this.token.transferFromAndCall(creator, secondAvatarId, this.avatars.address, firstTokenId, extraData, { from: creator }) | ||
.should.be.rejectedWith('revert'); | ||
|
||
// transferFrom should have failed so balance is still 0 but approved is accounts[1] | ||
const appproved = await this.token.getApproved(firstTokenId); | ||
accounts[1].should.be.equal(appproved); | ||
|
||
const tokenBalance = await this.token.balanceOf(secondAvatarId, this.avatars.address); | ||
new BigNumber(0).should.be.bignumber.equal(tokenBalance); | ||
}); | ||
|
||
it('should fail approve (with data) when using token contract address as receiver', async function () { | ||
const extraData = this.message.contract.showMessage.getData( | ||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
await this.token.approveAndCall(this.token.contract.address, firstAvatarId, extraData, { from: accounts[0] }) | ||
.should.be.rejectedWith('revert'); | ||
}); | ||
|
||
it('should fail transferFrom (with data) when using token contract address as receiver', async function () { | ||
const extraData = this.message.contract.showMessage.getData( | ||
web3.toHex(123456), 666, 'Transfer Done' | ||
); | ||
|
||
await this.token.approve(accounts[1], firstTokenId, { from: accounts[0] }); | ||
|
||
await this.token.transferFromAndCall(accounts[0], thirdAvatarId, this.avatars.address, firstTokenId, extraData, { from: accounts[1] }) | ||
.should.be.rejectedWith('revert'); | ||
}); | ||
}); | ||
}); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add newline to EOF |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add newline at EOF