公共转账(transfer、transferFrom、safeTransferFrom 等)在 isTransferAllowed 或 isUserAllowed 将为 from 和 to 地址之一或两者返回 false 的情况下,必须不成功 (MUST NOT succeed)。
铸造 必须不能成功转移到 isUserAllowed 会返回 false 的帐户 (MUST NOT succeed to accounts where isUserAllowed would return false)。
销毁 不应受到对代币持有者的 isTransferAllowed 或 isUserAllowed 检查的限制 (SHOULD NOT be restricted by isTransferAllowed or isUserAllowed checks on the token holder)。可以限制它以防止销毁比未冻结数量更多的资产(例如,在公共销毁函数中)。它可以销毁比未冻结数量更多的资产(例如,在授权的销毁函数中),在这种情况下,合约必须相应地更新冻结状态,并在底层的基本代币转账事件之前发出 Frozen 事件。
每当对 isUserAllowed 或 isTransferAllowed 的内部调用返回 false 时,ERC7943NotAllowedTransfer 和 ERC7943NotAllowedUser 错误都可以用作通用回滚机制 (CAN be used as a general revert mechanism)。这些错误可能不被使用,或者可能会被更具体的错误所取代,具体取决于在这些调用中执行的自定义检查。
pragmasolidity^0.8.29;/* required imports ... */contractuRWA20isContext,ERC20,AccessControlEnumerable,IERC7943{bytes32publicconstantMINTER_ROLE=keccak256("MINTER_ROLE");bytes32publicconstantBURNER_ROLE=keccak256("BURNER_ROLE");bytes32publicconstantENFORCER_ROLE=keccak256("ENFORCER_ROLE");bytes32publicconstantWHITELIST_ROLE=keccak256("WHITELIST_ROLE");mapping(addressuser=>boolwhitelisted)publicisWhitelisted;mapping(addressuser=>uint256amount)internal_frozenTokens;eventWhitelisted(addressindexedaccount,boolstatus);errorNotZeroAddress();constructor(stringmemoryname,stringmemorysymbol,addressinitialAdmin)ERC20(name,symbol){/* give initialAdmin necessary roles ...*/// 给予初始管理员必要的角色...
}functionisTransferAllowed(addressfrom,addressto,uint256,uint256amount)publicvirtualviewreturns(boolallowed){if(amount>balanceOf(from)-_frozenTokens[from])return;if(!isUserAllowed(from)||!isUserAllowed(to))return;allowed=true;}functionisUserAllowed(addressuser)publicvirtualviewreturns(boolallowed){if(isWhitelisted[user])allowed=true;}functiongetFrozen(addressuser,uint256)externalviewreturns(uint256amount){amount=_frozenTokens[user];}functionchangeWhitelist(addressaccount,boolstatus)externalonlyRole(WHITELIST_ROLE){require(account!=address(0),NotZeroAddress());isWhitelisted[account]=status;emitWhitelisted(account,status);}/* standard mint and burn functions with access control ...*/// 具有访问控制的标准铸造和销毁功能...
functionsetFrozen(addressuser,uint256,uint256amount)publiconlyRole(ENFORCER_ROLE){require(amount<=balanceOf(user),IERC20Errors.ERC20InsufficientBalance(user,balanceOf(user),amount));_frozenTokens[user]=amount;emitFrozen(user,0,amount);}functionforceTransfer(addressfrom,addressto,uint256,uint256amount)publiconlyRole(ENFORCER_ROLE){require(isUserAllowed(to),ERC7943NotAllowedUser(to));_excessFrozenUpdate(from,amount);super._update(from,to,amount);emitForcedTransfer(from,to,0,amount);}function_excessFrozenUpdate(addressuser,uint256amount)internal{uint256unfrozenBalance=balanceOf(user)-_frozenTokens[user];if(amount>unfrozenBalance&&amount<=balanceOf(user)){// Protect from underflow: if amount > balanceOf(user) the call will revert in super._update with insufficient balance error
// 防止下溢:如果 amount > balanceOf(user),则调用将在 super._update 中回滚,并出现余额不足错误
_frozenTokens[user]-=amount-unfrozenBalance;// Reduce by excess amount
// 减少过剩数量
emitFrozen(user,0,_frozenTokens[user]);}}function_update(addressfrom,addressto,uint256amount)internalvirtualoverride{if(from!=address(0)&&to!=address(0)){// Transfer
// 转账
require(amount<=balanceOf(from),IERC20Errors.ERC20InsufficientBalance(from,balanceOf(from),amount));require(amount<=balanceOf(from)-_frozenTokens[from],ERC7943InsufficientUnfrozenBalance(from,0,amount,balanceOf(from)-_frozenTokens[from]));require(isTransferAllowed(from,to,0,amount),ERC7943NotAllowedTransfer(from,to,0,amount));}elseif(from==address(0)){// Mint
// 铸造
require(isUserAllowed(to),ERC7943NotAllowedUser(to));}else{// Burn
// 销毁
_excessFrozenUpdate(from,amount);}super._update(from,to,amount);}functionsupportsInterface(bytes4interfaceId)publicviewvirtualoverride(AccessControlEnumerable,IERC165)returns(bool){returninterfaceId==type(IERC7943).interfaceId||interfaceId==type(IERC20).interfaceId||super.supportsInterface(interfaceId);}}
pragmasolidity^0.8.29;/* required imports ... */contractuRWA721isContext,ERC721,AccessControlEnumerable,IERC7943{/* same definitions, constructor and changeWhitelist function as before ...*/// 与之前相同的定义、构造函数和 changeWhitelist 函数...
mapping(addressuser=>mapping(uint256tokenId=>uint8frozen))internal_frozenTokens;functionisUserAllowed(addressuser)publicviewvirtualoverridereturns(boolallowed){if(isWhitelisted[user])allowed=true;}functionisTransferAllowed(addressfrom,addressto,uint256tokenId,uint256)publicviewvirtualoverridereturns(boolallowed){addressowner=_ownerOf(tokenId);if(owner!=from||owner==address(0))return;if(!isUserAllowed(from)||!isUserAllowed(to))return;if(_frozenTokens[from][tokenId]>0)return;allowed=true;}functiongetFrozen(addressuser,uint256tokenId)externalviewreturns(uint256amount){amount=_frozenTokens[user][tokenId];}functionsetFrozen(addressuser,uint256tokenId,uint256amount)publiconlyRole(ENFORCER_ROLE){require(user==ownerOf(tokenId),IERC721Errors.ERC721InvalidOwner(user));require(amount==0||amount==1,InvalidAmount(amount));_frozenTokens[user][tokenId]=uint8(amount);emitFrozen(user,tokenId,amount);}functionforceTransfer(addressfrom,addressto,uint256tokenId,uint256)publicvirtualoverrideonlyRole(ENFORCER_ROLE){require(to!=address(0),ERC721InvalidReceiver(address(0)));require(isUserAllowed(to),ERC7943NotAllowedUser(to));_excessFrozenUpdate(from,tokenId);super._update(to,tokenId,address(0));// Skip _update override
// 跳过 _update 覆盖
ERC721Utils.checkOnERC721Received(_msgSender(),from,to,tokenId,"");emitForcedTransfer(from,to,tokenId,1);}function_excessFrozenUpdate(addressfrom,uint256tokenId)internal{_validateCorrectOwner(from,tokenId);if(_frozenTokens[from][tokenId]>0){_frozenTokens[from][tokenId]=0;// Unfreeze the token if it was frozen
// 如果代币已冻结,则解冻代币
emitFrozen(from,tokenId,0);}}function_validateCorrectOwner(addressclaimant,uint256tokenId)internalview{addresscurrentOwner=ownerOf(tokenId);require(currentOwner==claimant,ERC721IncorrectOwner(claimant,tokenId,currentOwner));}/* standard mint function with access control ...*///具有访问控制的标准铸造功能...
functionburn(uint256tokenId)externalvirtualonlyRole(BURNER_ROLE){addresspreviousOwner=_update(address(0),tokenId,_msgSender());if(previousOwner==address(0))revertERC721NonexistentToken(tokenId);}function_update(addressto,uint256tokenId,addressauth)internalvirtualoverridereturns(address){addressfrom=_ownerOf(tokenId);if(auth!=address(0)){_checkAuthorized(from,auth,tokenId);}if(from!=address(0)&&to!=address(0)){// Transfer
// 转账
_validateCorrectOwner(from,tokenId);require(_frozenTokens[from][tokenId]==0,ERC7943InsufficientUnfrozenBalance(from,tokenId,1,0));require(isTransferAllowed(from,to,tokenId,1),ERC7943NotAllowedTransfer(from,to,tokenId,1));}elseif(from==address(0)){// Mint
// 铸造
require(isUserAllowed(to),ERC7943NotAllowedUser(to));}else{// Burn
// 销毁
_excessFrozenUpdate(from,tokenId);}returnsuper._update(to,tokenId,auth);}functionsupportsInterface(bytes4interfaceId)publicviewvirtualoverride(AccessControlEnumerable,ERC721,IERC165)returns(bool){returninterfaceId==type(IERC7943).interfaceId||super.supportsInterface(interfaceId);}}