-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
1,991 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { Args, stringToBytes } from '@massalabs/as-types'; | ||
import { u256 } from 'as-bignum/assembly'; | ||
import * as token from './token'; | ||
import * as mint from './mintable/mint'; | ||
import { Context } from '@massalabs/massa-as-sdk'; | ||
import { grantRole } from '../utils/accessControl'; | ||
export * from './burnable/burn'; | ||
export * from './mintable/mint'; | ||
export * from '../utils/accessControl'; | ||
export * from '../utils/ownership'; | ||
|
||
export function constructor(binaryArgs: StaticArray<u8>): void { | ||
const args = new Args(binaryArgs); | ||
const uri = args.nextString().expect('uri argument is missing or invalid'); | ||
const ids = args | ||
.nextFixedSizeArray<u256>() | ||
.expect('ids argument is missing or invalid'); | ||
const amounts = args | ||
.nextFixedSizeArray<u256>() | ||
.expect('amounts argument is missing or invalid'); | ||
token.constructor(new Args().add(uri).serialize()); | ||
grantRole( | ||
new Args() | ||
.add(mint.MINTER_ROLE) | ||
.add(Context.caller().toString()) | ||
.serialize(), | ||
); | ||
mint.mintBatch( | ||
new Args() | ||
.add(Context.caller().toString()) | ||
.add(ids) | ||
.add(amounts) | ||
.add(stringToBytes('')) | ||
.serialize(), | ||
); | ||
} | ||
|
||
// export everything from the token module except the constructor | ||
export { | ||
uri, | ||
balanceOf, | ||
balanceOfBatch, | ||
setApprovalForAll, | ||
isApprovedForAll, | ||
safeTransferFrom, | ||
safeBatchTransferFrom, | ||
} from './token'; |
56 changes: 56 additions & 0 deletions
56
smart-contracts/assembly/contracts/MRC1155/OnERC1155Received.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { Args } from '@massalabs/as-types'; | ||
import { createEvent, generateEvent } from '@massalabs/massa-as-sdk'; | ||
|
||
import { u256 } from 'as-bignum/assembly'; | ||
|
||
export function onERC1155BatchReceived( | ||
binaryArgs: StaticArray<u8>, | ||
): StaticArray<u8> { | ||
const args = new Args(binaryArgs); | ||
const operator = args | ||
.nextString() | ||
.expect('operator argument is missing or invalid'); | ||
const from = args.nextString().expect('from argument is missing or invalid'); | ||
const ids = args | ||
.nextFixedSizeArray<u256>() | ||
.expect('ids argument is missing or invalid'); | ||
const values = args | ||
.nextFixedSizeArray<u256>() | ||
.expect('values argument is missing or invalid'); | ||
const data = args.nextBytes().expect('data argument is missing or invalid'); | ||
|
||
generateEvent( | ||
createEvent('ERC1155BatchReceived', [ | ||
operator, | ||
from, | ||
ids.map<string>((id: u256) => id.toString()).join(';'), | ||
values.map<string>((id: u256) => id.toString()).join(';'), | ||
data.toString(), | ||
]), | ||
); | ||
return new Args().add('bc197c81').serialize(); | ||
} | ||
|
||
export function onERC1155Received( | ||
binaryArgs: StaticArray<u8>, | ||
): StaticArray<u8> { | ||
const args = new Args(binaryArgs); | ||
const operator = args | ||
.nextString() | ||
.expect('operator argument is missing or invalid'); | ||
const from = args.nextString().expect('from argument is missing or invalid'); | ||
const id = args.nextU256().expect('id argument is missing or invalid'); | ||
const value = args.nextU256().expect('value argument is missing or invalid'); | ||
const data = args.nextBytes().expect('data argument is missing or invalid'); | ||
|
||
generateEvent( | ||
createEvent('ERC1155Received', [ | ||
operator, | ||
from, | ||
id.toString(), | ||
value.toString(), | ||
data.toString(), | ||
]), | ||
); | ||
return new Args().add('f23a6e61').serialize(); | ||
} |
56 changes: 56 additions & 0 deletions
56
smart-contracts/assembly/contracts/MRC1155/OnERC1155ReceivedFail.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { Args } from '@massalabs/as-types'; | ||
import { createEvent, generateEvent } from '@massalabs/massa-as-sdk'; | ||
|
||
import { u256 } from 'as-bignum/assembly'; | ||
|
||
export function onERC1155BatchReceived( | ||
binaryArgs: StaticArray<u8>, | ||
): StaticArray<u8> { | ||
const args = new Args(binaryArgs); | ||
const operator = args | ||
.nextString() | ||
.expect('operator argument is missing or invalid'); | ||
const from = args.nextString().expect('from argument is missing or invalid'); | ||
const ids = args | ||
.nextFixedSizeArray<u256>() | ||
.expect('ids argument is missing or invalid'); | ||
const values = args | ||
.nextFixedSizeArray<u256>() | ||
.expect('values argument is missing or invalid'); | ||
const data = args.nextBytes().expect('data argument is missing or invalid'); | ||
|
||
generateEvent( | ||
createEvent('ERC1155BatchReceived', [ | ||
operator, | ||
from, | ||
ids.map<string>((id: u256) => id.toString()).join(';'), | ||
values.map<string>((id: u256) => id.toString()).join(';'), | ||
data.toString(), | ||
]), | ||
); | ||
return new Args().add('wrong').serialize(); | ||
} | ||
|
||
export function onERC1155Received( | ||
binaryArgs: StaticArray<u8>, | ||
): StaticArray<u8> { | ||
const args = new Args(binaryArgs); | ||
const operator = args | ||
.nextString() | ||
.expect('operator argument is missing or invalid'); | ||
const from = args.nextString().expect('from argument is missing or invalid'); | ||
const id = args.nextU256().expect('id argument is missing or invalid'); | ||
const value = args.nextU256().expect('value argument is missing or invalid'); | ||
const data = args.nextBytes().expect('data argument is missing or invalid'); | ||
|
||
generateEvent( | ||
createEvent('ERC1155Received', [ | ||
operator, | ||
from, | ||
id.toString(), | ||
value.toString(), | ||
data.toString(), | ||
]), | ||
); | ||
return new Args().add('wrong').serialize(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# MRC1155 | ||
|
||
This repository contains a set of smart contracts to implement the ERC1155 standard on the Massa blockchain. | ||
see [ERC1155 documentation](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC1155) | ||
|
||
On top of that it also includes multiples extensions to the standard to allow for more complex use cases: | ||
- [burnable](./assembly/contracts/burnable.sol) | ||
- [mintable](./assembly/contracts/mintable.sol) | ||
- [metadata](./assembly/contracts/metadata.sol) | ||
|
||
It can be easily merged into massa-standards as this repository contains a set of smart contracts that are fully compatible with the ERC1155 standard with the only common depencies being ownership contract. | ||
|
||
## Documentation | ||
|
||
A documentation for each functions internals or externals has been created that can be found just before the functions declarations. | ||
|
||
## Unit tests | ||
|
||
A big set of unit tests has been written to ensure the correctness of the smart contracts compared to the standard and some security related checks. | ||
|
||
The only missing coverage is for the call of the ERC1155Receiver function which cannot be tested with the current mocked tests environment. | ||
|
1 change: 1 addition & 0 deletions
1
smart-contracts/assembly/contracts/MRC1155/__tests__/as-pect.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/// <reference types="@as-pect/assembly/types/as-pect" /> |
205 changes: 205 additions & 0 deletions
205
smart-contracts/assembly/contracts/MRC1155/__tests__/burnable.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
import { | ||
changeCallStack, | ||
resetStorage, | ||
setDeployContext, | ||
} from '@massalabs/massa-as-sdk'; | ||
import { | ||
Args, | ||
stringToBytes, | ||
u256ToBytes, | ||
fixedSizeArrayToBytes, | ||
boolToByte, | ||
} from '@massalabs/as-types'; | ||
import { | ||
balanceOf, | ||
balanceOfBatch, | ||
constructor, | ||
setApprovalForAll, | ||
} from '../token'; | ||
import { u256 } from 'as-bignum/assembly'; | ||
import { _mint, _mintBatch } from '../token-internal'; | ||
import { burn, burnBatch } from '..'; | ||
|
||
// address of the contract set in vm-mock. must match with contractAddr of @massalabs/massa-as-sdk/vm-mock/vm.js | ||
const contractAddr = 'AS12BqZEQ6sByhRLyEuf0YbQmcF2PsDdkNNG1akBJu9XcjZA1eT'; | ||
|
||
const user1Address = 'AU12UBnqTHDQALpocVBnkPNy7y5CndUJQTLutaVDDFgMJcq5kQiKq'; | ||
|
||
const user2Address = 'AU12BqZEQ6sByhRLyEuf0YbQmcF2PsDdkNNG1akBJu9XcjZA1e8'; | ||
|
||
const TOKEN_URI = 'ipfs://QmW77ZQQ7Jm9q8WuLbH8YZg2K7T9Qnjbzm7jYVQQrJY5Yd'; | ||
|
||
function switchUser(user: string): void { | ||
changeCallStack(user + ' , ' + contractAddr); | ||
} | ||
|
||
beforeEach(() => { | ||
switchUser(user1Address); | ||
resetStorage(); | ||
setDeployContext(user1Address); | ||
constructor(new Args().add(stringToBytes(TOKEN_URI)).serialize()); | ||
}); | ||
|
||
describe('burn', () => { | ||
test('should burn tokens', () => { | ||
const id = u256.One; | ||
const value = u256.from(10); | ||
const data = stringToBytes('burn data'); | ||
_mint(user1Address, id, value, data); | ||
|
||
expect( | ||
balanceOf( | ||
new Args().add(stringToBytes(user1Address)).add(id).serialize(), | ||
), | ||
).toStrictEqual(u256ToBytes(value)); | ||
|
||
burn( | ||
new Args() | ||
.add(stringToBytes(user1Address)) | ||
.add(id) | ||
.add(value) | ||
.serialize(), | ||
); | ||
expect( | ||
balanceOf( | ||
new Args().add(stringToBytes(user1Address)).add(id).serialize(), | ||
), | ||
).toStrictEqual(u256ToBytes(u256.Zero)); | ||
}); | ||
|
||
test('should burn tokens with approval', () => { | ||
const id = u256.One; | ||
const value = u256.from(10); | ||
const data = stringToBytes('burn data'); | ||
_mint(user1Address, id, value, data); | ||
expect( | ||
balanceOf( | ||
new Args().add(stringToBytes(user1Address)).add(id).serialize(), | ||
), | ||
).toStrictEqual(u256ToBytes(value)); | ||
|
||
setApprovalForAll( | ||
new Args() | ||
.add(stringToBytes(user2Address)) | ||
.add(boolToByte(true)) | ||
.serialize(), | ||
); | ||
|
||
switchUser(user2Address); | ||
burn( | ||
new Args() | ||
.add(stringToBytes(user1Address)) | ||
.add(id) | ||
.add(value) | ||
.serialize(), | ||
); | ||
expect( | ||
balanceOf( | ||
new Args().add(stringToBytes(user1Address)).add(id).serialize(), | ||
), | ||
).toStrictEqual(u256ToBytes(u256.Zero)); | ||
}); | ||
|
||
throws('ERC1155MissingApprovalForAll', () => { | ||
const id = u256.One; | ||
const value = u256.from(10); | ||
const data = stringToBytes('burn data'); | ||
_mint(user1Address, id, value, data); | ||
expect( | ||
balanceOf( | ||
new Args().add(stringToBytes(user1Address)).add(id).serialize(), | ||
), | ||
).toStrictEqual(u256ToBytes(value)); | ||
|
||
switchUser(user2Address); | ||
burn( | ||
new Args() | ||
.add(stringToBytes(user1Address)) | ||
.add(id) | ||
.add(value) | ||
.serialize(), | ||
); | ||
}); | ||
}); | ||
|
||
describe('burnBatch', () => { | ||
test('should burn batch of tokens', () => { | ||
const ids = [u256.One, u256.from(2), u256.from(3)]; | ||
const values = [u256.from(10), u256.from(20), u256.from(30)]; | ||
const data = stringToBytes('burn data'); | ||
_mintBatch(user1Address, ids, values, data); | ||
expect( | ||
balanceOfBatch( | ||
new Args() | ||
.add([user1Address, user1Address, user1Address]) | ||
.add(ids) | ||
.serialize(), | ||
), | ||
).toStrictEqual(fixedSizeArrayToBytes<u256>(values)); | ||
|
||
burnBatch(new Args().add(user1Address).add(ids).add(values).serialize()); | ||
expect( | ||
balanceOfBatch( | ||
new Args() | ||
.add([user1Address, user1Address, user1Address]) | ||
.add(ids) | ||
.serialize(), | ||
), | ||
).toStrictEqual( | ||
fixedSizeArrayToBytes<u256>([u256.Zero, u256.Zero, u256.Zero]), | ||
); | ||
}); | ||
|
||
test('should burn batch of tokens with approval', () => { | ||
const ids = [u256.One, u256.from(2), u256.from(3)]; | ||
const values = [u256.from(10), u256.from(20), u256.from(30)]; | ||
const data = stringToBytes('burn data'); | ||
_mintBatch(user1Address, ids, values, data); | ||
expect( | ||
balanceOfBatch( | ||
new Args() | ||
.add([user1Address, user1Address, user1Address]) | ||
.add(ids) | ||
.serialize(), | ||
), | ||
).toStrictEqual(fixedSizeArrayToBytes<u256>(values)); | ||
|
||
setApprovalForAll( | ||
new Args() | ||
.add(stringToBytes(user2Address)) | ||
.add(boolToByte(true)) | ||
.serialize(), | ||
); | ||
|
||
switchUser(user2Address); | ||
burnBatch(new Args().add(user1Address).add(ids).add(values).serialize()); | ||
expect( | ||
balanceOfBatch( | ||
new Args() | ||
.add([user1Address, user1Address, user1Address]) | ||
.add(ids) | ||
.serialize(), | ||
), | ||
).toStrictEqual( | ||
fixedSizeArrayToBytes<u256>([u256.Zero, u256.Zero, u256.Zero]), | ||
); | ||
}); | ||
|
||
throws('ERC1155MissingApprovalForAll', () => { | ||
const ids = [u256.One, u256.from(2), u256.from(3)]; | ||
const values = [u256.from(10), u256.from(20), u256.from(30)]; | ||
const data = stringToBytes('burn data'); | ||
_mintBatch(user1Address, ids, values, data); | ||
expect( | ||
balanceOfBatch( | ||
new Args() | ||
.add([user1Address, user1Address, user1Address]) | ||
.add(ids) | ||
.serialize(), | ||
), | ||
).toStrictEqual(fixedSizeArrayToBytes<u256>(values)); | ||
|
||
switchUser(user2Address); | ||
burnBatch(new Args().add(user1Address).add(ids).add(values).serialize()); | ||
}); | ||
}); |
Oops, something went wrong.