-
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.
* fix nft approve * approval to 1 address & approvalForAll * change variable name and assertions messages * fix tokenId to string * change names * u256 & events * fix tests * fix wrapper * test approve for all * fix transfert * fix get approve for all * add balanceOf * fix tests * Add set token uri * Fix wrapper * Format * Fix TU * Fix approval for all --------- Co-authored-by: AurelienFT <aurelien.foucault@epitech.eu>
- Loading branch information
1 parent
921fe3c
commit 8010503
Showing
5 changed files
with
626 additions
and
325 deletions.
There are no files selected for viewing
226 changes: 226 additions & 0 deletions
226
smart-contracts/assembly/contracts/NFT/NFT-internals.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,226 @@ | ||
import { stringToBytes, bytesToU256, u256ToBytes } from '@massalabs/as-types'; | ||
import { Storage, Context } from '@massalabs/massa-as-sdk'; | ||
import { u256 } from 'as-bignum/assembly'; | ||
import { | ||
approvedForAllTokenKey, | ||
approvedTokenKey, | ||
counterKey, | ||
nft1_ownerOf, | ||
ownerKey, | ||
ownerTokenKey, | ||
} from './NFT'; | ||
|
||
/** | ||
* Increment the NFT counter | ||
*/ | ||
export function _increment(): void { | ||
const currentID = bytesToU256(Storage.get(counterKey)); | ||
const newID = u256.add(currentID, u256.fromU32(1)); | ||
Storage.set(counterKey, u256ToBytes(newID)); | ||
} | ||
|
||
export function _updateBalanceOf(address: string, increment: boolean): void { | ||
const balanceKey = stringToBytes('balanceOf_' + address); | ||
const number = increment ? 1 : -1; | ||
|
||
if (Storage.has(balanceKey)) { | ||
const balance = bytesToU256(Storage.get(balanceKey)); | ||
const newBalance = u256.add(balance, u256.fromI32(number)); | ||
Storage.set(balanceKey, u256ToBytes(newBalance)); | ||
} else { | ||
assert(number == 1, 'Balance cannot be negative'); | ||
Storage.set(balanceKey, u256ToBytes(new u256(1))); | ||
} | ||
} | ||
|
||
export function _getBalanceOf(address: string): u256 { | ||
const balanceKey = stringToBytes('balanceOf_' + address); | ||
|
||
if (!Storage.has(balanceKey)) return new u256(0); | ||
|
||
return bytesToU256(Storage.get(balanceKey)); | ||
} | ||
|
||
/** | ||
* @returns true if the caller is the creator of the SC | ||
*/ | ||
export function _onlyOwner(): bool { | ||
return Context.caller().toString() == Storage.get(ownerKey); | ||
} | ||
|
||
/** | ||
* @param tokenId - the tokenID | ||
* @returns true if the caller is token's owner | ||
*/ | ||
export function _isTokenOwner(address: string, tokenId: u256): bool { | ||
// as we need to compare two byteArrays, we need to compare the pointers | ||
// we transform our byte array to their pointers and we compare them | ||
const left = nft1_ownerOf(u256ToBytes(tokenId)); | ||
return ( | ||
memory.compare( | ||
changetype<usize>(left), | ||
changetype<usize>(stringToBytes(address)), | ||
left.length, | ||
) == 0 | ||
); | ||
} | ||
|
||
/** | ||
* @param tokenId - the tokenID | ||
* @returns true if the token is minted | ||
*/ | ||
export function _onlyMinted(tokenId: u256): bool { | ||
return Storage.has(ownerTokenKey + tokenId.toString()); | ||
} | ||
|
||
/** | ||
* Internal function returning the currentSupply | ||
* @returns u256 | ||
*/ | ||
export function _currentSupply(): u256 { | ||
return bytesToU256(Storage.get(counterKey)); | ||
} | ||
|
||
// ==================================================== // | ||
// ==== TRANSFER ==== // | ||
// ==================================================== // | ||
|
||
export function _transfer( | ||
caller: string, | ||
owner: string, | ||
recipient: string, | ||
tokenId: u256, | ||
): void { | ||
assertIsMinted(tokenId); | ||
assertIsOwner(owner, tokenId); | ||
assertNotSelfTransfer(owner, recipient); | ||
assertIsApproved(owner, caller, tokenId); | ||
|
||
_removeApproval(tokenId); | ||
_updateBalanceOf(owner, false); | ||
_updateBalanceOf(recipient, true); | ||
|
||
Storage.set(ownerTokenKey + tokenId.toString(), recipient); | ||
} | ||
|
||
// ==================================================== // | ||
// ==== APPROVAL ==== // | ||
// ==================================================== // | ||
|
||
/** | ||
* Store the approved address for a token | ||
* | ||
* @param owner - owner address | ||
* @param spenderAddress - spender address | ||
* @param tokenId - The token ID to approve | ||
*/ | ||
export function _approve( | ||
tokenId: u256, | ||
owner: string, | ||
spenderAddress: string, | ||
): void { | ||
assertIsMinted(tokenId); | ||
assertIsOwner(owner, tokenId); | ||
assert(!_isApproved(spenderAddress, tokenId), 'Already approved'); | ||
|
||
const key = approvedTokenKey + tokenId.toString(); | ||
Storage.set(key, spenderAddress); | ||
} | ||
|
||
/** | ||
* Removes the approval of the token | ||
* @param tokenId - the tokenID | ||
*/ | ||
function _removeApproval(id: u256): void { | ||
const key = approvedTokenKey + id.toString(); | ||
Storage.set(key, ''); | ||
} | ||
|
||
export function _getApproved(tokenId: u256): string { | ||
const key = approvedTokenKey + tokenId.toString(); | ||
|
||
if (!Storage.has(key)) return ''; | ||
|
||
return Storage.get(key); | ||
} | ||
|
||
export function _isApproved(address: string, tokenId: u256): bool { | ||
if (address.length === 0) return false; | ||
const approvedAddress = _getApproved(tokenId); | ||
return approvedAddress === address; | ||
} | ||
|
||
export function _setApprovalForAll( | ||
owner: string, | ||
operator: string, | ||
approved: bool, | ||
): void { | ||
assert(owner != operator, 'Not allowed to set approval for all for yourself'); | ||
|
||
const key = approvedForAllTokenKey + owner + operator; | ||
|
||
Storage.set(key, approved.toString()); | ||
} | ||
|
||
export function _isApprovedForAll(owner: string, operator: string): bool { | ||
const key = approvedForAllTokenKey + owner + operator; | ||
|
||
if (!Storage.has(key)) return false; | ||
|
||
const approved = Storage.get(key); | ||
|
||
return approved == 'true'; | ||
} | ||
|
||
// ==================================================== // | ||
// ==== General Assertions ==== // | ||
// ==================================================== // | ||
|
||
export function assertIsMinted(tokenId: u256): void { | ||
assert(_onlyMinted(tokenId), `Token ${tokenId.toString()} is not minted`); | ||
} | ||
|
||
export function assertIsOwner(address: string, tokenId: u256): void { | ||
assert( | ||
_isTokenOwner(address, tokenId), | ||
`${address} is not the owner of ${tokenId.toString()}`, | ||
); | ||
} | ||
|
||
function assertIsApproved(owner: string, caller: string, tokenId: u256): void { | ||
assert( | ||
_isApproved(caller, tokenId) || | ||
_isApprovedForAll(owner, caller) || | ||
owner === caller, | ||
`${caller} is not allowed to transfer this token`, | ||
); | ||
} | ||
|
||
export function assertNotSelfTransfer(owner: string, recipient: string): void { | ||
assert( | ||
owner != recipient, | ||
'The owner and the recipient must be different addresses', | ||
); | ||
} | ||
|
||
// ==================================================== // | ||
// ==== EVENTS ==== // | ||
// ==================================================== // | ||
|
||
/** | ||
* Constructs a pretty formatted event with given key and arguments. | ||
* | ||
* @remarks | ||
* The result is meant to be used with the {@link generateEvent} function. | ||
* It is useful to generate events from an array. | ||
* | ||
* @param key - the string event key. | ||
* | ||
* @param args - the string array arguments. | ||
* | ||
* @returns the stringified event. | ||
* | ||
*/ | ||
export function createEvent(key: string, args: Array<string>): string { | ||
return `${key}:`.concat(args.join(',')); | ||
} |
Oops, something went wrong.